2016-03-19 20:58:06 +01:00
#!/usr/bin/env python3
2019-02-20 20:03:13 -05:00
# Copyright (c) 2014-2019 The Bitcoin Core developers
2014-10-23 09:48:19 +08:00
# Distributed under the MIT software license, see the accompanying
2014-06-16 14:45:32 +02:00
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-01-17 18:34:40 -05:00
""" Test the wallet keypool and interaction with wallet encryption/locking. """
2014-06-16 14:45:32 +02:00
2018-07-07 00:10:35 +02:00
import time
2019-10-23 15:21:50 +02:00
from decimal import Decimal
2018-07-07 00:10:35 +02:00
2015-11-15 17:58:01 +01:00
from test_framework . test_framework import BitcoinTestFramework
2018-07-07 00:10:35 +02:00
from test_framework . util import assert_equal , assert_raises_rpc_error
2014-06-16 14:45:32 +02:00
2015-11-15 17:58:01 +01:00
class KeyPoolTest ( BitcoinTestFramework ) :
2017-06-09 18:21:21 -04:00
def set_test_params ( self ) :
self . num_nodes = 1
2015-11-15 17:58:01 +01:00
2018-09-09 13:32:37 -04:00
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2015-11-15 17:58:01 +01:00
def run_test ( self ) :
nodes = self . nodes
2016-07-21 21:19:02 +02:00
addr_before_encrypting = nodes [ 0 ] . getnewaddress ( )
2018-02-09 11:12:27 -05:00
addr_before_encrypting_data = nodes [ 0 ] . getaddressinfo ( addr_before_encrypting )
2016-07-21 21:19:02 +02:00
wallet_info_old = nodes [ 0 ] . getwalletinfo ( )
2019-07-16 15:33:35 -04:00
if not self . options . descriptors :
assert addr_before_encrypting_data [ ' hdseedid ' ] == wallet_info_old [ ' hdseedid ' ]
2018-04-16 11:13:07 +02:00
2015-11-15 18:48:18 +01:00
# Encrypt wallet and wait to terminate
2018-02-20 16:09:51 -05:00
nodes [ 0 ] . encryptwallet ( ' test ' )
2019-07-16 15:33:35 -04:00
if self . options . descriptors :
# Import hardened derivation only descriptors
nodes [ 0 ] . walletpassphrase ( ' test ' , 10 )
nodes [ 0 ] . importdescriptors ( [
{
" desc " : " wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/*h)#y4dfsj7n " ,
" timestamp " : " now " ,
" range " : [ 0 , 0 ] ,
" active " : True
} ,
{
" desc " : " pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1h/*h)#a0nyvl0k " ,
" timestamp " : " now " ,
" range " : [ 0 , 0 ] ,
" active " : True
} ,
{
" desc " : " sh(wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/2h/*h))#lmeu2axg " ,
" timestamp " : " now " ,
" range " : [ 0 , 0 ] ,
" active " : True
} ,
{
" desc " : " wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/3h/*h)#jkl636gm " ,
" timestamp " : " now " ,
" range " : [ 0 , 0 ] ,
" active " : True ,
" internal " : True
} ,
{
" desc " : " pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/4h/*h)#l3crwaus " ,
" timestamp " : " now " ,
" range " : [ 0 , 0 ] ,
" active " : True ,
" internal " : True
} ,
{
" desc " : " sh(wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/5h/*h))#qg8wa75f " ,
" timestamp " : " now " ,
" range " : [ 0 , 0 ] ,
" active " : True ,
" internal " : True
}
] )
nodes [ 0 ] . walletlock ( )
2015-11-15 18:48:18 +01:00
# Keep creating keys
2014-06-16 14:45:32 +02:00
addr = nodes [ 0 ] . getnewaddress ( )
2018-02-09 11:12:27 -05:00
addr_data = nodes [ 0 ] . getaddressinfo ( addr )
2016-07-21 21:19:02 +02:00
wallet_info = nodes [ 0 ] . getwalletinfo ( )
2019-07-16 15:33:35 -04:00
assert addr_before_encrypting_data [ ' hdmasterfingerprint ' ] != addr_data [ ' hdmasterfingerprint ' ]
if not self . options . descriptors :
assert addr_data [ ' hdseedid ' ] == wallet_info [ ' hdseedid ' ]
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 12 , " Error: Keypool ran out, please call keypoolrefill first " , nodes [ 0 ] . getnewaddress )
2015-11-15 18:48:18 +01:00
2017-01-17 08:55:30 +01:00
# put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min)
2015-11-15 18:48:18 +01:00
nodes [ 0 ] . walletpassphrase ( ' test ' , 12000 )
2017-01-10 16:45:30 +01:00
nodes [ 0 ] . keypoolrefill ( 6 )
2015-11-15 18:48:18 +01:00
nodes [ 0 ] . walletlock ( )
2017-01-10 16:45:30 +01:00
wi = nodes [ 0 ] . getwalletinfo ( )
2019-07-16 15:33:35 -04:00
if self . options . descriptors :
assert_equal ( wi [ ' keypoolsize_hd_internal ' ] , 18 )
assert_equal ( wi [ ' keypoolsize ' ] , 18 )
else :
assert_equal ( wi [ ' keypoolsize_hd_internal ' ] , 6 )
assert_equal ( wi [ ' keypoolsize ' ] , 6 )
2015-11-15 18:48:18 +01:00
2017-01-10 16:45:30 +01:00
# drain the internal keys
nodes [ 0 ] . getrawchangeaddress ( )
nodes [ 0 ] . getrawchangeaddress ( )
2017-01-17 08:55:30 +01:00
nodes [ 0 ] . getrawchangeaddress ( )
nodes [ 0 ] . getrawchangeaddress ( )
nodes [ 0 ] . getrawchangeaddress ( )
nodes [ 0 ] . getrawchangeaddress ( )
2015-11-15 18:48:18 +01:00
addr = set ( )
# the next one should fail
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 12 , " Keypool ran out " , nodes [ 0 ] . getrawchangeaddress )
2015-11-15 18:48:18 +01:00
2017-01-10 16:45:30 +01:00
# drain the external keys
2019-10-23 15:21:50 +02:00
addr . add ( nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) )
addr . add ( nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) )
addr . add ( nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) )
addr . add ( nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) )
addr . add ( nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) )
addr . add ( nodes [ 0 ] . getnewaddress ( address_type = " bech32 " ) )
2019-02-19 17:43:44 -05:00
assert len ( addr ) == 6
2017-01-10 16:45:30 +01:00
# the next one should fail
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 12 , " Error: Keypool ran out, please call keypoolrefill first " , nodes [ 0 ] . getnewaddress )
2017-01-10 16:45:30 +01:00
2015-11-15 18:48:18 +01:00
# refill keypool with three new addresses
2016-01-08 13:12:16 +01:00
nodes [ 0 ] . walletpassphrase ( ' test ' , 1 )
2015-11-15 18:48:18 +01:00
nodes [ 0 ] . keypoolrefill ( 3 )
2017-01-10 16:45:30 +01:00
2016-01-08 13:12:16 +01:00
# test walletpassphrase timeout
time . sleep ( 1.1 )
assert_equal ( nodes [ 0 ] . getwalletinfo ( ) [ " unlocked_until " ] , 0 )
2015-11-15 18:48:18 +01:00
2018-10-12 00:04:06 +09:00
# drain the keypool
for _ in range ( 3 ) :
nodes [ 0 ] . getnewaddress ( )
assert_raises_rpc_error ( - 12 , " Keypool ran out " , nodes [ 0 ] . getnewaddress )
2015-11-15 18:48:18 +01:00
2017-01-10 16:45:30 +01:00
nodes [ 0 ] . walletpassphrase ( ' test ' , 100 )
nodes [ 0 ] . keypoolrefill ( 100 )
wi = nodes [ 0 ] . getwalletinfo ( )
2019-07-16 15:33:35 -04:00
if self . options . descriptors :
assert_equal ( wi [ ' keypoolsize_hd_internal ' ] , 300 )
assert_equal ( wi [ ' keypoolsize ' ] , 300 )
else :
assert_equal ( wi [ ' keypoolsize_hd_internal ' ] , 100 )
assert_equal ( wi [ ' keypoolsize ' ] , 100 )
2017-01-10 16:45:30 +01:00
2019-10-23 15:21:50 +02:00
# create a blank wallet
2019-07-16 15:33:35 -04:00
nodes [ 0 ] . createwallet ( wallet_name = ' w2 ' , blank = True , disable_private_keys = True )
2019-10-23 15:21:50 +02:00
w2 = nodes [ 0 ] . get_wallet_rpc ( ' w2 ' )
# refer to initial wallet as w1
2020-09-28 20:24:06 -04:00
w1 = nodes [ 0 ] . get_wallet_rpc ( self . default_wallet_name )
2019-10-23 15:21:50 +02:00
# import private key and fund it
address = addr . pop ( )
2019-07-16 15:33:35 -04:00
desc = w1 . getaddressinfo ( address ) [ ' desc ' ]
if self . options . descriptors :
res = w2 . importdescriptors ( [ { ' desc ' : desc , ' timestamp ' : ' now ' } ] )
else :
res = w2 . importmulti ( [ { ' desc ' : desc , ' timestamp ' : ' now ' } ] )
2019-10-23 15:21:50 +02:00
assert_equal ( res [ 0 ] [ ' success ' ] , True )
w1 . walletpassphrase ( ' test ' , 100 )
Merge bbb1ba1814 into merged_master (Bitcoin PR #17219)
This modifies the CreateTransaction loop in a way not remotely worth the complexity,
and includes an absurdly fragile test where I had to add a bunch of trace statements
and tweak pretty-much every single hardcoded number. Not to name names, but it was
Sjors. (In fairness, the PR is a pure simplification of the CreateTransaction logic,
and it wasn't hard to merge even. It was just the test that caused my grief.)
Adapting the "use a dummy CTxDestination in the case that we cannot retrieve one from
the wallet" logic to our `mapScriptChange` map was not trivial. On my first attempt I
incorrectly assigned a positive vout index to the dummy script, which caused us to
call `ReturnDestination` later on the (unused) dummy destination. This is harmless now,
but when descriptor wallets are introduced in #16528, they introduce an edge case where
returning a null destination can incorrectly mark the 0th key of a BIP32 range as
unused. This triggered a test failure much later, in #19504, which uses descriptor
wallets in fundrawtransaction. The bug was that we'd import a descriptor, mark the
first key as being used, lock the wallet, call `fundrawtransaction` on a transaction
that did not require change (incorrectly marking the first key as unused but leaving
it in the descriptor ScriptPubKeyMan's cache), then call `fundrawtransaction` again
on a transaction that *did* require change. The wallet would then incorrectly retrieve
the "unused" key from cache and use it for change, rather than correctly failing and
advising the user that it could not produce change with a locked wallet and empty
keypool. This was not a fun bug to track down.
Another interesting observation is that branch-and-bound uses the CT size-overestimate
for change when trying to create changeless outputs, while our normal dust detection
uses Core's unchanged "an output is 133 bytes" logic. So when BnB is used we're willing
to delete a far bigger change output than we are when we don't use BnB.
Lest you think this works in Core, they're also inconsistent because BnB uses a
normal fee estimate for gauging change cost, while non-BnB uses the discardfee rate.
My advice is to hold your nose, pull stuff in from Core as it comes in, and thanks
to Andy's efforts things are getting better. Don't bother reviewing this too closely.
2020-12-03 00:58:01 +00:00
# ELEMENTS: the cost of change at a 10sat/b feerate is ~15000 sat,
# so we need to start with a bigger utxo to trigger change creation.
# all the below numbers are increased by 15000.
res = w1 . sendtoaddress ( address = address , amount = 0.00025000 )
2019-10-23 15:21:50 +02:00
nodes [ 0 ] . generate ( 1 )
destination = addr . pop ( )
# Using a fee rate (10 sat / byte) well above the minimum relay rate
# creating a 5,000 sat transaction with change should not be possible
assert_raises_rpc_error ( - 4 , " Transaction needs a change address, but we can ' t generate it. Please call keypoolrefill first. " , w2 . walletcreatefundedpsbt , inputs = [ ] , outputs = [ { addr . pop ( ) : 0.00005000 } ] , options = { " subtractFeeFromOutputs " : [ 0 ] , " feeRate " : 0.00010 } )
# creating a 10,000 sat transaction without change, with a manual input, should still be possible
Merge bbb1ba1814 into merged_master (Bitcoin PR #17219)
This modifies the CreateTransaction loop in a way not remotely worth the complexity,
and includes an absurdly fragile test where I had to add a bunch of trace statements
and tweak pretty-much every single hardcoded number. Not to name names, but it was
Sjors. (In fairness, the PR is a pure simplification of the CreateTransaction logic,
and it wasn't hard to merge even. It was just the test that caused my grief.)
Adapting the "use a dummy CTxDestination in the case that we cannot retrieve one from
the wallet" logic to our `mapScriptChange` map was not trivial. On my first attempt I
incorrectly assigned a positive vout index to the dummy script, which caused us to
call `ReturnDestination` later on the (unused) dummy destination. This is harmless now,
but when descriptor wallets are introduced in #16528, they introduce an edge case where
returning a null destination can incorrectly mark the 0th key of a BIP32 range as
unused. This triggered a test failure much later, in #19504, which uses descriptor
wallets in fundrawtransaction. The bug was that we'd import a descriptor, mark the
first key as being used, lock the wallet, call `fundrawtransaction` on a transaction
that did not require change (incorrectly marking the first key as unused but leaving
it in the descriptor ScriptPubKeyMan's cache), then call `fundrawtransaction` again
on a transaction that *did* require change. The wallet would then incorrectly retrieve
the "unused" key from cache and use it for change, rather than correctly failing and
advising the user that it could not produce change with a locked wallet and empty
keypool. This was not a fun bug to track down.
Another interesting observation is that branch-and-bound uses the CT size-overestimate
for change when trying to create changeless outputs, while our normal dust detection
uses Core's unchanged "an output is 133 bytes" logic. So when BnB is used we're willing
to delete a far bigger change output than we are when we don't use BnB.
Lest you think this works in Core, they're also inconsistent because BnB uses a
normal fee estimate for gauging change cost, while non-BnB uses the discardfee rate.
My advice is to hold your nose, pull stuff in from Core as it comes in, and thanks
to Andy's efforts things are getting better. Don't bother reviewing this too closely.
2020-12-03 00:58:01 +00:00
res = w2 . walletcreatefundedpsbt ( inputs = w2 . listunspent ( ) , outputs = [ { destination : 0.00025000 } ] , options = { " subtractFeeFromOutputs " : [ 0 ] , " feeRate " : 0.00010 } )
2019-10-23 15:21:50 +02:00
assert_equal ( " psbt " in res , True )
# creating a 10,000 sat transaction without change should still be possible
Merge bbb1ba1814 into merged_master (Bitcoin PR #17219)
This modifies the CreateTransaction loop in a way not remotely worth the complexity,
and includes an absurdly fragile test where I had to add a bunch of trace statements
and tweak pretty-much every single hardcoded number. Not to name names, but it was
Sjors. (In fairness, the PR is a pure simplification of the CreateTransaction logic,
and it wasn't hard to merge even. It was just the test that caused my grief.)
Adapting the "use a dummy CTxDestination in the case that we cannot retrieve one from
the wallet" logic to our `mapScriptChange` map was not trivial. On my first attempt I
incorrectly assigned a positive vout index to the dummy script, which caused us to
call `ReturnDestination` later on the (unused) dummy destination. This is harmless now,
but when descriptor wallets are introduced in #16528, they introduce an edge case where
returning a null destination can incorrectly mark the 0th key of a BIP32 range as
unused. This triggered a test failure much later, in #19504, which uses descriptor
wallets in fundrawtransaction. The bug was that we'd import a descriptor, mark the
first key as being used, lock the wallet, call `fundrawtransaction` on a transaction
that did not require change (incorrectly marking the first key as unused but leaving
it in the descriptor ScriptPubKeyMan's cache), then call `fundrawtransaction` again
on a transaction that *did* require change. The wallet would then incorrectly retrieve
the "unused" key from cache and use it for change, rather than correctly failing and
advising the user that it could not produce change with a locked wallet and empty
keypool. This was not a fun bug to track down.
Another interesting observation is that branch-and-bound uses the CT size-overestimate
for change when trying to create changeless outputs, while our normal dust detection
uses Core's unchanged "an output is 133 bytes" logic. So when BnB is used we're willing
to delete a far bigger change output than we are when we don't use BnB.
Lest you think this works in Core, they're also inconsistent because BnB uses a
normal fee estimate for gauging change cost, while non-BnB uses the discardfee rate.
My advice is to hold your nose, pull stuff in from Core as it comes in, and thanks
to Andy's efforts things are getting better. Don't bother reviewing this too closely.
2020-12-03 00:58:01 +00:00
res = w2 . walletcreatefundedpsbt ( inputs = [ ] , outputs = [ { destination : 0.00025000 } ] , options = { " subtractFeeFromOutputs " : [ 0 ] , " feeRate " : 0.00010 } )
2019-10-23 15:21:50 +02:00
assert_equal ( " psbt " in res , True )
# should work without subtractFeeFromOutputs if the exact fee is subtracted from the amount
Merge bbb1ba1814 into merged_master (Bitcoin PR #17219)
This modifies the CreateTransaction loop in a way not remotely worth the complexity,
and includes an absurdly fragile test where I had to add a bunch of trace statements
and tweak pretty-much every single hardcoded number. Not to name names, but it was
Sjors. (In fairness, the PR is a pure simplification of the CreateTransaction logic,
and it wasn't hard to merge even. It was just the test that caused my grief.)
Adapting the "use a dummy CTxDestination in the case that we cannot retrieve one from
the wallet" logic to our `mapScriptChange` map was not trivial. On my first attempt I
incorrectly assigned a positive vout index to the dummy script, which caused us to
call `ReturnDestination` later on the (unused) dummy destination. This is harmless now,
but when descriptor wallets are introduced in #16528, they introduce an edge case where
returning a null destination can incorrectly mark the 0th key of a BIP32 range as
unused. This triggered a test failure much later, in #19504, which uses descriptor
wallets in fundrawtransaction. The bug was that we'd import a descriptor, mark the
first key as being used, lock the wallet, call `fundrawtransaction` on a transaction
that did not require change (incorrectly marking the first key as unused but leaving
it in the descriptor ScriptPubKeyMan's cache), then call `fundrawtransaction` again
on a transaction that *did* require change. The wallet would then incorrectly retrieve
the "unused" key from cache and use it for change, rather than correctly failing and
advising the user that it could not produce change with a locked wallet and empty
keypool. This was not a fun bug to track down.
Another interesting observation is that branch-and-bound uses the CT size-overestimate
for change when trying to create changeless outputs, while our normal dust detection
uses Core's unchanged "an output is 133 bytes" logic. So when BnB is used we're willing
to delete a far bigger change output than we are when we don't use BnB.
Lest you think this works in Core, they're also inconsistent because BnB uses a
normal fee estimate for gauging change cost, while non-BnB uses the discardfee rate.
My advice is to hold your nose, pull stuff in from Core as it comes in, and thanks
to Andy's efforts things are getting better. Don't bother reviewing this too closely.
2020-12-03 00:58:01 +00:00
res = w2 . walletcreatefundedpsbt ( inputs = [ ] , outputs = [ { destination : 0.00021650 } ] , options = { " feeRate " : 0.00010 } )
2019-10-23 15:21:50 +02:00
assert_equal ( " psbt " in res , True )
# dust change should be removed
Merge bbb1ba1814 into merged_master (Bitcoin PR #17219)
This modifies the CreateTransaction loop in a way not remotely worth the complexity,
and includes an absurdly fragile test where I had to add a bunch of trace statements
and tweak pretty-much every single hardcoded number. Not to name names, but it was
Sjors. (In fairness, the PR is a pure simplification of the CreateTransaction logic,
and it wasn't hard to merge even. It was just the test that caused my grief.)
Adapting the "use a dummy CTxDestination in the case that we cannot retrieve one from
the wallet" logic to our `mapScriptChange` map was not trivial. On my first attempt I
incorrectly assigned a positive vout index to the dummy script, which caused us to
call `ReturnDestination` later on the (unused) dummy destination. This is harmless now,
but when descriptor wallets are introduced in #16528, they introduce an edge case where
returning a null destination can incorrectly mark the 0th key of a BIP32 range as
unused. This triggered a test failure much later, in #19504, which uses descriptor
wallets in fundrawtransaction. The bug was that we'd import a descriptor, mark the
first key as being used, lock the wallet, call `fundrawtransaction` on a transaction
that did not require change (incorrectly marking the first key as unused but leaving
it in the descriptor ScriptPubKeyMan's cache), then call `fundrawtransaction` again
on a transaction that *did* require change. The wallet would then incorrectly retrieve
the "unused" key from cache and use it for change, rather than correctly failing and
advising the user that it could not produce change with a locked wallet and empty
keypool. This was not a fun bug to track down.
Another interesting observation is that branch-and-bound uses the CT size-overestimate
for change when trying to create changeless outputs, while our normal dust detection
uses Core's unchanged "an output is 133 bytes" logic. So when BnB is used we're willing
to delete a far bigger change output than we are when we don't use BnB.
Lest you think this works in Core, they're also inconsistent because BnB uses a
normal fee estimate for gauging change cost, while non-BnB uses the discardfee rate.
My advice is to hold your nose, pull stuff in from Core as it comes in, and thanks
to Andy's efforts things are getting better. Don't bother reviewing this too closely.
2020-12-03 00:58:01 +00:00
res = w2 . walletcreatefundedpsbt ( inputs = [ ] , outputs = [ { destination : 0.00021000 } ] , options = { " feeRate " : 0.00010 } )
2019-10-23 15:21:50 +02:00
assert_equal ( " psbt " in res , True )
# create a transaction without change at the maximum fee rate, such that the output is still spendable:
Merge bbb1ba1814 into merged_master (Bitcoin PR #17219)
This modifies the CreateTransaction loop in a way not remotely worth the complexity,
and includes an absurdly fragile test where I had to add a bunch of trace statements
and tweak pretty-much every single hardcoded number. Not to name names, but it was
Sjors. (In fairness, the PR is a pure simplification of the CreateTransaction logic,
and it wasn't hard to merge even. It was just the test that caused my grief.)
Adapting the "use a dummy CTxDestination in the case that we cannot retrieve one from
the wallet" logic to our `mapScriptChange` map was not trivial. On my first attempt I
incorrectly assigned a positive vout index to the dummy script, which caused us to
call `ReturnDestination` later on the (unused) dummy destination. This is harmless now,
but when descriptor wallets are introduced in #16528, they introduce an edge case where
returning a null destination can incorrectly mark the 0th key of a BIP32 range as
unused. This triggered a test failure much later, in #19504, which uses descriptor
wallets in fundrawtransaction. The bug was that we'd import a descriptor, mark the
first key as being used, lock the wallet, call `fundrawtransaction` on a transaction
that did not require change (incorrectly marking the first key as unused but leaving
it in the descriptor ScriptPubKeyMan's cache), then call `fundrawtransaction` again
on a transaction that *did* require change. The wallet would then incorrectly retrieve
the "unused" key from cache and use it for change, rather than correctly failing and
advising the user that it could not produce change with a locked wallet and empty
keypool. This was not a fun bug to track down.
Another interesting observation is that branch-and-bound uses the CT size-overestimate
for change when trying to create changeless outputs, while our normal dust detection
uses Core's unchanged "an output is 133 bytes" logic. So when BnB is used we're willing
to delete a far bigger change output than we are when we don't use BnB.
Lest you think this works in Core, they're also inconsistent because BnB uses a
normal fee estimate for gauging change cost, while non-BnB uses the discardfee rate.
My advice is to hold your nose, pull stuff in from Core as it comes in, and thanks
to Andy's efforts things are getting better. Don't bother reviewing this too closely.
2020-12-03 00:58:01 +00:00
res = w2 . walletcreatefundedpsbt ( inputs = [ ] , outputs = [ { destination : 0.00025000 } ] , options = { " subtractFeeFromOutputs " : [ 0 ] , " feeRate " : 0.0008824 } )
2019-10-23 15:21:50 +02:00
assert_equal ( " psbt " in res , True )
Merge bbb1ba1814 into merged_master (Bitcoin PR #17219)
This modifies the CreateTransaction loop in a way not remotely worth the complexity,
and includes an absurdly fragile test where I had to add a bunch of trace statements
and tweak pretty-much every single hardcoded number. Not to name names, but it was
Sjors. (In fairness, the PR is a pure simplification of the CreateTransaction logic,
and it wasn't hard to merge even. It was just the test that caused my grief.)
Adapting the "use a dummy CTxDestination in the case that we cannot retrieve one from
the wallet" logic to our `mapScriptChange` map was not trivial. On my first attempt I
incorrectly assigned a positive vout index to the dummy script, which caused us to
call `ReturnDestination` later on the (unused) dummy destination. This is harmless now,
but when descriptor wallets are introduced in #16528, they introduce an edge case where
returning a null destination can incorrectly mark the 0th key of a BIP32 range as
unused. This triggered a test failure much later, in #19504, which uses descriptor
wallets in fundrawtransaction. The bug was that we'd import a descriptor, mark the
first key as being used, lock the wallet, call `fundrawtransaction` on a transaction
that did not require change (incorrectly marking the first key as unused but leaving
it in the descriptor ScriptPubKeyMan's cache), then call `fundrawtransaction` again
on a transaction that *did* require change. The wallet would then incorrectly retrieve
the "unused" key from cache and use it for change, rather than correctly failing and
advising the user that it could not produce change with a locked wallet and empty
keypool. This was not a fun bug to track down.
Another interesting observation is that branch-and-bound uses the CT size-overestimate
for change when trying to create changeless outputs, while our normal dust detection
uses Core's unchanged "an output is 133 bytes" logic. So when BnB is used we're willing
to delete a far bigger change output than we are when we don't use BnB.
Lest you think this works in Core, they're also inconsistent because BnB uses a
normal fee estimate for gauging change cost, while non-BnB uses the discardfee rate.
My advice is to hold your nose, pull stuff in from Core as it comes in, and thanks
to Andy's efforts things are getting better. Don't bother reviewing this too closely.
2020-12-03 00:58:01 +00:00
assert_equal ( res [ " fee " ] , Decimal ( " 0.00016853 " ) )
2019-10-23 15:21:50 +02:00
# creating a 10,000 sat transaction with a manual change address should be possible
Merge bbb1ba1814 into merged_master (Bitcoin PR #17219)
This modifies the CreateTransaction loop in a way not remotely worth the complexity,
and includes an absurdly fragile test where I had to add a bunch of trace statements
and tweak pretty-much every single hardcoded number. Not to name names, but it was
Sjors. (In fairness, the PR is a pure simplification of the CreateTransaction logic,
and it wasn't hard to merge even. It was just the test that caused my grief.)
Adapting the "use a dummy CTxDestination in the case that we cannot retrieve one from
the wallet" logic to our `mapScriptChange` map was not trivial. On my first attempt I
incorrectly assigned a positive vout index to the dummy script, which caused us to
call `ReturnDestination` later on the (unused) dummy destination. This is harmless now,
but when descriptor wallets are introduced in #16528, they introduce an edge case where
returning a null destination can incorrectly mark the 0th key of a BIP32 range as
unused. This triggered a test failure much later, in #19504, which uses descriptor
wallets in fundrawtransaction. The bug was that we'd import a descriptor, mark the
first key as being used, lock the wallet, call `fundrawtransaction` on a transaction
that did not require change (incorrectly marking the first key as unused but leaving
it in the descriptor ScriptPubKeyMan's cache), then call `fundrawtransaction` again
on a transaction that *did* require change. The wallet would then incorrectly retrieve
the "unused" key from cache and use it for change, rather than correctly failing and
advising the user that it could not produce change with a locked wallet and empty
keypool. This was not a fun bug to track down.
Another interesting observation is that branch-and-bound uses the CT size-overestimate
for change when trying to create changeless outputs, while our normal dust detection
uses Core's unchanged "an output is 133 bytes" logic. So when BnB is used we're willing
to delete a far bigger change output than we are when we don't use BnB.
Lest you think this works in Core, they're also inconsistent because BnB uses a
normal fee estimate for gauging change cost, while non-BnB uses the discardfee rate.
My advice is to hold your nose, pull stuff in from Core as it comes in, and thanks
to Andy's efforts things are getting better. Don't bother reviewing this too closely.
2020-12-03 00:58:01 +00:00
res = w2 . walletcreatefundedpsbt ( inputs = [ ] , outputs = [ { destination : 0.00025000 } ] , options = { " subtractFeeFromOutputs " : [ 0 ] , " feeRate " : 0.00010 , " changeAddress " : addr . pop ( ) } )
2019-10-23 15:21:50 +02:00
assert_equal ( " psbt " in res , True )
2014-06-16 14:45:32 +02:00
if __name__ == ' __main__ ' :
2015-11-15 17:58:01 +01:00
KeyPoolTest ( ) . main ( )