Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc-wallet-standardness.py
Show First 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
address_nonstd = nonstd_node.getnewaddress() | address_nonstd = nonstd_node.getnewaddress() | ||||
# make and mature some coins for the nonstandard node | # make and mature some coins for the nonstandard node | ||||
nonstd_node.generate(120) | nonstd_node.generate(120) | ||||
sync_blocks(self.nodes) | sync_blocks(self.nodes) | ||||
def fund_and_test_wallet(scriptPubKey, shouldBeStandard, shouldBeInWallet, | def fund_and_test_wallet(scriptPubKey, shouldBeStandard, shouldBeInWallet, | ||||
amount=10000, spendfee=500, nonstd_error="scriptpubkey (code 64)", canSign=None): | amount=10000, spendfee=500, nonstd_error="scriptpubkey (code 64)", sign_error=None): | ||||
""" Get the nonstandard node to fund a transaction, test its | """ Get the nonstandard node to fund a transaction, test its | ||||
standardness by trying to broadcast on the standard node, then | standardness by trying to broadcast on the standard node, then | ||||
mine it and see if it ended up in the standard node's wallet. | mine it and see if it ended up in the standard node's wallet. | ||||
Finally, it attempts to spend the coin. | Finally, it attempts to spend the coin. | ||||
""" | """ | ||||
if canSign is None: | |||||
canSign = shouldBeInWallet | |||||
self.log.info("Trying script {}".format(scriptPubKey.hex(),)) | self.log.info("Trying script {}".format(scriptPubKey.hex(),)) | ||||
# get nonstandard node to fund the script | # get nonstandard node to fund the script | ||||
tx = CTransaction() | tx = CTransaction() | ||||
tx.vout.append(CTxOut(max(amount, 10000), scriptPubKey)) | tx.vout.append(CTxOut(max(amount, 10000), scriptPubKey)) | ||||
rawtx = nonstd_node.fundrawtransaction( | rawtx = nonstd_node.fundrawtransaction( | ||||
ToHex(tx), {'lockUnspents': True, 'changePosition': 1})['hex'] | ToHex(tx), {'lockUnspents': True, 'changePosition': 1})['hex'] | ||||
# fundrawtransaction doesn't like to fund dust outputs, so we | # fundrawtransaction doesn't like to fund dust outputs, so we | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
# to miner fees. | # to miner fees. | ||||
outputs = [{"data": b"to miner, with love".hex()}] | outputs = [{"data": b"to miner, with love".hex()}] | ||||
else: | else: | ||||
outputs = [{address_nonstd: outamount}] | outputs = [{address_nonstd: outamount}] | ||||
spendtx = std_node.createrawtransaction( | spendtx = std_node.createrawtransaction( | ||||
[{'txid': txid, 'vout': 0}], outputs) | [{'txid': txid, 'vout': 0}], outputs) | ||||
signresult = std_node.signrawtransactionwithwallet(spendtx) | signresult = std_node.signrawtransactionwithwallet(spendtx) | ||||
if canSign: | if sign_error is None: | ||||
assert_equal(signresult['complete'], True) | assert_equal(signresult['complete'], True) | ||||
txid = std_node.sendrawtransaction(signresult['hex']) | txid = std_node.sendrawtransaction(signresult['hex']) | ||||
[blockhash] = std_node.generate(1) | [blockhash] = std_node.generate(1) | ||||
# make sure it was mined | # make sure it was mined | ||||
assert txid in std_node.getblock(blockhash)["tx"] | assert txid in std_node.getblock(blockhash)["tx"] | ||||
sync_blocks(self.nodes) | sync_blocks(self.nodes) | ||||
else: | else: | ||||
# signresult['errors'] will vary depending on input script. What | |||||
# occurs is that in sign.cpp, ProduceSignature gets back | |||||
# solved=false since SignStep sees a nonstandard input. Then, | |||||
# an empty SignatureData results. Back in rawtransaction.cpp's | |||||
# SignTransaction, it will then attempt to execute the | |||||
# scriptPubKey with an empty scriptSig. A P2PKH script will thus | |||||
# fail at OP_DUP with stack error, and P2PK/Multisig will fail | |||||
# once they hit a nonminimal push. The error message is just an | |||||
# artifact of the script type, basically. | |||||
assert_equal(signresult['complete'], False) | assert_equal(signresult['complete'], False) | ||||
assert_equal(signresult['errors'][0]['error'], sign_error) | |||||
# we start with an empty wallet | # we start with an empty wallet | ||||
assert_equal(std_node.getbalance(), 0) | assert_equal(std_node.getbalance(), 0) | ||||
address = std_node.getnewaddress() | address = std_node.getnewaddress() | ||||
pubkey = bytes.fromhex(std_node.getaddressinfo(address)['pubkey']) | pubkey = bytes.fromhex(std_node.getaddressinfo(address)['pubkey']) | ||||
pubkeyhash = hash160(pubkey) | pubkeyhash = hash160(pubkey) | ||||
# P2PK | # P2PK | ||||
fund_and_test_wallet(CScript([pubkey, OP_CHECKSIG]), True, True) | fund_and_test_wallet(CScript([pubkey, OP_CHECKSIG]), True, True) | ||||
fund_and_test_wallet( | fund_and_test_wallet( | ||||
CScript([OP_PUSHDATA1, pubkey, OP_CHECKSIG]), False, False) | CScript([OP_PUSHDATA1, pubkey, OP_CHECKSIG]), False, False, | ||||
sign_error='Data push larger than necessary') | |||||
# P2PKH | # P2PKH | ||||
fund_and_test_wallet(CScript( | fund_and_test_wallet(CScript( | ||||
[OP_DUP, OP_HASH160, pubkeyhash, OP_EQUALVERIFY, OP_CHECKSIG]), True, True) | [OP_DUP, OP_HASH160, pubkeyhash, OP_EQUALVERIFY, OP_CHECKSIG]), True, True) | ||||
# The signing error changes here since the script check (with empty | |||||
# scriptSig) hits OP_DUP before it hits the nonminimal push; in all | |||||
# other cases we hit the nonminimal push first. | |||||
fund_and_test_wallet(CScript( | fund_and_test_wallet(CScript( | ||||
[OP_DUP, OP_HASH160, OP_PUSHDATA1, pubkeyhash, OP_EQUALVERIFY, OP_CHECKSIG]), False, False) | [OP_DUP, OP_HASH160, OP_PUSHDATA1, pubkeyhash, OP_EQUALVERIFY, OP_CHECKSIG]), False, False, | ||||
sign_error='Unable to sign input, invalid stack size (possibly missing key)') | |||||
# Bare multisig | # Bare multisig | ||||
fund_and_test_wallet( | fund_and_test_wallet( | ||||
CScript([OP_1, pubkey, OP_1, OP_CHECKMULTISIG]), True, False, canSign=True) | CScript([OP_1, pubkey, OP_1, OP_CHECKMULTISIG]), True, False) | ||||
fund_and_test_wallet( | fund_and_test_wallet( | ||||
CScript([OP_1, OP_PUSHDATA1, pubkey, OP_1, OP_CHECKMULTISIG]), False, False) | CScript([OP_1, OP_PUSHDATA1, pubkey, OP_1, | ||||
OP_CHECKMULTISIG]), False, False, | |||||
sign_error='Data push larger than necessary') | |||||
fund_and_test_wallet( | fund_and_test_wallet( | ||||
CScript([OP_1, pubkey, b'\x01', OP_CHECKMULTISIG]), False, False) | CScript([OP_1, pubkey, b'\x01', OP_CHECKMULTISIG]), False, False, | ||||
sign_error='Data push larger than necessary') | |||||
fund_and_test_wallet( | fund_and_test_wallet( | ||||
CScript([b'\x01', pubkey, OP_1, OP_CHECKMULTISIG]), False, False) | CScript([b'\x01', pubkey, OP_1, OP_CHECKMULTISIG]), False, False, | ||||
sign_error='Data push larger than necessary') | |||||
# Note: 1-of-5 is nonstandard to fund but standard to spend. | # Note: 1-of-5 is nonstandard to fund but standard to spend. | ||||
fund_and_test_wallet( | fund_and_test_wallet( | ||||
CScript([OP_1, pubkey, pubkey, pubkey, pubkey, pubkey, OP_5, OP_CHECKMULTISIG]), False, False, canSign=True) | CScript([OP_1, pubkey, pubkey, pubkey, pubkey, pubkey, OP_5, OP_CHECKMULTISIG]), False, False) | ||||
fund_and_test_wallet( | fund_and_test_wallet( | ||||
CScript([OP_1, pubkey, pubkey, pubkey, OP_PUSHDATA1, pubkey, pubkey, OP_5, OP_CHECKMULTISIG]), False, False) | CScript([OP_1, pubkey, pubkey, pubkey, OP_PUSHDATA1, | ||||
pubkey, pubkey, OP_5, OP_CHECKMULTISIG]), False, False, | |||||
sign_error='Data push larger than necessary') | |||||
# Dust also is nonstandard to fund but standard to spend. | # Dust also is nonstandard to fund but standard to spend. | ||||
fund_and_test_wallet( | fund_and_test_wallet( | ||||
CScript([pubkey, OP_CHECKSIG]), False, True, amount=200, nonstd_error="dust (code 64)") | CScript([pubkey, OP_CHECKSIG]), False, True, amount=200, nonstd_error="dust (code 64)") | ||||
# and we end with an empty wallet | # and we end with an empty wallet | ||||
assert_equal(std_node.getbalance(), 0) | assert_equal(std_node.getbalance(), 0) | ||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
WalletStandardnessTest().main() | WalletStandardnessTest().main() |