mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-28 16:36:04 +01:00
Merge #9294: Use internal HD chain for change outputs (hd split)
4115af7Fix rebase issue where pwalletMain was used instead of pwallet Ser./Deser. nInternalChainCounter as last element (Jonas Schnelli)9382f04Do not break backward compatibility during wallet encryption (Jonas Schnelli)1df08d1Add assertion for CanSupportFeature(FEATURE_HD_SPLIT) (Jonas Schnelli)cd468d0Define CWallet::DeriveNewChildKey() as private (Jonas Schnelli)ed79e4fOptimize GetOldestKeyPoolTime(), return as soon as we have both oldest keys (Jonas Schnelli)771a304Make sure we set the wallets min version to FEATURE_HD_SPLIT at the very first point (Jonas Schnelli)1b3b5c6Slightly modify fundrawtransaction.py test (change getnewaddress() into getrawchangeaddress()) (Jonas Schnelli)003e197Remove FEATURE_HD_SPLIT bump TODO (Jonas Schnelli)d9638e5Overhaul the internal/external key derive switch (Jonas Schnelli)1090502Fix superfluous cast and code style nits in RPC wallet-hd.py test (Jonas Schnelli)58e1483CKeyPool avoid "catch (...)" in SerializationOp (Jonas Schnelli)e138876Only show keypoolsize_hd_internal if HD split is enabled (Jonas Schnelli)add38d9GetOldestKeyPoolTime: if HD & HD Chain Split is enabled, response max(oldest-internal-key, oldest-external-key) (Jonas Schnelli)dd526c2Don't switch to HD-chain-split during wallet encryption of non HD-chain-split wallets (Jonas Schnelli)79df9dfSwitch to 100% for the HD internal keypool size (Jonas Schnelli)bcafca1Make sure we always generate one keypool key at minimum (Jonas Schnelli)d0a627aFix issue where CDataStream->nVersion was taken a CKeyPool record version (Jonas Schnelli)9af8f00Make sure we hand out keypool keys if HD_SPLIT is not enabled (Jonas Schnelli)469a47bMake sure ReserveKeyFromKeyPool only hands out internal keys if HD_SPLIT is supported (Jonas Schnelli)05a9b49Fix wrong keypool internal size in RPC getwalletinfo help (Jonas Schnelli)01de822Removed redundant IsLocked() check in NewKeyPool() (Jonas Schnelli)d59531dImmediately return setKeyPool's size if HD or HD_SPLIT is disabled or not supported (Jonas Schnelli)02592f4[Wallet] split the keypool in an internal and external part (Jonas Schnelli) Tree-SHA512: 80d355d5e844b48c3163b56c788ab8b5b5285db0ceeb19858a3ef517d5a702afeca21dbae526d7b8fb4101c2a745af1d92bf557c40cf516780f17992bf678c1a
This commit is contained in:
@@ -467,6 +467,7 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
|
||||
# drain the keypool
|
||||
self.nodes[1].getnewaddress()
|
||||
self.nodes[1].getrawchangeaddress()
|
||||
inputs = []
|
||||
outputs = {self.nodes[0].getnewaddress():1.1}
|
||||
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
|
||||
@@ -476,6 +477,7 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
|
||||
#refill the keypool
|
||||
self.nodes[1].walletpassphrase("test", 100)
|
||||
self.nodes[1].keypoolrefill(8) #need to refill the keypool to get an internal change address
|
||||
self.nodes[1].walletlock()
|
||||
|
||||
assert_raises_jsonrpc(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2)
|
||||
@@ -644,7 +646,7 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
if out['value'] > 1.0:
|
||||
changeaddress += out['scriptPubKey']['addresses'][0]
|
||||
assert(changeaddress != "")
|
||||
nextaddr = self.nodes[3].getnewaddress()
|
||||
nextaddr = self.nodes[3].getrawchangeaddress()
|
||||
# frt should not have removed the key from the keypool
|
||||
assert(changeaddress == nextaddr)
|
||||
|
||||
|
||||
@@ -27,28 +27,42 @@ class KeyPoolTest(BitcoinTestFramework):
|
||||
wallet_info = nodes[0].getwalletinfo()
|
||||
assert(addr_before_encrypting_data['hdmasterkeyid'] != wallet_info['hdmasterkeyid'])
|
||||
assert(addr_data['hdmasterkeyid'] == wallet_info['hdmasterkeyid'])
|
||||
|
||||
assert_raises_jsonrpc(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
|
||||
|
||||
# put three new keys in the keypool
|
||||
# put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min)
|
||||
nodes[0].walletpassphrase('test', 12000)
|
||||
nodes[0].keypoolrefill(3)
|
||||
nodes[0].keypoolrefill(6)
|
||||
nodes[0].walletlock()
|
||||
wi = nodes[0].getwalletinfo()
|
||||
assert_equal(wi['keypoolsize_hd_internal'], 6)
|
||||
assert_equal(wi['keypoolsize'], 6)
|
||||
|
||||
# drain the keys
|
||||
# drain the internal keys
|
||||
nodes[0].getrawchangeaddress()
|
||||
nodes[0].getrawchangeaddress()
|
||||
nodes[0].getrawchangeaddress()
|
||||
nodes[0].getrawchangeaddress()
|
||||
nodes[0].getrawchangeaddress()
|
||||
nodes[0].getrawchangeaddress()
|
||||
addr = set()
|
||||
addr.add(nodes[0].getrawchangeaddress())
|
||||
addr.add(nodes[0].getrawchangeaddress())
|
||||
addr.add(nodes[0].getrawchangeaddress())
|
||||
addr.add(nodes[0].getrawchangeaddress())
|
||||
# assert that four unique addresses were returned
|
||||
assert(len(addr) == 4)
|
||||
# the next one should fail
|
||||
assert_raises_jsonrpc(-12, "Keypool ran out", nodes[0].getrawchangeaddress)
|
||||
|
||||
# drain the external keys
|
||||
addr.add(nodes[0].getnewaddress())
|
||||
addr.add(nodes[0].getnewaddress())
|
||||
addr.add(nodes[0].getnewaddress())
|
||||
addr.add(nodes[0].getnewaddress())
|
||||
addr.add(nodes[0].getnewaddress())
|
||||
addr.add(nodes[0].getnewaddress())
|
||||
assert(len(addr) == 6)
|
||||
# the next one should fail
|
||||
assert_raises_jsonrpc(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
|
||||
|
||||
# refill keypool with three new addresses
|
||||
nodes[0].walletpassphrase('test', 1)
|
||||
nodes[0].keypoolrefill(3)
|
||||
|
||||
# test walletpassphrase timeout
|
||||
time.sleep(1.1)
|
||||
assert_equal(nodes[0].getwalletinfo()["unlocked_until"], 0)
|
||||
@@ -57,9 +71,14 @@ class KeyPoolTest(BitcoinTestFramework):
|
||||
nodes[0].generate(1)
|
||||
nodes[0].generate(1)
|
||||
nodes[0].generate(1)
|
||||
nodes[0].generate(1)
|
||||
assert_raises_jsonrpc(-12, "Keypool ran out", nodes[0].generate, 1)
|
||||
|
||||
nodes[0].walletpassphrase('test', 100)
|
||||
nodes[0].keypoolrefill(100)
|
||||
wi = nodes[0].getwalletinfo()
|
||||
assert_equal(wi['keypoolsize_hd_internal'], 100)
|
||||
assert_equal(wi['keypoolsize'], 100)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = False
|
||||
|
||||
@@ -88,7 +88,7 @@ class WalletDumpTest(BitcoinTestFramework):
|
||||
read_dump(tmpdir + "/node0/wallet.unencrypted.dump", addrs, None)
|
||||
assert_equal(found_addr, test_addr_count) # all keys must be in the dump
|
||||
assert_equal(found_addr_chg, 50) # 50 blocks where mined
|
||||
assert_equal(found_addr_rsv, 90 + 1) # keypool size (TODO: fix off-by-one)
|
||||
assert_equal(found_addr_rsv, 90*2) # 90 keys plus 100% internal keys
|
||||
|
||||
#encrypt wallet, restart, unlock and dump
|
||||
self.nodes[0].encryptwallet('test')
|
||||
@@ -102,8 +102,8 @@ class WalletDumpTest(BitcoinTestFramework):
|
||||
found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_enc = \
|
||||
read_dump(tmpdir + "/node0/wallet.encrypted.dump", addrs, hd_master_addr_unenc)
|
||||
assert_equal(found_addr, test_addr_count)
|
||||
assert_equal(found_addr_chg, 90 + 1 + 50) # old reserve keys are marked as change now
|
||||
assert_equal(found_addr_rsv, 90 + 1) # keypool size (TODO: fix off-by-one)
|
||||
assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now
|
||||
assert_equal(found_addr_rsv, 90*2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletDumpTest().main ()
|
||||
|
||||
@@ -42,6 +42,11 @@ class WalletHDTest(BitcoinTestFramework):
|
||||
masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
|
||||
assert_equal(len(masterkeyid), 40)
|
||||
|
||||
# create an internal key
|
||||
change_addr = self.nodes[1].getrawchangeaddress()
|
||||
change_addrV= self.nodes[1].validateaddress(change_addr);
|
||||
assert_equal(change_addrV["hdkeypath"], "m/0'/1'/0'") #first internal child key
|
||||
|
||||
# Import a non-HD private key in the HD wallet
|
||||
non_hd_add = self.nodes[0].getnewaddress()
|
||||
self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add))
|
||||
@@ -65,6 +70,11 @@ class WalletHDTest(BitcoinTestFramework):
|
||||
self.nodes[0].sendtoaddress(non_hd_add, 1)
|
||||
self.nodes[0].generate(1)
|
||||
|
||||
# create an internal key (again)
|
||||
change_addr = self.nodes[1].getrawchangeaddress()
|
||||
change_addrV= self.nodes[1].validateaddress(change_addr);
|
||||
assert_equal(change_addrV["hdkeypath"], "m/0'/1'/1'") #second internal child key
|
||||
|
||||
self.sync_all()
|
||||
assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1)
|
||||
|
||||
@@ -90,6 +100,15 @@ class WalletHDTest(BitcoinTestFramework):
|
||||
#connect_nodes_bi(self.nodes, 0, 1)
|
||||
assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1)
|
||||
|
||||
# send a tx and make sure its using the internal chain for the changeoutput
|
||||
txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
|
||||
outs = self.nodes[1].decoderawtransaction(self.nodes[1].gettransaction(txid)['hex'])['vout'];
|
||||
keypath = ""
|
||||
for out in outs:
|
||||
if out['value'] != 1:
|
||||
keypath = self.nodes[1].validateaddress(out['scriptPubKey']['addresses'][0])['hdkeypath']
|
||||
|
||||
assert_equal(keypath[0:7], "m/0'/1'")
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletHDTest().main ()
|
||||
|
||||
Reference in New Issue
Block a user