mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-07-02 11:41:18 +02:00
Overhaul importmulti logic
This introduces various changes to the importmulti logic: * Instead of processing input and importing things at the same time, first process all input data and verify it, so no changes are made in case of an error. * Verify that no superfluous information is provided (no keys or scripts that don't contribute to solvability in particular). * Add way more sanity checks, by means of descending into all involved scripts.
This commit is contained in:
@ -119,9 +119,13 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
CScript([OP_HASH160, witness_script, OP_EQUAL]).hex(), # p2sh-p2wsh
|
||||
script_to_p2sh_p2wsh(script_code)) # p2sh-p2wsh addr
|
||||
|
||||
def test_importmulti(self, req, success, error_code=None, error_message=None):
|
||||
def test_importmulti(self, req, success, error_code=None, error_message=None, warnings=[]):
|
||||
"""Run importmulti and assert success"""
|
||||
result = self.nodes[1].importmulti([req])
|
||||
observed_warnings = []
|
||||
if 'warnings' in result[0]:
|
||||
observed_warnings = result[0]['warnings']
|
||||
assert_equal("\n".join(sorted(warnings)), "\n".join(sorted(observed_warnings)))
|
||||
assert_equal(result[0]['success'], success)
|
||||
if error_code is not None:
|
||||
assert_equal(result[0]['error']['code'], error_code)
|
||||
@ -178,7 +182,7 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
"timestamp": "now"},
|
||||
False,
|
||||
error_code=-5,
|
||||
error_message='Invalid address')
|
||||
error_message='Invalid address \"not valid address\"')
|
||||
|
||||
# ScriptPubKey + internal
|
||||
self.log.info("Should import a scriptPubKey with internal flag")
|
||||
@ -227,7 +231,8 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
"timestamp": "now",
|
||||
"pubkeys": [key.pubkey],
|
||||
"internal": False},
|
||||
True)
|
||||
True,
|
||||
warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
||||
self.test_address(address,
|
||||
iswatchonly=True,
|
||||
ismine=False,
|
||||
@ -241,7 +246,8 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
"timestamp": "now",
|
||||
"pubkeys": [key.pubkey],
|
||||
"internal": True},
|
||||
True)
|
||||
True,
|
||||
warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
||||
self.test_address(address,
|
||||
iswatchonly=True,
|
||||
ismine=False,
|
||||
@ -284,20 +290,19 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
error_message='The wallet already contains the private key for this address or script')
|
||||
|
||||
# Address + Private key + watchonly
|
||||
self.log.info("Should not import an address with private key and with watchonly")
|
||||
self.log.info("Should import an address with private key and with watchonly")
|
||||
key = self.get_key()
|
||||
address = key.p2pkh_addr
|
||||
self.test_importmulti({"scriptPubKey": {"address": address},
|
||||
"timestamp": "now",
|
||||
"keys": [key.privkey],
|
||||
"watchonly": True},
|
||||
False,
|
||||
error_code=-8,
|
||||
error_message='Watch-only addresses should not include private keys')
|
||||
True,
|
||||
warnings=["All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag."])
|
||||
self.test_address(address,
|
||||
iswatchonly=False,
|
||||
ismine=False,
|
||||
timestamp=None)
|
||||
ismine=True,
|
||||
timestamp=timestamp)
|
||||
|
||||
# ScriptPubKey + Private key + internal
|
||||
self.log.info("Should import a scriptPubKey with internal and with private key")
|
||||
@ -358,8 +363,9 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_addr},
|
||||
"timestamp": "now",
|
||||
"redeemscript": multisig.redeem_script},
|
||||
True)
|
||||
self.test_address(multisig.p2sh_addr, timestamp=timestamp)
|
||||
True,
|
||||
warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
||||
self.test_address(multisig.p2sh_addr, timestamp=timestamp, iswatchonly=True, ismine=False, solvable=True)
|
||||
|
||||
p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0]
|
||||
assert_equal(p2shunspent['spendable'], False)
|
||||
@ -377,9 +383,13 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
"timestamp": "now",
|
||||
"redeemscript": multisig.redeem_script,
|
||||
"keys": multisig.privkeys[0:2]},
|
||||
True)
|
||||
True,
|
||||
warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
||||
self.test_address(multisig.p2sh_addr,
|
||||
timestamp=timestamp)
|
||||
timestamp=timestamp,
|
||||
ismine=False,
|
||||
iswatchonly=True,
|
||||
solvable=True)
|
||||
|
||||
p2shunspent = self.nodes[1].listunspent(0, 999999, [multisig.p2sh_addr])[0]
|
||||
assert_equal(p2shunspent['spendable'], False)
|
||||
@ -398,28 +408,31 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
"redeemscript": multisig.redeem_script,
|
||||
"keys": multisig.privkeys[0:2],
|
||||
"watchonly": True},
|
||||
False,
|
||||
error_code=-8,
|
||||
error_message='Watch-only addresses should not include private keys')
|
||||
True)
|
||||
self.test_address(multisig.p2sh_addr,
|
||||
iswatchonly=True,
|
||||
ismine=False,
|
||||
solvable=True,
|
||||
timestamp=timestamp)
|
||||
|
||||
# Address + Public key + !Internal + Wrong pubkey
|
||||
self.log.info("Should not import an address with a wrong public key")
|
||||
self.log.info("Should not import an address with the wrong public key as non-solvable")
|
||||
key = self.get_key()
|
||||
address = key.p2pkh_addr
|
||||
wrong_key = self.get_key().pubkey
|
||||
self.test_importmulti({"scriptPubKey": {"address": address},
|
||||
"timestamp": "now",
|
||||
"pubkeys": [wrong_key]},
|
||||
False,
|
||||
error_code=-5,
|
||||
error_message='Key does not match address destination')
|
||||
True,
|
||||
warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
||||
self.test_address(address,
|
||||
iswatchonly=False,
|
||||
iswatchonly=True,
|
||||
ismine=False,
|
||||
timestamp=None)
|
||||
solvable=False,
|
||||
timestamp=timestamp)
|
||||
|
||||
# ScriptPubKey + Public key + internal + Wrong pubkey
|
||||
self.log.info("Should not import a scriptPubKey with internal and with a wrong public key")
|
||||
self.log.info("Should import a scriptPubKey with internal and with a wrong public key as non-solvable")
|
||||
key = self.get_key()
|
||||
address = key.p2pkh_addr
|
||||
wrong_key = self.get_key().pubkey
|
||||
@ -427,32 +440,32 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
"timestamp": "now",
|
||||
"pubkeys": [wrong_key],
|
||||
"internal": True},
|
||||
False,
|
||||
error_code=-5,
|
||||
error_message='Key does not match address destination')
|
||||
True,
|
||||
warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
||||
self.test_address(address,
|
||||
iswatchonly=False,
|
||||
iswatchonly=True,
|
||||
ismine=False,
|
||||
timestamp=None)
|
||||
solvable=False,
|
||||
timestamp=timestamp)
|
||||
|
||||
# Address + Private key + !watchonly + Wrong private key
|
||||
self.log.info("Should not import an address with a wrong private key")
|
||||
self.log.info("Should import an address with a wrong private key as non-solvable")
|
||||
key = self.get_key()
|
||||
address = key.p2pkh_addr
|
||||
wrong_privkey = self.get_key().privkey
|
||||
self.test_importmulti({"scriptPubKey": {"address": address},
|
||||
"timestamp": "now",
|
||||
"keys": [wrong_privkey]},
|
||||
False,
|
||||
error_code=-5,
|
||||
error_message='Key does not match address destination')
|
||||
True,
|
||||
warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
||||
self.test_address(address,
|
||||
iswatchonly=False,
|
||||
iswatchonly=True,
|
||||
ismine=False,
|
||||
timestamp=None)
|
||||
solvable=False,
|
||||
timestamp=timestamp)
|
||||
|
||||
# ScriptPubKey + Private key + internal + Wrong private key
|
||||
self.log.info("Should not import a scriptPubKey with internal and with a wrong private key")
|
||||
self.log.info("Should import a scriptPubKey with internal and with a wrong private key as non-solvable")
|
||||
key = self.get_key()
|
||||
address = key.p2pkh_addr
|
||||
wrong_privkey = self.get_key().privkey
|
||||
@ -460,13 +473,13 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
"timestamp": "now",
|
||||
"keys": [wrong_privkey],
|
||||
"internal": True},
|
||||
False,
|
||||
error_code=-5,
|
||||
error_message='Key does not match address destination')
|
||||
True,
|
||||
warnings=["Importing as non-solvable: some required keys are missing. If this is intentional, don't provide any keys, pubkeys, witnessscript, or redeemscript.", "Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
||||
self.test_address(address,
|
||||
iswatchonly=False,
|
||||
iswatchonly=True,
|
||||
ismine=False,
|
||||
timestamp=None)
|
||||
solvable=False,
|
||||
timestamp=timestamp)
|
||||
|
||||
# Importing existing watch only address with new timestamp should replace saved timestamp.
|
||||
assert_greater_than(timestamp, watchonly_timestamp)
|
||||
@ -516,7 +529,8 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
self.test_importmulti({"scriptPubKey": {"address": address},
|
||||
"timestamp": "now",
|
||||
"pubkeys": [key.pubkey]},
|
||||
True)
|
||||
True,
|
||||
warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
||||
self.test_address(address,
|
||||
ismine=False,
|
||||
solvable=True)
|
||||
@ -571,7 +585,8 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
"timestamp": "now",
|
||||
"redeemscript": key.p2sh_p2wpkh_redeem_script,
|
||||
"pubkeys": [key.pubkey]},
|
||||
True)
|
||||
True,
|
||||
warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
||||
self.test_address(address,
|
||||
solvable=True,
|
||||
ismine=False)
|
||||
@ -591,14 +606,17 @@ class ImportMultiTest(BitcoinTestFramework):
|
||||
|
||||
# P2SH-P2WSH multisig + redeemscript with no private key
|
||||
multisig = self.get_multisig()
|
||||
address = multisig.p2sh_p2wsh_addr
|
||||
self.log.info("Should import a p2sh-p2wsh with respective redeem script but no private key")
|
||||
self.test_importmulti({"scriptPubKey": {"address": multisig.p2sh_p2wsh_addr},
|
||||
self.test_importmulti({"scriptPubKey": {"address": address},
|
||||
"timestamp": "now",
|
||||
"redeemscript": multisig.p2wsh_script,
|
||||
"witnessscript": multisig.redeem_script},
|
||||
True)
|
||||
True,
|
||||
warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."])
|
||||
self.test_address(address,
|
||||
solvable=True)
|
||||
solvable=True,
|
||||
ismine=False)
|
||||
|
||||
if __name__ == '__main__':
|
||||
ImportMultiTest().main()
|
||||
|
Reference in New Issue
Block a user