def assert_tx_format_also_signed(self, utxo, segwit): raw = self.nodes[0].createrawtransaction( [{"txid": utxo["txid"], "vout": utxo["vout"]}], [{self.unknown_addr: "49.9"}, {"fee": "0.1"}] ) unsigned_decoded = self.nodes[0].decoderawtransaction(raw) assert_equal(len(unsigned_decoded["vin"]), 1) assert('txinwitness' not in unsigned_decoded["vin"][0]) # Cross-check python serialization tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(raw))) assert_equal(tx.vin[0].prevout.hash, int("0x"+utxo["txid"], 0)) assert_equal(len(tx.vin), len(unsigned_decoded["vin"])) assert_equal(len(tx.vout), len(unsigned_decoded["vout"])) # assert re-encoding serialized = bytes_to_hex_str(tx.serialize()) assert_equal(serialized, raw) # Now sign and repeat tests signed_raw = self.nodes[0].signrawtransactionwithwallet(raw)["hex"] signed_decoded = self.nodes[0].decoderawtransaction(signed_raw) assert_equal(len(signed_decoded["vin"]), 1) assert(("txinwitness" in signed_decoded["vin"][0]) == segwit) # Cross-check python serialization tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(signed_raw))) assert_equal(tx.vin[0].prevout.hash, int("0x"+utxo["txid"], 0)) assert_equal(bytes_to_hex_str(tx.vin[0].scriptSig), signed_decoded["vin"][0]["scriptSig"]["hex"]) # test witness if segwit: wit_decoded = signed_decoded["vin"][0]["txinwitness"] for i in range(len(wit_decoded)): assert_equal(bytes_to_hex_str(tx.wit.vtxinwit[0].scriptWitness.stack[i]), wit_decoded[i]) # assert re-encoding serialized = bytes_to_hex_str(tx.serialize()) assert_equal(serialized, signed_raw) txid = self.nodes[0].sendrawtransaction(serialized) nodetx = self.nodes[0].getrawtransaction(txid, 1) assert_equal(nodetx["txid"], tx.rehash()) # cross-check wtxid report from node wtxid = bytes_to_hex_str(ser_uint256(tx.calc_sha256(True))[::-1]) assert_equal(nodetx["wtxid"], wtxid) assert_equal(nodetx["hash"], wtxid) # witness hash stuff assert_equal(nodetx["withash"], tx.calc_witness_hash()) return (txid, wtxid)
def buildDummySegwitNameUpdate (self, name, value, addr): """ Builds a transaction that updates the given name to the given value and address. We assume that the name is at a native segwit script. The witness of the transaction will be set to two dummy stack elements so that the program itself is "well-formed" even if it won't execute successfully. """ data = self.node.name_show (name) u = self.findUnspent (Decimal ('0.01')) ins = [data, u] outs = {addr: Decimal ('0.01')} txHex = self.node.createrawtransaction (ins, outs) nameOp = {"op": "name_update", "name": name, "value": value} txHex = self.node.namerawtransaction (txHex, 0, nameOp)['hex'] txHex = self.node.signrawtransactionwithwallet (txHex)['hex'] tx = CTransaction () tx.deserialize (io.BytesIO (hex_str_to_bytes (txHex))) tx.wit = CTxWitness () tx.wit.vtxinwit.append (CTxInWitness ()) tx.wit.vtxinwit[0].scriptWitness = CScriptWitness () tx.wit.vtxinwit[0].scriptWitness.stack = [b"dummy"] * 2 txHex = tx.serialize ().hex () return txHex
def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001*COIN, _total_txs=None): if _total_txs is None: _total_txs = [0] if _total_txs[0] >= max_txs: return txout_value = (initial_value - fee) // tree_width if txout_value < fee: return vout = [CTxOut(txout_value, CScript([i+1])) for i in range(tree_width)] tx = CTransaction() tx.vin = [CTxIn(prevout, nSequence=0)] tx.vout = vout tx_hex = txToHex(tx) assert(len(tx.serialize()) < 100000) txid = self.nodes[0].sendrawtransaction(tx_hex, True) yield tx _total_txs[0] += 1 txid = int(txid, 16) for i, txout in enumerate(tx.vout): for x in branch(COutPoint(txid, i), txout_value, max_txs, tree_width=tree_width, fee=fee, _total_txs=_total_txs): yield x
def setScriptSigOps (self, txHex, ind, scriptSigOps): """ Update the given hex transaction by setting the scriptSig for the input with the given index. """ tx = CTransaction () tx.deserialize (io.BytesIO (hex_str_to_bytes (txHex))) tx.vin[ind].scriptSig = CScript (scriptSigOps) return tx.serialize ().hex ()
def sign_stake_tx(self, block, stake_in_value, fZPoS=False): ''' signs a coinstake transaction :param block: (CBlock) block with stake to sign stake_in_value: (int) staked amount fZPoS: (bool) zerocoin stake :return: stake_tx_signed: (CTransaction) signed tx ''' self.block_sig_key = CECKey() if fZPoS: self.log.info("Signing zPoS stake...") # Create raw zerocoin stake TX (signed) raw_stake = self.node.createrawzerocoinstake(block.prevoutStake) stake_tx_signed_raw_hex = raw_stake["hex"] # Get stake TX private key to sign the block with stake_pkey = raw_stake["private-key"] self.block_sig_key.set_compressed(True) self.block_sig_key.set_secretbytes(bytes.fromhex(stake_pkey)) else: # Create a new private key and get the corresponding public key self.block_sig_key.set_secretbytes(hash256(pack('<I', 0xffff))) pubkey = self.block_sig_key.get_pubkey() # Create the raw stake TX (unsigned) scriptPubKey = CScript([pubkey, OP_CHECKSIG]) outNValue = int(stake_in_value + 2*COIN) stake_tx_unsigned = CTransaction() stake_tx_unsigned.nTime = block.nTime stake_tx_unsigned.vin.append(CTxIn(block.prevoutStake)) stake_tx_unsigned.vin[0].nSequence = 0xffffffff stake_tx_unsigned.vout.append(CTxOut()) stake_tx_unsigned.vout.append(CTxOut(outNValue, scriptPubKey)) # Sign the stake TX stake_tx_signed_raw_hex = self.node.signrawtransaction(bytes_to_hex_str(stake_tx_unsigned.serialize()))['hex'] # Deserialize the signed raw tx into a CTransaction object and return it stake_tx_signed = CTransaction() stake_tx_signed.deserialize(BytesIO(hex_str_to_bytes(stake_tx_signed_raw_hex))) return stake_tx_signed
def test_multiple_children(self): node = self.nodes[0] self.log.info( "Testmempoolaccept a package in which a transaction has two children within the package" ) first_coin = self.coins.pop() value = (first_coin["amount"] - Decimal("0.0002") ) / 2 # Deduct reasonable fee and make 2 outputs inputs = [{"txid": first_coin["txid"], "vout": 0}] outputs = [{self.address: value}, {ADDRESS_BCRT1_P2WSH_OP_TRUE: value}] rawtx = node.createrawtransaction(inputs, outputs) parent_signed = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys) parent_tx = CTransaction() assert parent_signed["complete"] parent_tx.deserialize(BytesIO(hex_str_to_bytes(parent_signed["hex"]))) parent_txid = parent_tx.rehash() assert node.testmempoolaccept([parent_signed["hex"]])[0]["allowed"] parent_locking_script_a = parent_tx.vout[0].scriptPubKey.hex() child_value = value - Decimal("0.0001") # Child A (_, tx_child_a_hex, _, _) = self.chain_transaction(parent_txid, child_value, 0, parent_locking_script_a) assert not node.testmempoolaccept([tx_child_a_hex])[0]["allowed"] # Child B rawtx_b = node.createrawtransaction([{ "txid": parent_txid, "vout": 1 }], {self.address: child_value}) tx_child_b = CTransaction() tx_child_b.deserialize(BytesIO(hex_str_to_bytes(rawtx_b))) tx_child_b.wit.vtxinwit = [CTxInWitness()] tx_child_b.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] tx_child_b_hex = tx_child_b.serialize().hex() assert not node.testmempoolaccept([tx_child_b_hex])[0]["allowed"] self.log.info( "Testmempoolaccept with entire package, should work with children in either order" ) testres_multiple_ab = node.testmempoolaccept( rawtxs=[parent_signed["hex"], tx_child_a_hex, tx_child_b_hex]) testres_multiple_ba = node.testmempoolaccept( rawtxs=[parent_signed["hex"], tx_child_b_hex, tx_child_a_hex]) assert all([ testres["allowed"] for testres in testres_multiple_ab + testres_multiple_ba ]) testres_single = [] # Test accept and then submit each one individually, which should be identical to package testaccept for rawtx in [parent_signed["hex"], tx_child_a_hex, tx_child_b_hex]: testres = node.testmempoolaccept([rawtx]) testres_single.append(testres[0]) # Submit the transaction now so its child should have no problem validating node.sendrawtransaction(rawtx) assert_equal(testres_single, testres_multiple_ab)
def decoderawtransaction_asm_sighashtype(self): """Test decoding scripts via RPC command "decoderawtransaction". This test is in with the "decodescript" tests because they are testing the same "asm" script decodes. """ # this test case uses a random plain vanilla mainnet transaction with a single P2PKH input and output tx = '0100000001696a20784a2c70143f634e95227dbdfdf0ecd51647052e70854512235f5986ca010000008a47304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb014104d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536ffffffff0100e1f505000000001976a914eb6c6e0cdb2d256a32d97b8df1fc75d1920d9bca88ac00000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb[ALL] 04d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536', rpc_result['vin'][0]['scriptSig']['asm']) # this test case uses a mainnet transaction that has a P2SH input and both P2PKH and P2SH outputs. # it's from James D'Angelo's awesome introductory videos about multisig: https://www.youtube.com/watch?v=zIbUSaZBJgU and https://www.youtube.com/watch?v=OSA1pwlaypc # verify that we have not altered scriptPubKey decoding. tx = '01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914dc863734a218bfe83ef770ee9d41a27f824a6e5688acee2a02000000000017a9142a5edea39971049a540474c6a99edf0aa4074c588700000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('8e3730608c3b0bb5df54f09076e196bc292a8e39a78e73b44b6ba08c78f5cbb0', rpc_result['txid']) assert_equal('0 3045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea[ALL] 3045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75[ALL] 5221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53ae', rpc_result['vin'][0]['scriptSig']['asm']) assert_equal('OP_DUP OP_HASH160 dc863734a218bfe83ef770ee9d41a27f824a6e56 OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][0]['scriptPubKey']['asm']) assert_equal('OP_HASH160 2a5edea39971049a540474c6a99edf0aa4074c58 OP_EQUAL', rpc_result['vout'][1]['scriptPubKey']['asm']) txSave = CTransaction() txSave.deserialize(BytesIO(hex_str_to_bytes(tx))) # make sure that a specifically crafted op_return value will not pass all the IsDERSignature checks and then get decoded as a sighash type tx = '01000000015ded05872fdbda629c7d3d02b194763ce3b9b1535ea884e3c8e765d42e316724020000006b48304502204c10d4064885c42638cbff3585915b322de33762598321145ba033fc796971e2022100bb153ad3baa8b757e30a2175bd32852d2e1cb9080f84d7e32fcdfd667934ef1b012103163c0ff73511ea1743fb5b98384a2ff09dd06949488028fd819f4d83f56264efffffffff0200000000000000000b6a0930060201000201000180380100000000001976a9141cabd296e753837c086da7a45a6c2fe0d49d7b7b88ac00000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('OP_RETURN 300602010002010001', rpc_result['vout'][0]['scriptPubKey']['asm']) # verify that we have not altered scriptPubKey processing even of a specially crafted P2PKH pubkeyhash and P2SH redeem script hash that is made to pass the der signature checks tx = '01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914301102070101010101010102060101010101010188acee2a02000000000017a91430110207010101010101010206010101010101018700000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('OP_DUP OP_HASH160 3011020701010101010101020601010101010101 OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][0]['scriptPubKey']['asm']) assert_equal('OP_HASH160 3011020701010101010101020601010101010101 OP_EQUAL', rpc_result['vout'][1]['scriptPubKey']['asm']) # some more full transaction tests of varying specific scriptSigs. used instead of # tests in decodescript_script_sig because the decodescript RPC is specifically # for working on scriptPubKeys (argh!). push_signature = txSave.vin[0].scriptSig.hex()[2:(0x48*2+4)] signature = push_signature[2:] der_signature = signature[:-2] signature_sighash_decoded = der_signature + '[ALL]' signature_2 = der_signature + '82' push_signature_2 = '48' + signature_2 signature_2_sighash_decoded = der_signature + '[NONE|ANYONECANPAY]' # 1) P2PK scriptSig txSave.vin[0].scriptSig = hex_str_to_bytes(push_signature) rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex()) assert_equal(signature_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) # make sure that the sighash decodes come out correctly for a more complex / lesser used case. txSave.vin[0].scriptSig = hex_str_to_bytes(push_signature_2) rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex()) assert_equal(signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) # 2) multisig scriptSig txSave.vin[0].scriptSig = hex_str_to_bytes('00' + push_signature + push_signature_2) rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex()) assert_equal('0 ' + signature_sighash_decoded + ' ' + signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) # 3) test a scriptSig that contains more than push operations. # in fact, it contains an OP_RETURN with data specially crafted to cause improper decode if the code does not catch it. txSave.vin[0].scriptSig = hex_str_to_bytes('6a143011020701010101010101020601010101010101') rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex()) assert_equal('OP_RETURN 3011020701010101010101020601010101010101', rpc_result['vin'][0]['scriptSig']['asm'])
def run_test(self): node = self.nodes[0] self.log.info('Start with empty mempool, and 200 blocks') self.mempool_size = 0 assert_equal(node.getblockcount(), 200) assert_equal(node.getmempoolinfo()['size'], self.mempool_size) coins = node.listunspent() self.log.info('Should not accept garbage to testmempoolaccept') assert_raises_rpc_error(-3, 'Expected type array, got string', lambda: node.testmempoolaccept(rawtxs='ff00baar')) assert_raises_rpc_error(-8, 'Array must contain exactly one raw transaction for now', lambda: node.testmempoolaccept(rawtxs=['ff00baar', 'ff22'])) assert_raises_rpc_error(-22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar'])) self.log.info('A transaction already in the blockchain') coin = coins.pop() # Pick a random coin(base) to spend raw_tx_in_block = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{'txid': coin['txid'], 'vout': coin['vout']}], outputs=[{node.getnewaddress(): 0.3}, {node.getnewaddress(): 49}], ))['hex'] txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, maxfeerate=0) node.generate(1) self.mempool_size = 0 self.check_mempool_result( result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': '18: txn-already-known'}], rawtxs=[raw_tx_in_block], ) self.log.info('A transaction not in the mempool') fee = 0.00000700 raw_tx_0 = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{"txid": txid_in_block, "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER}], # RBF is used later outputs=[{node.getnewaddress(): 0.3 - fee}], ))['hex'] tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) txid_0 = tx.rehash() self.check_mempool_result( result_expected=[{'txid': txid_0, 'allowed': True}], rawtxs=[raw_tx_0], ) self.log.info('A final transaction not in the mempool') coin = coins.pop() # Pick a random coin(base) to spend raw_tx_final = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{'txid': coin['txid'], 'vout': coin['vout'], "sequence": 0xffffffff}], # SEQUENCE_FINAL outputs=[{node.getnewaddress(): 0.025}], locktime=node.getblockcount() + 2000, # Can be anything ))['hex'] tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_final))) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': True}], rawtxs=[tx.serialize().hex()], maxfeerate=0, ) node.sendrawtransaction(hexstring=raw_tx_final, maxfeerate=0) self.mempool_size += 1 self.log.info('A transaction in the mempool') node.sendrawtransaction(hexstring=raw_tx_0) self.mempool_size += 1 self.check_mempool_result( result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': '18: txn-already-in-mempool'}], rawtxs=[raw_tx_0], ) self.log.info('A transaction that replaces a mempool transaction') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vout[0].nValue -= int(fee * COIN) # Double the fee tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER + 1 # Now, opt out of RBF raw_tx_0 = node.signrawtransactionwithwallet(tx.serialize().hex())['hex'] tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) txid_0 = tx.rehash() self.check_mempool_result( result_expected=[{'txid': txid_0, 'allowed': True}], rawtxs=[raw_tx_0], ) self.log.info('A transaction that conflicts with an unconfirmed tx') # Send the transaction that replaces the mempool transaction and opts out of replaceability node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0) # take original raw_tx_0 tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee # skip re-signing the tx self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '18: txn-mempool-conflict'}], rawtxs=[tx.serialize().hex()], maxfeerate=0, ) self.log.info('A transaction with missing inputs, that never existed') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vin[0].prevout = COutPoint(hash=int('ff' * 32, 16), n=14) # skip re-signing the tx self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'missing-inputs'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with missing inputs, that existed once in the past') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vin[0].prevout.n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend raw_tx_1 = node.signrawtransactionwithwallet(tx.serialize().hex())['hex'] txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, maxfeerate=0) # Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them raw_tx_spend_both = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[ {'txid': txid_0, 'vout': 0}, {'txid': txid_1, 'vout': 0}, ], outputs=[{node.getnewaddress(): 0.1}] ))['hex'] txid_spend_both = node.sendrawtransaction(hexstring=raw_tx_spend_both, maxfeerate=0) node.generate(1) self.mempool_size = 0 # Now see if we can add the coins back to the utxo set by sending the exact txs again self.check_mempool_result( result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': 'missing-inputs'}], rawtxs=[raw_tx_0], ) self.check_mempool_result( result_expected=[{'txid': txid_1, 'allowed': False, 'reject-reason': 'missing-inputs'}], rawtxs=[raw_tx_1], ) self.log.info('Create a signed "reference" tx for later use') raw_tx_reference = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{'txid': txid_spend_both, 'vout': 0}], outputs=[{node.getnewaddress(): 0.05}], ))['hex'] tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) # Reference tx should be valid on itself self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': True}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with no outputs') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout = [] # Skip re-signing the transaction for context independent checks from now on # tx.deserialize(BytesIO(hex_str_to_bytes(node.signrawtransactionwithwallet(tx.serialize().hex())['hex']))) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-empty'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A really large transaction') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin = [tx.vin[0]] * math.ceil(MAX_BLOCK_BASE_SIZE / len(tx.vin[0].serialize())) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-oversize'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with negative output value') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].nValue *= -1 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-negative'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with too large output value') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].nValue = 21000000 * COIN + 1 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-vout-toolarge'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with too large sum of output values') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout = [tx.vout[0]] * 2 tx.vout[0].nValue = 21000000 * COIN self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-txouttotal-toolarge'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with duplicate inputs') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin = [tx.vin[0]] * 2 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-inputs-duplicate'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A coinbase transaction') # Pick the input of the first tx we signed, so it has to be a coinbase tx raw_tx_coinbase_spent = node.getrawtransaction(txid=node.decoderawtransaction(hexstring=raw_tx_in_block)['vin'][0]['txid']) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_coinbase_spent))) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: coinbase'}], rawtxs=[tx.serialize().hex()], ) self.log.info('Some nonstandard transactions') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.nVersion = 3 # A version currently non-standard self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: version'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].scriptPubKey = CScript([OP_0]) # Some non-standard script self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptpubkey'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[0].scriptSig = CScript([OP_HASH160]) # Some not-pushonly scriptSig self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptsig-not-pushonly'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript([OP_HASH160, hash160(b'burn'), OP_EQUAL])) num_scripts = 100000 // len(output_p2sh_burn.serialize()) # Use enough outputs to make the tx too large for our policy tx.vout = [output_p2sh_burn] * num_scripts self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: tx-size'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0] = output_p2sh_burn tx.vout[0].nValue -= 1 # Make output smaller, such that it is dust for our policy self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: dust'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff']) tx.vout = [tx.vout[0]] * 2 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: multi-op-return'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A timelocked transaction') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[0].nSequence -= 1 # Should be non-max, so locktime is not ignored tx.nLockTime = node.getblockcount() + 1 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-final'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction that is locked by BIP68 sequence logic') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[0].nSequence = 2 # We could include it in the second block mined from now, but not the very next one # Can skip re-signing the tx because of early rejection self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-BIP68-final'}], rawtxs=[tx.serialize().hex()], maxfeerate=0, )
def _test_moveWithCurrency(self): """ Sends currency to predefined addresses together with a move and checks the "out" field produced by the notifications. """ self.log.info("Sending move with currency...") addr1 = self.node.getnewaddress() addr2 = "dHNvNaqcD7XPDnoRjAoyfcMpHRi5upJD7p" # Send a move and spend coins in the same transaction. We use a raw # transaction, so that we can actually send *two* outputs to the *same* # address. This should work, and return it only once in the notification # with the total amount. We use amounts with precision down to satoshis # to check this works correctly. hex1 = self.node.getaddressinfo(addr1)["scriptPubKey"] hex2 = self.node.getaddressinfo(addr2)["scriptPubKey"] scr1 = CScript(hex_str_to_bytes(hex1)) scr2 = CScript(hex_str_to_bytes(hex2)) tx = CTransaction() name = self.node.name_show("p/x") tx.vin.append(CTxIn(COutPoint(int(name["txid"], 16), name["vout"]))) tx.vout.append(CTxOut(12345678, scr1)) tx.vout.append(CTxOut(142424242, scr2)) tx.vout.append(CTxOut(COIN, scr1)) tx.vout.append(CTxOut(COIN // 2, CScript([OP_TRUE]))) tx.vout.append(CTxOut(COIN // 100, scr1)) rawtx = tx.serialize().hex() nameOp = { "op": "name_update", "name": "p/x", "value": json.dumps({"g": { "a": "move" }}), } rawtx = self.node.namerawtransaction(rawtx, 4, nameOp)["hex"] rawtx = self.node.fundrawtransaction(rawtx)["hex"] signed = self.node.signrawtransactionwithwallet(rawtx) assert signed["complete"] txid = self.node.sendrawtransaction(signed["hex"]) self.node.generate(1) _, data = self.games["a"].receive() assert_equal(len(data["moves"]), 1) assertMove(data["moves"][0], txid, "x", "move") assert_equal(data["moves"][0]["burnt"], 0) out = data["moves"][0]["out"] assert_equal(len(out), 3) quant = Decimal('1.00000000') assert_equal( Decimal(out[addr1]).quantize(quant), Decimal('1.12345678')) assert_equal( Decimal(out[addr2]).quantize(quant), Decimal('1.42424242')) _, data = self.games["b"].receive() assert_equal(data["moves"], [])
def test_desc_size_limits(self): """Create 3 mempool transactions and 2 package transactions (25KvB each): Ma ^ ^ Mb Mc ^ ^ Pd Pe The top ancestor in the package exceeds descendant size limits but only if the in-mempool and in-package descendants are all considered together. """ node = self.nodes[0] assert_equal(0, node.getmempoolinfo()["size"]) target_weight = 21 * 1000 * WITNESS_SCALE_FACTOR high_fee = Decimal("0.0021") # 10 sats/vB self.log.info("Check that in-mempool and in-package descendant sizes are calculated properly in packages") # Top parent in mempool, Ma first_coin = self.coins.pop() parent_value = (first_coin["amount"] - high_fee) / 2 # Deduct fee and make 2 outputs inputs = [{"txid": first_coin["txid"], "vout": 0}] outputs = [{self.address : parent_value}, {ADDRESS_BCRT1_P2WSH_OP_TRUE: parent_value}] rawtx = node.createrawtransaction(inputs, outputs) parent_tx = bulk_transaction(tx_from_hex(rawtx), node, target_weight, self.privkeys) node.sendrawtransaction(parent_tx.serialize().hex()) package_hex = [] for j in range(2): # Two legs (left and right) # Mempool transaction (Mb and Mc) mempool_tx = CTransaction() spk = parent_tx.vout[j].scriptPubKey.hex() value = Decimal(parent_tx.vout[j].nValue) / COIN txid = parent_tx.rehash() prevtxs = [{ "txid": txid, "vout": j, "scriptPubKey": spk, "amount": value, }] if j == 0: # normal key (tx_small, _, _, _) = make_chain(node, self.address, self.privkeys, txid, value, j, spk, high_fee) mempool_tx = bulk_transaction(tx_small, node, target_weight, self.privkeys, prevtxs) else: # OP_TRUE inputs = [{"txid": txid, "vout": 1}] outputs = {self.address: value - high_fee} small_tx = tx_from_hex(node.createrawtransaction(inputs, outputs)) mempool_tx = bulk_transaction(small_tx, node, target_weight, None, prevtxs) node.sendrawtransaction(mempool_tx.serialize().hex()) # Package transaction (Pd and Pe) spk = mempool_tx.vout[0].scriptPubKey.hex() value = Decimal(mempool_tx.vout[0].nValue) / COIN txid = mempool_tx.rehash() (tx_small, _, _, _) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk, high_fee) prevtxs = [{ "txid": txid, "vout": 0, "scriptPubKey": spk, "amount": value, }] package_tx = bulk_transaction(tx_small, node, target_weight, self.privkeys, prevtxs) package_hex.append(package_tx.serialize().hex()) assert_equal(3, node.getmempoolinfo()["size"]) assert_equal(2, len(package_hex)) testres_too_heavy = node.testmempoolaccept(rawtxs=package_hex) for txres in testres_too_heavy: assert_equal(txres["package-error"], "package-mempool-limits") # Clear mempool and check that the package passes now node.generate(1) assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=package_hex)])
def run_test(self): node = self.nodes[0] self.log.info('Start with empty mempool, and 200 blocks') self.mempool_size = 0 assert_equal(node.getblockcount(), 200) assert_equal(node.getmempoolinfo()['size'], self.mempool_size) coins = node.listunspent() self.log.info('Should not accept garbage to testmempoolaccept') assert_raises_rpc_error(-3, 'Expected type array, got string', lambda: node.testmempoolaccept(rawtxs='ff00baar')) assert_raises_rpc_error(-8, 'Array must contain exactly one raw transaction for now', lambda: node.testmempoolaccept(rawtxs=['ff00baar', 'ff22'])) assert_raises_rpc_error(-22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar'])) self.log.info('A transaction already in the blockchain') coin = coins.pop() # Pick a random coin(base) to spend raw_tx_in_block = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{'txid': coin['txid'], 'vout': coin['vout']}], outputs=[{node.getnewaddress(): 0.3}, {node.getnewaddress(): 49}], ))['hex'] txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, maxfeerate=0) node.generate(1) self.mempool_size = 0 self.check_mempool_result( result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': 'txn-already-known'}], rawtxs=[raw_tx_in_block], ) self.log.info('A transaction not in the mempool') fee = 0.00000700 raw_tx_0 = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{"txid": txid_in_block, "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER}], # RBF is used later outputs=[{node.getnewaddress(): 0.3 - fee}], ))['hex'] tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) txid_0 = tx.rehash() self.check_mempool_result( result_expected=[{'txid': txid_0, 'allowed': True}], rawtxs=[raw_tx_0], ) self.log.info('A final transaction not in the mempool') coin = coins.pop() # Pick a random coin(base) to spend raw_tx_final = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{'txid': coin['txid'], 'vout': coin['vout'], "sequence": 0xffffffff}], # SEQUENCE_FINAL outputs=[{node.getnewaddress(): 0.025}], locktime=node.getblockcount() + 2000, # Can be anything ))['hex'] tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_final))) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': True}], rawtxs=[tx.serialize().hex()], maxfeerate=0, ) node.sendrawtransaction(hexstring=raw_tx_final, maxfeerate=0) self.mempool_size += 1 self.log.info('A transaction in the mempool') node.sendrawtransaction(hexstring=raw_tx_0) self.mempool_size += 1 self.check_mempool_result( result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': 'txn-already-in-mempool'}], rawtxs=[raw_tx_0], ) self.log.info('A transaction that replaces a mempool transaction') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vout[0].nValue -= int(fee * COIN) # Double the fee tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER + 1 # Now, opt out of RBF raw_tx_0 = node.signrawtransactionwithwallet(tx.serialize().hex())['hex'] tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) txid_0 = tx.rehash() self.check_mempool_result( result_expected=[{'txid': txid_0, 'allowed': True}], rawtxs=[raw_tx_0], ) self.log.info('A transaction that conflicts with an unconfirmed tx') # Send the transaction that replaces the mempool transaction and opts out of replaceability node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0) # take original raw_tx_0 tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee # skip re-signing the tx self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'txn-mempool-conflict'}], rawtxs=[tx.serialize().hex()], maxfeerate=0, ) self.log.info('A transaction with missing inputs, that never existed') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vin[0].prevout = COutPoint(hash=int('ff' * 32, 16), n=14) # skip re-signing the tx self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'missing-inputs'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with missing inputs, that existed once in the past') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) tx.vin[0].prevout.n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend raw_tx_1 = node.signrawtransactionwithwallet(tx.serialize().hex())['hex'] txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, maxfeerate=0) # Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them raw_tx_spend_both = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[ {'txid': txid_0, 'vout': 0}, {'txid': txid_1, 'vout': 0}, ], outputs=[{node.getnewaddress(): 0.1}] ))['hex'] txid_spend_both = node.sendrawtransaction(hexstring=raw_tx_spend_both, maxfeerate=0) node.generate(1) self.mempool_size = 0 # Now see if we can add the coins back to the utxo set by sending the exact txs again self.check_mempool_result( result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': 'missing-inputs'}], rawtxs=[raw_tx_0], ) self.check_mempool_result( result_expected=[{'txid': txid_1, 'allowed': False, 'reject-reason': 'missing-inputs'}], rawtxs=[raw_tx_1], ) self.log.info('Create a signed "reference" tx for later use') raw_tx_reference = node.signrawtransactionwithwallet(node.createrawtransaction( inputs=[{'txid': txid_spend_both, 'vout': 0}], outputs=[{node.getnewaddress(): 0.05}], ))['hex'] tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) # Reference tx should be valid on itself self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': True}], rawtxs=[tx.serialize().hex()], maxfeerate=0, ) self.log.info('A transaction with no outputs') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout = [] # Skip re-signing the transaction for context independent checks from now on # tx.deserialize(BytesIO(hex_str_to_bytes(node.signrawtransactionwithwallet(tx.serialize().hex())['hex']))) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-vout-empty'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A really large transaction') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin = [tx.vin[0]] * math.ceil(MAX_BLOCK_BASE_SIZE / len(tx.vin[0].serialize())) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-oversize'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with negative output value') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].nValue *= -1 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-vout-negative'}], rawtxs=[tx.serialize().hex()], ) # The following two validations prevent overflow of the output amounts (see CVE-2010-5139). self.log.info('A transaction with too large output value') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].nValue = MAX_MONEY + 1 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-vout-toolarge'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with too large sum of output values') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout = [tx.vout[0]] * 2 tx.vout[0].nValue = MAX_MONEY self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-txouttotal-toolarge'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction with duplicate inputs') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin = [tx.vin[0]] * 2 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-inputs-duplicate'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A coinbase transaction') # Pick the input of the first tx we signed, so it has to be a coinbase tx raw_tx_coinbase_spent = node.getrawtransaction(txid=node.decoderawtransaction(hexstring=raw_tx_in_block)['vin'][0]['txid']) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_coinbase_spent))) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'coinbase'}], rawtxs=[tx.serialize().hex()], ) self.log.info('Some nonstandard transactions') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.nVersion = 3 # A version currently non-standard self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'version'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].scriptPubKey = CScript([OP_0]) # Some non-standard script self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'scriptpubkey'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) key = ECKey() key.generate() pubkey = key.get_pubkey().get_bytes() tx.vout[0].scriptPubKey = CScript([OP_2, pubkey, pubkey, pubkey, OP_3, OP_CHECKMULTISIG]) # Some bare multisig script (2-of-3) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bare-multisig'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[0].scriptSig = CScript([OP_HASH160]) # Some not-pushonly scriptSig self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'scriptsig-not-pushonly'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[0].scriptSig = CScript([b'a' * 1648]) # Some too large scriptSig (>1650 bytes) self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'scriptsig-size'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript([OP_HASH160, hash160(b'burn'), OP_EQUAL])) num_scripts = 100000 // len(output_p2sh_burn.serialize()) # Use enough outputs to make the tx too large for our policy tx.vout = [output_p2sh_burn] * num_scripts self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'tx-size'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0] = output_p2sh_burn tx.vout[0].nValue -= 1 # Make output smaller, such that it is dust for our policy self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'dust'}], rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff']) tx.vout = [tx.vout[0]] * 2 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'multi-op-return'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A timelocked transaction') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[0].nSequence -= 1 # Should be non-max, so locktime is not ignored tx.nLockTime = node.getblockcount() + 1 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'non-final'}], rawtxs=[tx.serialize().hex()], ) self.log.info('A transaction that is locked by BIP68 sequence logic') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vin[0].nSequence = 2 # We could include it in the second block mined from now, but not the very next one # Can skip re-signing the tx because of early rejection self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'non-BIP68-final'}], rawtxs=[tx.serialize().hex()], maxfeerate=0, )
def run_test(self): self.log.info( 'prepare some coins for multiple *rawtransaction commands') self.nodes[2].generate(1) self.sync_all() self.nodes[0].generate(101) self.sync_all() self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.5) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 5.0) self.sync_all() self.nodes[0].generate(5) self.sync_all() self.log.info( 'Test getrawtransaction on genesis block coinbase returns an error' ) block = self.nodes[0].getblock(self.nodes[0].getblockhash(0)) assert_raises_rpc_error( -5, "The genesis block coinbase is not considered an ordinary transaction", self.nodes[0].getrawtransaction, block['merkleroot']) self.log.info( 'Check parameter types and required parameters of createrawtransaction' ) # Test `createrawtransaction` required parameters assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction) assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, []) # Test `createrawtransaction` invalid extra parameters assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, [], {}, 0, 'foo') # Test `createrawtransaction` invalid `inputs` txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' assert_raises_rpc_error(-3, "Expected type array", self.nodes[0].createrawtransaction, 'foo', {}) assert_raises_rpc_error(-1, "JSON value is not an object as expected", self.nodes[0].createrawtransaction, ['foo'], {}) assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].createrawtransaction, [{}], {}) assert_raises_rpc_error( -8, "txid must be of length 64 (not 3, for 'foo')", self.nodes[0].createrawtransaction, [{ 'txid': 'foo' }], {}) assert_raises_rpc_error( -8, "txid must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", self.nodes[0].createrawtransaction, [{ 'txid': 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844' }], {}) assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{ 'txid': txid }], {}) assert_raises_rpc_error(-8, "Invalid parameter, vout must be a number", self.nodes[0].createrawtransaction, [{ 'txid': txid, 'vout': 'foo' }], {}) assert_raises_rpc_error(-8, "Invalid parameter, vout must be positive", self.nodes[0].createrawtransaction, [{ 'txid': txid, 'vout': -1 }], {}) assert_raises_rpc_error( -8, "Invalid parameter, sequence number is out of range", self.nodes[0].createrawtransaction, [{ 'txid': txid, 'vout': 0, 'sequence': -1 }], {}) # Test `createrawtransaction` invalid `outputs` address = self.nodes[0].getnewaddress() address2 = self.nodes[0].getnewaddress() assert_raises_rpc_error(-1, "JSON value is not an array as expected", self.nodes[0].createrawtransaction, [], 'foo') # Should not throw for backwards compatibility self.nodes[0].createrawtransaction(inputs=[], outputs={}) self.nodes[0].createrawtransaction(inputs=[], outputs=[]) assert_raises_rpc_error(-8, "Data must be hexadecimal string", self.nodes[0].createrawtransaction, [], {'data': 'foo'}) assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].createrawtransaction, [], {'foo': 0}) assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].createrawtransaction, [], {address: 'foo'}) assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].createrawtransaction, [], {address: -1}) assert_raises_rpc_error( -8, "Invalid parameter, duplicated address: {}".format(address), self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)])) assert_raises_rpc_error( -8, "Invalid parameter, duplicated address: {}".format(address), self.nodes[0].createrawtransaction, [], [{ address: 1 }, { address: 1 }]) assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], [{ "data": 'aa' }, { "data": "bb" }]) assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], multidict([("data", 'aa'), ("data", "bb")])) assert_raises_rpc_error( -8, "Invalid parameter, key-value pair must contain exactly one key", self.nodes[0].createrawtransaction, [], [{ 'a': 1, 'b': 2 }]) assert_raises_rpc_error( -8, "Invalid parameter, key-value pair not an object as expected", self.nodes[0].createrawtransaction, [], [['key-value pair1'], ['2']]) # Test `createrawtransaction` invalid `locktime` assert_raises_rpc_error(-3, "Expected type number", self.nodes[0].createrawtransaction, [], {}, 'foo') assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, -1) assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, 4294967296) self.log.info( 'Check that createrawtransaction accepts an array and object as outputs' ) tx = CTransaction() # One output tx.deserialize( BytesIO( hex_str_to_bytes(self.nodes[2].createrawtransaction( inputs=[{ 'txid': txid, 'vout': 9 }], outputs={address: 99})))) assert_equal(len(tx.vout), 1) assert_equal( tx.serialize().hex(), self.nodes[2].createrawtransaction(inputs=[{ 'txid': txid, 'vout': 9 }], outputs=[{ address: 99 }]), ) # Two outputs tx.deserialize( BytesIO( hex_str_to_bytes(self.nodes[2].createrawtransaction( inputs=[{ 'txid': txid, 'vout': 9 }], outputs=OrderedDict([(address, 99), (address2, 99)]))))) assert_equal(len(tx.vout), 2) assert_equal( tx.serialize().hex(), self.nodes[2].createrawtransaction(inputs=[{ 'txid': txid, 'vout': 9 }], outputs=[{ address: 99 }, { address2: 99 }]), ) # Multiple mixed outputs tx.deserialize( BytesIO( hex_str_to_bytes(self.nodes[2].createrawtransaction( inputs=[{ 'txid': txid, 'vout': 9 }], outputs=multidict([(address, 99), (address2, 99), ('data', '99')]))))) assert_equal(len(tx.vout), 3) assert_equal( tx.serialize().hex(), self.nodes[2].createrawtransaction(inputs=[{ 'txid': txid, 'vout': 9 }], outputs=[{ address: 99 }, { address2: 99 }, { 'data': '99' }]), ) self.log.info('sendrawtransaction with missing input') # won't exists inputs = [{ 'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1 }] outputs = {self.nodes[0].getnewaddress(): 4.998} rawtx = self.nodes[2].createrawtransaction(inputs, outputs) rawtx = pad_raw_tx(rawtx) rawtx = self.nodes[2].signrawtransactionwithwallet(rawtx) # This will raise an exception since there are missing inputs assert_raises_rpc_error(-25, "bad-txns-inputs-missingorspent", self.nodes[2].sendrawtransaction, rawtx['hex']) ##################################### # getrawtransaction with block hash # ##################################### # make a tx by sending then generate 2 blocks; block1 has the tx in it tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1) block1, block2 = self.nodes[2].generate(2) self.sync_all() # We should be able to get the raw transaction by providing the correct # block gottx = self.nodes[0].getrawtransaction(tx, True, block1) assert_equal(gottx['txid'], tx) assert_equal(gottx['in_active_chain'], True) # We should not have the 'in_active_chain' flag when we don't provide a # block gottx = self.nodes[0].getrawtransaction(tx, True) assert_equal(gottx['txid'], tx) assert 'in_active_chain' not in gottx # We should not get the tx if we provide an unrelated block assert_raises_rpc_error(-5, "No such transaction found", self.nodes[0].getrawtransaction, tx, True, block2) # An invalid block hash should raise the correct errors assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].getrawtransaction, tx, True, True) assert_raises_rpc_error( -8, "parameter 3 must be of length 64 (not 6, for 'foobar')", self.nodes[0].getrawtransaction, tx, True, "foobar") assert_raises_rpc_error( -8, "parameter 3 must be of length 64 (not 8, for 'abcd1234')", self.nodes[0].getrawtransaction, tx, True, "abcd1234") assert_raises_rpc_error( -8, "parameter 3 must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].getrawtransaction, tx, True, "ZZZ0000000000000000000000000000000000000000000000000000000000000") assert_raises_rpc_error( -5, "Block hash not found", self.nodes[0].getrawtransaction, tx, True, "0000000000000000000000000000000000000000000000000000000000000000") # Undo the blocks and check in_active_chain self.nodes[0].invalidateblock(block1) gottx = self.nodes[0].getrawtransaction(txid=tx, verbose=True, blockhash=block1) assert_equal(gottx['in_active_chain'], False) self.nodes[0].reconsiderblock(block1) assert_equal(self.nodes[0].getbestblockhash(), block2) # # RAW TX MULTISIG TESTS # # # 2of2 test addr1 = self.nodes[2].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[2].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) # Tests for createmultisig and addmultisigaddress assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, ["01020304"]) # createmultisig can only take public keys self.nodes[0].createmultisig(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) # addmultisigaddress can take both pubkeys and addresses so long as # they are in the wallet, which is tested here. assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 2, [addr1Obj['pubkey'], addr1]) mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj['pubkey'], addr1])['address'] # use balance deltas instead of absolute values bal = self.nodes[2].getbalance() # send 1.2 BCH to msig adr txId = self.nodes[0].sendtoaddress(mSigObj, 1.2) self.sync_all() self.nodes[0].generate(1) self.sync_all() # node2 has both keys of the 2of2 ms addr., tx should affect the # balance assert_equal(self.nodes[2].getbalance(), bal + Decimal('1.20000000')) # 2of3 test from different nodes bal = self.nodes[2].getbalance() addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr3 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) addr3Obj = self.nodes[2].getaddressinfo(addr3) mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey'] ])['address'] txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) rawTx = self.nodes[0].decoderawtransaction(decTx['hex']) self.sync_all() self.nodes[0].generate(1) self.sync_all() # THIS IS AN INCOMPLETE FEATURE # NODE2 HAS TWO OF THREE KEY AND THE FUNDS SHOULD BE SPENDABLE AND # COUNT AT BALANCE CALCULATION # for now, assume the funds of a 2of3 multisig tx are not marked as # spendable assert_equal(self.nodes[2].getbalance(), bal) txDetails = self.nodes[0].gettransaction(txId, True) rawTx = self.nodes[0].decoderawtransaction(txDetails['hex']) vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('2.20000000')) bal = self.nodes[0].getbalance() inputs = [{ "txid": txId, "vout": vout['n'], "scriptPubKey": vout['scriptPubKey']['hex'], "amount": vout['value'], }] outputs = {self.nodes[0].getnewaddress(): 2.19} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned = self.nodes[1].signrawtransactionwithwallet( rawTx, inputs) # node1 only has one key, can't comp. sign the tx assert_equal(rawTxPartialSigned['complete'], False) rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx, inputs) # node2 can sign the tx compl., own two of three keys assert_equal(rawTxSigned['complete'], True) self.nodes[2].sendrawtransaction(rawTxSigned['hex']) rawTx = self.nodes[0].decoderawtransaction(rawTxSigned['hex']) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[0].getbalance(), bal + Decimal('50.00000000') + Decimal('2.19000000')) # block reward + tx rawTxBlock = self.nodes[0].getblock(self.nodes[0].getbestblockhash()) # 2of2 test for combining transactions bal = self.nodes[2].getbalance() addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) self.nodes[1].addmultisigaddress( 2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] mSigObjValid = self.nodes[2].getaddressinfo(mSigObj) txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) rawTx2 = self.nodes[0].decoderawtransaction(decTx['hex']) self.sync_all() self.nodes[0].generate(1) self.sync_all() # the funds of a 2of2 multisig tx should not be marked as spendable assert_equal(self.nodes[2].getbalance(), bal) txDetails = self.nodes[0].gettransaction(txId, True) rawTx2 = self.nodes[0].decoderawtransaction(txDetails['hex']) vout = next(o for o in rawTx2['vout'] if o['value'] == Decimal('2.20000000')) bal = self.nodes[0].getbalance() inputs = [{ "txid": txId, "vout": vout['n'], "scriptPubKey": vout['scriptPubKey']['hex'], "redeemScript": mSigObjValid['hex'], "amount": vout['value'] }] outputs = {self.nodes[0].getnewaddress(): 2.19} rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned1 = self.nodes[1].signrawtransactionwithwallet( rawTx2, inputs) self.log.debug(rawTxPartialSigned1) # node1 only has one key, can't comp. sign the tx assert_equal(rawTxPartialSigned1['complete'], False) rawTxPartialSigned2 = self.nodes[2].signrawtransactionwithwallet( rawTx2, inputs) self.log.debug(rawTxPartialSigned2) # node2 only has one key, can't comp. sign the tx assert_equal(rawTxPartialSigned2['complete'], False) rawTxComb = self.nodes[2].combinerawtransaction( [rawTxPartialSigned1['hex'], rawTxPartialSigned2['hex']]) self.log.debug(rawTxComb) self.nodes[2].sendrawtransaction(rawTxComb) rawTx2 = self.nodes[0].decoderawtransaction(rawTxComb) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[0].getbalance(), bal + Decimal('50.00000000') + Decimal('2.19000000')) # block reward + tx # getrawtransaction tests # 1. valid parameters - only supply txid txHash = rawTx["hash"] assert_equal(self.nodes[0].getrawtransaction(txHash), rawTxSigned['hex']) # 2. valid parameters - supply txid and 0 for non-verbose assert_equal(self.nodes[0].getrawtransaction(txHash, 0), rawTxSigned['hex']) # 3. valid parameters - supply txid and False for non-verbose assert_equal(self.nodes[0].getrawtransaction(txHash, False), rawTxSigned['hex']) # 4. valid parameters - supply txid and 1 for verbose. # We only check the "hex" field of the output so we don't need to # update this test every time the output format changes. assert_equal(self.nodes[0].getrawtransaction(txHash, 1)["hex"], rawTxSigned['hex']) # 5. valid parameters - supply txid and True for non-verbose assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex']) # 6. invalid parameters - supply txid and string "Flase" assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, "False") # 7. invalid parameters - supply txid and empty array assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, []) # 8. invalid parameters - supply txid and empty dict assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, {}) # Sanity checks on verbose getrawtransaction output rawTxOutput = self.nodes[0].getrawtransaction(txHash, True) assert_equal(rawTxOutput["hex"], rawTxSigned["hex"]) assert_equal(rawTxOutput["txid"], txHash) assert_equal(rawTxOutput["hash"], txHash) assert_greater_than(rawTxOutput["size"], 300) assert_equal(rawTxOutput["version"], 0x02) assert_equal(rawTxOutput["locktime"], 0) assert_equal(len(rawTxOutput["vin"]), 1) assert_equal(len(rawTxOutput["vout"]), 1) assert_equal(rawTxOutput["blockhash"], rawTxBlock["hash"]) assert_equal(rawTxOutput["confirmations"], 3) assert_equal(rawTxOutput["time"], rawTxBlock["time"]) assert_equal(rawTxOutput["blocktime"], rawTxBlock["time"]) inputs = [{ 'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'sequence': 1000 }] outputs = {self.nodes[0].getnewaddress(): 1} assert_raises_rpc_error(-8, 'Invalid parameter, missing vout key', self.nodes[0].createrawtransaction, inputs, outputs) inputs[0]['vout'] = "1" assert_raises_rpc_error(-8, 'Invalid parameter, vout must be a number', self.nodes[0].createrawtransaction, inputs, outputs) inputs[0]['vout'] = -1 assert_raises_rpc_error(-8, 'Invalid parameter, vout must be positive', self.nodes[0].createrawtransaction, inputs, outputs) inputs[0]['vout'] = 1 rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 1000) # 9. invalid parameters - sequence number out of range inputs[0]['sequence'] = -1 assert_raises_rpc_error( -8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) # 10. invalid parameters - sequence number out of range inputs[0]['sequence'] = 4294967296 assert_raises_rpc_error( -8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) inputs[0]['sequence'] = 4294967294 rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 4294967294) #################################### # TRANSACTION VERSION NUMBER TESTS # #################################### # Test the minimum transaction version number that fits in a signed # 32-bit integer. tx = CTransaction() tx.nVersion = -0x80000000 rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['version'], -0x80000000) # Test the maximum transaction version number that fits in a signed # 32-bit integer. tx = CTransaction() tx.nVersion = 0x7fffffff rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['version'], 0x7fffffff) self.log.info('sendrawtransaction/testmempoolaccept with maxfeerate') txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0) rawTx = self.nodes[0].getrawtransaction(txId, True) vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('1.00000000')) self.sync_all() inputs = [{"txid": txId, "vout": vout['n']}] # 1000 sat fee outputs = {self.nodes[0].getnewaddress(): Decimal("0.99999000")} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx) assert_equal(rawTxSigned['complete'], True) # 1000 sat fee, ~200 b transaction, fee rate should land around 5 sat/b = 0.00005000 BTC/kB # Thus, testmempoolaccept should reject testres = self.nodes[2].testmempoolaccept([rawTxSigned['hex']], 0.00001000)[0] assert_equal(testres['allowed'], False) assert_equal(testres['reject-reason'], '256: absurdly-high-fee') # and sendrawtransaction should throw assert_raises_rpc_error(-26, "absurdly-high-fee", self.nodes[2].sendrawtransaction, rawTxSigned['hex'], 0.00001000) # And below calls should both succeed testres = self.nodes[2].testmempoolaccept(rawtxs=[rawTxSigned['hex']], maxfeerate='0.00007000')[0] assert_equal(testres['allowed'], True) self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'], maxfeerate='0.00007000') ########################################## # Decoding weird scripts in transactions # ########################################## self.log.info('Decode correctly-formatted but weird transactions') tx = CTransaction() # empty self.nodes[0].decoderawtransaction(ToHex(tx)) # truncated push tx.vin.append(CTxIn(COutPoint(42, 0), b'\x4e\x00\x00')) tx.vin.append(CTxIn(COutPoint(42, 0), b'\x4c\x10TRUNC')) tx.vout.append(CTxOut(0, b'\x4e\x00\x00')) tx.vout.append(CTxOut(0, b'\x4c\x10TRUNC')) self.nodes[0].decoderawtransaction(ToHex(tx)) # giant pushes and long scripts tx.vin.append(CTxIn(COutPoint(42, 0), CScript([b'giant push' * 10000]))) tx.vout.append(CTxOut(0, CScript([b'giant push' * 10000]))) self.nodes[0].decoderawtransaction(ToHex(tx)) self.log.info('Refuse garbage after transaction') assert_raises_rpc_error(-22, 'TX decode failed', self.nodes[0].decoderawtransaction, ToHex(tx) + '00')
def run_test(self): # All nodes should start with 6,250 FRAG: starting_balance = 6250 for i in range(4): assert_equal(self.nodes[i].getbalance(), starting_balance) self.nodes[i].getnewaddress( ) # bug workaround, coins generated assigned to first getnewaddress! self.nodes[0].settxfee(.001) node0_address1 = self.nodes[0].getnewaddress() node0_txid1 = self.nodes[0].sendtoaddress(node0_address1, (1219 * 5)) node0_tx1 = self.nodes[0].gettransaction(node0_txid1) node0_address2 = self.nodes[0].getnewaddress() node0_txid2 = self.nodes[0].sendtoaddress(node0_address2, (29 * 5)) node0_tx2 = self.nodes[0].gettransaction(node0_txid2) assert_equal(self.nodes[0].getbalance(), starting_balance + node0_tx1["fee"] + node0_tx2["fee"]) # Coins are sent to node1_address node1_address = self.nodes[1].getnewaddress() tx1_amount = 40 * 5 # Send tx1, and another transaction tx2 that won't be cloned txid1 = self.nodes[0].sendtoaddress(node1_address, tx1_amount) txid2 = self.nodes[0].sendtoaddress(node1_address, (20 * 5)) # Construct a clone of tx1, to be malleated rawtx1 = self.nodes[0].getrawtransaction(txid1, 1) clone_inputs = [{ "txid": rawtx1["vin"][0]["txid"], "vout": rawtx1["vin"][0]["vout"] }] clone_outputs = { rawtx1["vout"][0]["scriptPubKey"]["addresses"][0]: float(rawtx1["vout"][0]["value"]), rawtx1["vout"][1]["scriptPubKey"]["addresses"][0]: float(rawtx1["vout"][1]["value"]) } clone_locktime = rawtx1["locktime"] clone_raw = self.nodes[0].createrawtransaction(clone_inputs, clone_outputs, clone_locktime) # createrawtransaction randomizes the order of its outputs, so swap them if necessary. clone_tx = CTransaction() clone_tx.deserialize(io.BytesIO(bytes.fromhex(clone_raw))) if (rawtx1["vout"][0]["value"] == tx1_amount and clone_tx.vout[0].nValue != tx1_amount * COIN or rawtx1["vout"][0]["value"] != tx1_amount and clone_tx.vout[0].nValue == tx1_amount * COIN): (clone_tx.vout[0], clone_tx.vout[1]) = (clone_tx.vout[1], clone_tx.vout[0]) if (rawtx1 == clone_raw): print("## !! Equal hex!!") assert (False) # Use a different signature hash type to sign. This creates an equivalent but malleated clone. # Don't send the clone anywhere yet tx1_clone = self.nodes[0].signrawtransaction( clone_tx.serialize().hex(), None, None, "ALL|ANYONECANPAY") assert_equal(tx1_clone["complete"], True) # Have node0 mine a block, if requested: if (self.options.mine_block): self.nodes[0].generate(1) sync_blocks(self.nodes[0:2]) tx1 = self.nodes[0].gettransaction(txid1) tx2 = self.nodes[0].gettransaction(txid2) # Node0's balance should be starting balance, plus 50BTC for another # matured block, minus tx1 and tx2 amounts, and minus transaction fees: expected = starting_balance + node0_tx1["fee"] + node0_tx2["fee"] if self.options.mine_block: expected += 250 expected += tx1["amount"] + tx1["fee"] expected += tx2["amount"] + tx2["fee"] assert_equal(self.nodes[0].getbalance(), expected) if self.options.mine_block: assert_equal(tx1["confirmations"], 1) assert_equal(tx2["confirmations"], 1) else: assert_equal(tx1["confirmations"], 0) assert_equal(tx2["confirmations"], 0) # Send clone and its parent to miner self.nodes[2].sendrawtransaction(node0_tx1["hex"]) txid1_clone = self.nodes[2].sendrawtransaction(tx1_clone["hex"]) # mine a block... self.nodes[2].generate(1) # Reconnect the split network, and sync chain: connect_nodes(self.nodes[1], 2) connect_nodes(self.nodes[0], 2) connect_nodes(self.nodes[2], 0) connect_nodes(self.nodes[2], 1) self.nodes[2].sendrawtransaction(node0_tx2["hex"]) self.nodes[2].sendrawtransaction(tx2["hex"]) self.nodes[2].generate(1) # Mine another block to make sure we sync sync_blocks(self.nodes) # Re-fetch transaction info: tx1 = self.nodes[0].gettransaction(txid1) tx1_clone = self.nodes[0].gettransaction(txid1_clone) tx2 = self.nodes[0].gettransaction(txid2) # Verify expected confirmations assert_equal(tx1["confirmations"], -2) assert_equal(tx1_clone["confirmations"], 2) assert_equal(tx2["confirmations"], 1) # Check node0's total balance; should be same as before the clone, + 100 BTC for 2 matured, # less possible orphaned matured subsidy expected += 500 if (self.options.mine_block): expected -= 250 assert_equal(self.nodes[0].getbalance(), expected)
def _test_coin_stats_index(self): node = self.nodes[0] index_node = self.nodes[1] # Both none and muhash options allow the usage of the index index_hash_options = ['none', 'muhash'] # Generate a normal transaction and mine it self.generate(node, COINBASE_MATURITY + 1) address = self.nodes[0].get_deterministic_priv_key().address node.sendtoaddress(address=address, amount=10, subtractfeefromamount=True) self.generate(node, 1) self.sync_blocks(timeout=120) self.log.info( "Test that gettxoutsetinfo() output is consistent with or without coinstatsindex option" ) res0 = node.gettxoutsetinfo('none') # The fields 'disk_size' and 'transactions' do not exist on the index del res0['disk_size'], res0['transactions'] for hash_option in index_hash_options: res1 = index_node.gettxoutsetinfo(hash_option) # The fields 'block_info' and 'total_unspendable_amount' only exist on the index del res1['block_info'], res1['total_unspendable_amount'] res1.pop('muhash', None) # Everything left should be the same assert_equal(res1, res0) self.log.info( "Test that gettxoutsetinfo() can get fetch data on specific heights with index" ) # Generate a new tip self.generate(node, 5) for hash_option in index_hash_options: # Fetch old stats by height res2 = index_node.gettxoutsetinfo(hash_option, 102) del res2['block_info'], res2['total_unspendable_amount'] res2.pop('muhash', None) assert_equal(res0, res2) # Fetch old stats by hash res3 = index_node.gettxoutsetinfo(hash_option, res0['bestblock']) del res3['block_info'], res3['total_unspendable_amount'] res3.pop('muhash', None) assert_equal(res0, res3) # It does not work without coinstatsindex assert_raises_rpc_error( -8, "Querying specific block heights requires coinstatsindex", node.gettxoutsetinfo, hash_option, 102) self.log.info("Test gettxoutsetinfo() with index and verbose flag") for hash_option in index_hash_options: # Genesis block is unspendable res4 = index_node.gettxoutsetinfo(hash_option, 0) assert_equal(res4['total_unspendable_amount'], 50) assert_equal( res4['block_info'], { 'unspendable': 50, 'prevout_spent': 0, 'new_outputs_ex_coinbase': 0, 'coinbase': 0, 'unspendables': { 'genesis_block': 50, 'bip30': 0, 'scripts': 0, 'unclaimed_rewards': 0 } }) self.block_sanity_check(res4['block_info']) # Test an older block height that included a normal tx res5 = index_node.gettxoutsetinfo(hash_option, 102) assert_equal(res5['total_unspendable_amount'], 50) assert_equal( res5['block_info'], { 'unspendable': 0, 'prevout_spent': 50, 'new_outputs_ex_coinbase': Decimal('49.99995560'), 'coinbase': Decimal('50.00004440'), 'unspendables': { 'genesis_block': 0, 'bip30': 0, 'scripts': 0, 'unclaimed_rewards': 0 } }) self.block_sanity_check(res5['block_info']) # Generate and send a normal tx with two outputs tx1_inputs = [] tx1_outputs = { self.nodes[0].getnewaddress(): 21, self.nodes[0].getnewaddress(): 42 } raw_tx1 = self.nodes[0].createrawtransaction(tx1_inputs, tx1_outputs) funded_tx1 = self.nodes[0].fundrawtransaction(raw_tx1) signed_tx1 = self.nodes[0].signrawtransactionwithwallet( funded_tx1['hex']) tx1_txid = self.nodes[0].sendrawtransaction(signed_tx1['hex']) # Find the right position of the 21 BTC output tx1_final = self.nodes[0].gettransaction(tx1_txid) for output in tx1_final['details']: if output['amount'] == Decimal( '21.00000000') and output['category'] == 'receive': n = output['vout'] # Generate and send another tx with an OP_RETURN output (which is unspendable) tx2 = CTransaction() tx2.vin.append(CTxIn(COutPoint(int(tx1_txid, 16), n), b'')) tx2.vout.append( CTxOut(int(Decimal('20.99') * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))) tx2_hex = self.nodes[0].signrawtransactionwithwallet( tx2.serialize().hex())['hex'] self.nodes[0].sendrawtransaction(tx2_hex) # Include both txs in a block self.generate(self.nodes[0], 1) self.sync_all() for hash_option in index_hash_options: # Check all amounts were registered correctly res6 = index_node.gettxoutsetinfo(hash_option, 108) assert_equal(res6['total_unspendable_amount'], Decimal('70.99000000')) assert_equal( res6['block_info'], { 'unspendable': Decimal('20.99000000'), 'prevout_spent': 111, 'new_outputs_ex_coinbase': Decimal('89.99993620'), 'coinbase': Decimal('50.01006380'), 'unspendables': { 'genesis_block': 0, 'bip30': 0, 'scripts': Decimal('20.99000000'), 'unclaimed_rewards': 0 } }) self.block_sanity_check(res6['block_info']) # Create a coinbase that does not claim full subsidy and also # has two outputs cb = create_coinbase(109, nValue=35) cb.vout.append(CTxOut(5 * COIN, CScript([OP_FALSE]))) cb.rehash() # Generate a block that includes previous coinbase tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblock(tip)['time'] + 1 block = create_block(int(tip, 16), cb, block_time) block.solve() self.nodes[0].submitblock(block.serialize().hex()) self.sync_all() for hash_option in index_hash_options: res7 = index_node.gettxoutsetinfo(hash_option, 109) assert_equal(res7['total_unspendable_amount'], Decimal('80.99000000')) assert_equal( res7['block_info'], { 'unspendable': 10, 'prevout_spent': 0, 'new_outputs_ex_coinbase': 0, 'coinbase': 40, 'unspendables': { 'genesis_block': 0, 'bip30': 0, 'scripts': 0, 'unclaimed_rewards': 10 } }) self.block_sanity_check(res7['block_info']) self.log.info("Test that the index is robust across restarts") res8 = index_node.gettxoutsetinfo('muhash') self.restart_node(1, extra_args=self.extra_args[1]) res9 = index_node.gettxoutsetinfo('muhash') assert_equal(res8, res9) self.generate(index_node, 1) res10 = index_node.gettxoutsetinfo('muhash') assert (res8['txouts'] < res10['txouts'])
def create_block(self, prev_hash, staking_prevouts, height, node_n, s_address, fInvalid=0): api = self.nodes[node_n] # Get current time current_time = int(time.time()) nTime = current_time & 0xfffffff0 # Create coinbase TX coinbase = create_coinbase(height) coinbase.vout[0].nValue = 0 coinbase.vout[0].scriptPubKey = b"" coinbase.nTime = nTime coinbase.rehash() # Create Block with coinbase block = create_block(int(prev_hash, 16), coinbase, nTime) # Find valid kernel hash - Create a new private key used for block signing. if not block.solve_stake(staking_prevouts): raise Exception("Not able to solve for any prev_outpoint") # Create coinstake TX amount, prev_time, prevScript = staking_prevouts[block.prevoutStake] outNValue = int(amount + 250 * COIN) stake_tx_unsigned = CTransaction() stake_tx_unsigned.nTime = block.nTime stake_tx_unsigned.vin.append(CTxIn(block.prevoutStake)) stake_tx_unsigned.vin[0].nSequence = 0xffffffff stake_tx_unsigned.vout.append(CTxOut()) stake_tx_unsigned.vout.append( CTxOut(outNValue, hex_str_to_bytes(prevScript))) if fInvalid == 1: # Create a new private key and get the corresponding public key block_sig_key = CECKey() block_sig_key.set_secretbytes(hash256(pack('<I', 0xffff))) pubkey = block_sig_key.get_pubkey() stake_tx_unsigned.vout[1].scriptPubKey = CScript( [pubkey, OP_CHECKSIG]) else: # Export the staking private key to sign the block with it privKey, compressed = wif_to_privkey(api.dumpprivkey(s_address)) block_sig_key = CECKey() block_sig_key.set_compressed(compressed) block_sig_key.set_secretbytes(bytes.fromhex(privKey)) # check the address addy = key_to_p2pkh(bytes_to_hex_str(block_sig_key.get_pubkey()), False, True) assert (addy == s_address) if fInvalid == 2: # add a new output with 100 coins from the pot new_key = CECKey() new_key.set_secretbytes(hash256(pack('<I', 0xffff))) pubkey = new_key.get_pubkey() stake_tx_unsigned.vout.append( CTxOut(100 * COIN, CScript([pubkey, OP_CHECKSIG]))) stake_tx_unsigned.vout[1].nValue = outNValue - 100 * COIN # Sign coinstake TX and add it to the block stake_tx_signed_raw_hex = api.signrawtransaction( bytes_to_hex_str(stake_tx_unsigned.serialize()))['hex'] stake_tx_signed = CTransaction() stake_tx_signed.deserialize( BytesIO(hex_str_to_bytes(stake_tx_signed_raw_hex))) block.vtx.append(stake_tx_signed) # Get correct MerkleRoot and rehash block block.hashMerkleRoot = block.calc_merkle_root() block.rehash() # sign block with block signing key and return it block.sign_block(block_sig_key) return block
def run_test(self): node = self.nodes[0] self.log.info('Start with empty mempool and 101 blocks') # The last 100 coinbase transactions are premature blockhash = self.generate(node, 101)[0] txid = node.getblock(blockhash=blockhash, verbosity=2)["tx"][0]["txid"] assert_equal(node.getmempoolinfo()['size'], 0) self.log.info("Submit parent with multiple script branches to mempool") hashlock = hash160(b'Preimage') witness_script = CScript([ OP_IF, OP_HASH160, hashlock, OP_EQUAL, OP_ELSE, OP_TRUE, OP_ENDIF ]) witness_program = sha256(witness_script) script_pubkey = CScript([OP_0, witness_program]) parent = CTransaction() parent.vin.append(CTxIn(COutPoint(int(txid, 16), 0), b"")) parent.vout.append(CTxOut(int(9.99998 * COIN), script_pubkey)) parent.rehash() privkeys = [node.get_deterministic_priv_key().key] raw_parent = node.signrawtransactionwithkey( hexstring=parent.serialize().hex(), privkeys=privkeys)['hex'] parent_txid = node.sendrawtransaction(hexstring=raw_parent, maxfeerate=0) self.generate(node, 1) peer_wtxid_relay = node.add_p2p_connection(P2PTxInvStore()) # Create a new transaction with witness solving first branch child_witness_script = CScript([OP_TRUE]) child_witness_program = sha256(child_witness_script) child_script_pubkey = CScript([OP_0, child_witness_program]) child_one = CTransaction() child_one.vin.append(CTxIn(COutPoint(int(parent_txid, 16), 0), b"")) child_one.vout.append(CTxOut(int(9.99996 * COIN), child_script_pubkey)) child_one.wit.vtxinwit.append(CTxInWitness()) child_one.wit.vtxinwit[0].scriptWitness.stack = [ b'Preimage', b'\x01', witness_script ] child_one_wtxid = child_one.getwtxid() child_one_txid = child_one.rehash() # Create another identical transaction with witness solving second branch child_two = deepcopy(child_one) child_two.wit.vtxinwit[0].scriptWitness.stack = [b'', witness_script] child_two_wtxid = child_two.getwtxid() child_two_txid = child_two.rehash() assert_equal(child_one_txid, child_two_txid) assert child_one_wtxid != child_two_wtxid self.log.info("Submit child_one to the mempool") txid_submitted = node.sendrawtransaction(child_one.serialize().hex()) assert_equal( node.getmempoolentry(txid_submitted)['wtxid'], child_one_wtxid) peer_wtxid_relay.wait_for_broadcast([child_one_wtxid]) assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0) # testmempoolaccept reports the "already in mempool" error assert_equal(node.testmempoolaccept([child_one.serialize().hex()]), [{ "txid": child_one_txid, "wtxid": child_one_wtxid, "allowed": False, "reject-reason": "txn-already-in-mempool" }]) assert_equal( node.testmempoolaccept([child_two.serialize().hex()])[0], { "txid": child_two_txid, "wtxid": child_two_wtxid, "allowed": False, "reject-reason": "txn-same-nonwitness-data-in-mempool" }) # sendrawtransaction will not throw but quits early when the exact same transaction is already in mempool node.sendrawtransaction(child_one.serialize().hex()) self.log.info("Connect another peer that hasn't seen child_one before") peer_wtxid_relay_2 = node.add_p2p_connection(P2PTxInvStore()) self.log.info("Submit child_two to the mempool") # sendrawtransaction will not throw but quits early when a transaction with the same non-witness data is already in mempool node.sendrawtransaction(child_two.serialize().hex()) # The node should rebroadcast the transaction using the wtxid of the correct transaction # (child_one, which is in its mempool). peer_wtxid_relay_2.wait_for_broadcast([child_one_wtxid]) assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)
def decoderawtransaction_asm_sighashtype(self): """Test decoding scripts via RPC command "decoderawtransaction". This test is in with the "decodescript" tests because they are testing the same "asm" script decodes. """ # this test case uses a random plain vanilla mainnet transaction with a single P2PKH input and output tx = '0100000001696a20784a2c70143f634e95227dbdfdf0ecd51647052e70854512235f5986ca010000008a47304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb014104d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536ffffffff0100e1f505000000001976a914eb6c6e0cdb2d256a32d97b8df1fc75d1920d9bca88ac00000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal( '304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb[ALL] 04d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536', rpc_result['vin'][0]['scriptSig']['asm']) # this test case uses a mainnet transaction that has a P2SH input and both P2PKH and P2SH outputs. # it's from James D'Angelo's awesome introductory videos about multisig: https://www.youtube.com/watch?v=zIbUSaZBJgU and https://www.youtube.com/watch?v=OSA1pwlaypc # verify that we have not altered scriptPubKey decoding. tx = '01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914dc863734a218bfe83ef770ee9d41a27f824a6e5688acee2a02000000000017a9142a5edea39971049a540474c6a99edf0aa4074c588700000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal( '8e3730608c3b0bb5df54f09076e196bc292a8e39a78e73b44b6ba08c78f5cbb0', rpc_result['txid']) assert_equal( '0 3045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea[ALL] 3045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75[ALL] 5221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53ae', rpc_result['vin'][0]['scriptSig']['asm']) assert_equal( 'OP_DUP OP_HASH160 dc863734a218bfe83ef770ee9d41a27f824a6e56 OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][0]['scriptPubKey']['asm']) assert_equal( 'OP_HASH160 2a5edea39971049a540474c6a99edf0aa4074c58 OP_EQUAL', rpc_result['vout'][1]['scriptPubKey']['asm']) txSave = CTransaction() txSave.deserialize(BytesIO(hex_str_to_bytes(tx))) # make sure that a specifically crafted op_return value will not pass all the IsDERSignature checks and then get decoded as a sighash type tx = '01000000015ded05872fdbda629c7d3d02b194763ce3b9b1535ea884e3c8e765d42e316724020000006b48304502204c10d4064885c42638cbff3585915b322de33762598321145ba033fc796971e2022100bb153ad3baa8b757e30a2175bd32852d2e1cb9080f84d7e32fcdfd667934ef1b012103163c0ff73511ea1743fb5b98384a2ff09dd06949488028fd819f4d83f56264efffffffff0200000000000000000b6a0930060201000201000180380100000000001976a9141cabd296e753837c086da7a45a6c2fe0d49d7b7b88ac00000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('OP_RETURN 300602010002010001', rpc_result['vout'][0]['scriptPubKey']['asm']) # verify that we have not altered scriptPubKey processing even of a specially crafted P2PKH pubkeyhash and P2SH redeem script hash that is made to pass the der signature checks tx = '01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914301102070101010101010102060101010101010188acee2a02000000000017a91430110207010101010101010206010101010101018700000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal( 'OP_DUP OP_HASH160 3011020701010101010101020601010101010101 OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][0]['scriptPubKey']['asm']) assert_equal( 'OP_HASH160 3011020701010101010101020601010101010101 OP_EQUAL', rpc_result['vout'][1]['scriptPubKey']['asm']) # verify that names shorter than 5 bytes aren't interpreted into asm as integers (issue #140) tx = '0071000002c323b5df3dbd501ef8e26683749fd13f785eac6d4f4c2083196a33262605e6ec010000008b48304502207cc397996cf41a4be7ce229226c2e3862557256e372dd325dfefaba26a4a44a20221008e71fc315563d04d7d92d6e2b0ef9c5e095df50777664819623156dcf3ec1c1e0141045bc24b6e33ecbe341e8195b4e8fcb3f8c7937ca6f671f799644968dc00e82d0160c5e14b687cba83840381dda04711939a225588eb0e2401d4f14dc9d3a5ccdaffffffff882be8eb700e1c6eaef2682839293ae9d5bfacf82cee80510d4dee6eed359c98000000004847304402206c6115eb47b4f3010a6c0e79ea5ee7f036fc1da22af172c4bd39a460758bb34402204cc40302837a3a61d9b1e12a99e73572f95da7b5005e5ce842a7b8b4ba90d7f901ffffffff02e007646300000000434104fb090d04c8f110012a9f4545b6cb1f93a6f22db1b35bef7e1545a4a0aef0124cd25c522851ba2120f12b124785adc6f09b13f8eaa294cfe54a601dc75f11df29ac40420f00000000005a5203662f6a394269746d65737361676520616464726573733a20424d2d3263554755687335436a6973526975514756574447334a514a47717a5967713134356d7576a9147368feca713f2a9d7780343b74007e26a4fcfcea88ac00000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal( 'OP_NAME_UPDATE 662f6a 4269746d65737361676520616464726573733a20424d2d3263554755687335436a6973526975514756574447334a514a47717a596771313435 OP_2DROP OP_DROP OP_DUP OP_HASH160 7368feca713f2a9d7780343b74007e26a4fcfcea OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][1]['scriptPubKey']['asm']) # some more full transaction tests of varying specific scriptSigs. used instead of # tests in decodescript_script_sig because the decodescript RPC is specifically # for working on scriptPubKeys (argh!). push_signature = bytes_to_hex_str( txSave.vin[0].scriptSig)[2:(0x48 * 2 + 4)] signature = push_signature[2:] der_signature = signature[:-2] signature_sighash_decoded = der_signature + '[ALL]' signature_2 = der_signature + '82' push_signature_2 = '48' + signature_2 signature_2_sighash_decoded = der_signature + '[NONE|ANYONECANPAY]' # 1) P2PK scriptSig txSave.vin[0].scriptSig = hex_str_to_bytes(push_signature) rpc_result = self.nodes[0].decoderawtransaction( bytes_to_hex_str(txSave.serialize())) assert_equal(signature_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) # make sure that the sighash decodes come out correctly for a more complex / lesser used case. txSave.vin[0].scriptSig = hex_str_to_bytes(push_signature_2) rpc_result = self.nodes[0].decoderawtransaction( bytes_to_hex_str(txSave.serialize())) assert_equal(signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) # 2) multisig scriptSig txSave.vin[0].scriptSig = hex_str_to_bytes('00' + push_signature + push_signature_2) rpc_result = self.nodes[0].decoderawtransaction( bytes_to_hex_str(txSave.serialize())) assert_equal( '0 ' + signature_sighash_decoded + ' ' + signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) # 3) test a scriptSig that contains more than push operations. # in fact, it contains an OP_RETURN with data specially crafted to cause improper decode if the code does not catch it. txSave.vin[0].scriptSig = hex_str_to_bytes( '6a143011020701010101010101020601010101010101') rpc_result = self.nodes[0].decoderawtransaction( bytes_to_hex_str(txSave.serialize())) assert_equal('OP_RETURN 3011020701010101010101020601010101010101', rpc_result['vin'][0]['scriptSig']['asm'])
def run_test(self): self.log.info( 'prepare some coins for multiple *rawtransaction commands') self.nodes[2].generate(1) self.sync_all() self.nodes[0].generate(101) self.sync_all() self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.5) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 5.0) self.sync_all() self.nodes[0].generate(5) self.sync_all() self.log.info( 'Test getrawtransaction on genesis block coinbase returns an error' ) block = self.nodes[0].getblock(self.nodes[0].getblockhash(0)) assert_raises_rpc_error( -5, "The genesis block coinbase is not considered an ordinary transaction", self.nodes[0].getrawtransaction, block['merkleroot']) self.log.info( 'Check parameter types and required parameters of createrawtransaction' ) # Test `createrawtransaction` required parameters assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction) assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, []) # Test `createrawtransaction` invalid extra parameters assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, [], {}, 0, False, 'foo') # Test `createrawtransaction` invalid `inputs` txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' assert_raises_rpc_error(-3, "Expected type array", self.nodes[0].createrawtransaction, 'foo', {}) assert_raises_rpc_error(-1, "JSON value is not an object as expected", self.nodes[0].createrawtransaction, ['foo'], {}) assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].createrawtransaction, [{}], {}) assert_raises_rpc_error( -8, "txid must be of length 64 (not 3, for 'foo')", self.nodes[0].createrawtransaction, [{ 'txid': 'foo' }], {}) assert_raises_rpc_error( -8, "txid must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", self.nodes[0].createrawtransaction, [{ 'txid': 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844' }], {}) assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{ 'txid': txid }], {}) assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{ 'txid': txid, 'vout': 'foo' }], {}) assert_raises_rpc_error(-8, "Invalid parameter, vout cannot be negative", self.nodes[0].createrawtransaction, [{ 'txid': txid, 'vout': -1 }], {}) assert_raises_rpc_error( -8, "Invalid parameter, sequence number is out of range", self.nodes[0].createrawtransaction, [{ 'txid': txid, 'vout': 0, 'sequence': -1 }], {}) # Test `createrawtransaction` invalid `outputs` address = self.nodes[0].getnewaddress() address2 = self.nodes[0].getnewaddress() assert_raises_rpc_error(-1, "JSON value is not an array as expected", self.nodes[0].createrawtransaction, [], 'foo') self.nodes[0].createrawtransaction( inputs=[], outputs={}) # Should not throw for backwards compatibility self.nodes[0].createrawtransaction(inputs=[], outputs=[]) assert_raises_rpc_error(-8, "Data must be hexadecimal string", self.nodes[0].createrawtransaction, [], {'data': 'foo'}) assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].createrawtransaction, [], {'foo': 0}) assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].createrawtransaction, [], {address: 'foo'}) assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].createrawtransaction, [], {address: -1}) assert_raises_rpc_error( -8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)])) assert_raises_rpc_error( -8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], [{ address: 1 }, { address: 1 }]) assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], [{ "data": 'aa' }, { "data": "bb" }]) assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], multidict([("data", 'aa'), ("data", "bb")])) assert_raises_rpc_error( -8, "Invalid parameter, key-value pair must contain exactly one key", self.nodes[0].createrawtransaction, [], [{ 'a': 1, 'b': 2 }]) assert_raises_rpc_error( -8, "Invalid parameter, key-value pair not an object as expected", self.nodes[0].createrawtransaction, [], [['key-value pair1'], ['2']]) # Test `createrawtransaction` invalid `locktime` assert_raises_rpc_error(-3, "Expected type number", self.nodes[0].createrawtransaction, [], {}, 'foo') assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, -1) assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, 4294967296) # Test `createrawtransaction` invalid `replaceable` assert_raises_rpc_error(-3, "Expected type bool", self.nodes[0].createrawtransaction, [], {}, 0, 'foo') self.log.info( 'Check that createrawtransaction accepts an array and object as outputs' ) tx = CTransaction() # One output tx.deserialize( BytesIO( hex_str_to_bytes(self.nodes[2].createrawtransaction( inputs=[{ 'txid': txid, 'vout': 9 }], outputs={address: 99})))) assert_equal(len(tx.vout), 1) assert_equal( tx.serialize().hex(), self.nodes[2].createrawtransaction(inputs=[{ 'txid': txid, 'vout': 9 }], outputs=[{ address: 99 }]), ) # Two outputs tx.deserialize( BytesIO( hex_str_to_bytes(self.nodes[2].createrawtransaction( inputs=[{ 'txid': txid, 'vout': 9 }], outputs=OrderedDict([(address, 99), (address2, 99)]))))) assert_equal(len(tx.vout), 2) assert_equal( tx.serialize().hex(), self.nodes[2].createrawtransaction(inputs=[{ 'txid': txid, 'vout': 9 }], outputs=[{ address: 99 }, { address2: 99 }]), ) # Multiple mixed outputs tx.deserialize( BytesIO( hex_str_to_bytes(self.nodes[2].createrawtransaction( inputs=[{ 'txid': txid, 'vout': 9 }], outputs=multidict([(address, 99), (address2, 99), ('data', '99')]))))) assert_equal(len(tx.vout), 3) assert_equal( tx.serialize().hex(), self.nodes[2].createrawtransaction(inputs=[{ 'txid': txid, 'vout': 9 }], outputs=[{ address: 99 }, { address2: 99 }, { 'data': '99' }]), ) for type in ["bech32", "p2sh-segwit", "legacy"]: addr = self.nodes[0].getnewaddress("", type) addrinfo = self.nodes[0].getaddressinfo(addr) pubkey = addrinfo["scriptPubKey"] self.log.info('sendrawtransaction with missing prevtx info (%s)' % (type)) # Test `signrawtransactionwithwallet` invalid `prevtxs` inputs = [{'txid': txid, 'vout': 3, 'sequence': 1000}] outputs = {self.nodes[0].getnewaddress(): 1} rawtx = self.nodes[0].createrawtransaction(inputs, outputs) prevtx = dict(txid=txid, scriptPubKey=pubkey, vout=3, amount=1) succ = self.nodes[0].signrawtransactionwithwallet(rawtx, [prevtx]) assert succ["complete"] if type == "legacy": del prevtx["amount"] succ = self.nodes[0].signrawtransactionwithwallet( rawtx, [prevtx]) assert succ["complete"] if type != "legacy": assert_raises_rpc_error( -3, "Missing amount", self.nodes[0].signrawtransactionwithwallet, rawtx, [{ "txid": txid, "scriptPubKey": pubkey, "vout": 3, }]) assert_raises_rpc_error(-3, "Missing vout", self.nodes[0].signrawtransactionwithwallet, rawtx, [{ "txid": txid, "scriptPubKey": pubkey, "amount": 1, }]) assert_raises_rpc_error(-3, "Missing txid", self.nodes[0].signrawtransactionwithwallet, rawtx, [{ "scriptPubKey": pubkey, "vout": 3, "amount": 1, }]) assert_raises_rpc_error(-3, "Missing scriptPubKey", self.nodes[0].signrawtransactionwithwallet, rawtx, [{ "txid": txid, "vout": 3, "amount": 1 }]) ######################################### # sendrawtransaction with missing input # ######################################### self.log.info('sendrawtransaction with missing input') inputs = [{ 'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1 }] #won't exists outputs = {self.nodes[0].getnewaddress(): 4.998} rawtx = self.nodes[2].createrawtransaction(inputs, outputs) rawtx = self.nodes[2].signrawtransactionwithwallet(rawtx) # This will raise an exception since there are missing inputs assert_raises_rpc_error(-25, "bad-txns-inputs-missingorspent", self.nodes[2].sendrawtransaction, rawtx['hex']) ##################################### # getrawtransaction with block hash # ##################################### # make a tx by sending then generate 2 blocks; block1 has the tx in it tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1) block1, block2 = self.nodes[2].generate(2) self.sync_all() # We should be able to get the raw transaction by providing the correct block gottx = self.nodes[0].getrawtransaction(tx, True, block1) assert_equal(gottx['txid'], tx) assert_equal(gottx['in_active_chain'], True) # We should not have the 'in_active_chain' flag when we don't provide a block gottx = self.nodes[0].getrawtransaction(tx, True) assert_equal(gottx['txid'], tx) assert 'in_active_chain' not in gottx # We should not get the tx if we provide an unrelated block assert_raises_rpc_error(-5, "No such transaction found", self.nodes[0].getrawtransaction, tx, True, block2) # An invalid block hash should raise the correct errors assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].getrawtransaction, tx, True, True) assert_raises_rpc_error( -8, "parameter 3 must be of length 64 (not 6, for 'foobar')", self.nodes[0].getrawtransaction, tx, True, "foobar") assert_raises_rpc_error( -8, "parameter 3 must be of length 64 (not 8, for 'abcd1234')", self.nodes[0].getrawtransaction, tx, True, "abcd1234") assert_raises_rpc_error( -8, "parameter 3 must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].getrawtransaction, tx, True, "ZZZ0000000000000000000000000000000000000000000000000000000000000") assert_raises_rpc_error( -5, "Block hash not found", self.nodes[0].getrawtransaction, tx, True, "0000000000000000000000000000000000000000000000000000000000000000") # Undo the blocks and check in_active_chain self.nodes[0].invalidateblock(block1) gottx = self.nodes[0].getrawtransaction(txid=tx, verbose=True, blockhash=block1) assert_equal(gottx['in_active_chain'], False) self.nodes[0].reconsiderblock(block1) assert_equal(self.nodes[0].getbestblockhash(), block2) ######################### # RAW TX MULTISIG TESTS # ######################### # 2of2 test addr1 = self.nodes[2].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[2].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) # Tests for createmultisig and addmultisigaddress assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, ["01020304"]) self.nodes[0].createmultisig( 2, [addr1Obj['pubkey'], addr2Obj['pubkey'] ]) # createmultisig can only take public keys assert_raises_rpc_error( -5, "Invalid public key", self.nodes[0].createmultisig, 2, [addr1Obj['pubkey'], addr1] ) # addmultisigaddress can take both pubkeys and addresses so long as they are in the wallet, which is tested here. mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj['pubkey'], addr1])['address'] #use balance deltas instead of absolute values bal = self.nodes[2].getbalance() # send 1.2 BTC to msig adr txId = self.nodes[0].sendtoaddress(mSigObj, 1.2) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal( self.nodes[2].getbalance(), bal + Decimal('1.20000000') ) #node2 has both keys of the 2of2 ms addr., tx should affect the balance # 2of3 test from different nodes bal = self.nodes[2].getbalance() addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr3 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) addr3Obj = self.nodes[2].getaddressinfo(addr3) mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey'] ])['address'] txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) rawTx = self.nodes[0].decoderawtransaction(decTx['hex']) self.sync_all() self.nodes[0].generate(1) self.sync_all() #THIS IS AN INCOMPLETE FEATURE #NODE2 HAS TWO OF THREE KEY AND THE FUNDS SHOULD BE SPENDABLE AND COUNT AT BALANCE CALCULATION assert_equal( self.nodes[2].getbalance(), bal ) #for now, assume the funds of a 2of3 multisig tx are not marked as spendable txDetails = self.nodes[0].gettransaction(txId, True) rawTx = self.nodes[0].decoderawtransaction(txDetails['hex']) vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('2.20000000')) bal = self.nodes[0].getbalance() inputs = [{ "txid": txId, "vout": vout['n'], "scriptPubKey": vout['scriptPubKey']['hex'], "amount": vout['value'] }] outputs = {self.nodes[0].getnewaddress(): 2.19} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned = self.nodes[1].signrawtransactionwithwallet( rawTx, inputs) assert_equal(rawTxPartialSigned['complete'], False) #node1 only has one key, can't comp. sign the tx rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx, inputs) assert_equal( rawTxSigned['complete'], True) #node2 can sign the tx compl., own two of three keys self.nodes[2].sendrawtransaction(rawTxSigned['hex']) rawTx = self.nodes[0].decoderawtransaction(rawTxSigned['hex']) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[0].getbalance(), bal + Decimal('50.00000000') + Decimal('2.19000000')) #block reward + tx # 2of2 test for combining transactions bal = self.nodes[2].getbalance() addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) self.nodes[1].addmultisigaddress( 2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] mSigObjValid = self.nodes[2].getaddressinfo(mSigObj) txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) rawTx2 = self.nodes[0].decoderawtransaction(decTx['hex']) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal( self.nodes[2].getbalance(), bal ) # the funds of a 2of2 multisig tx should not be marked as spendable txDetails = self.nodes[0].gettransaction(txId, True) rawTx2 = self.nodes[0].decoderawtransaction(txDetails['hex']) vout = next(o for o in rawTx2['vout'] if o['value'] == Decimal('2.20000000')) bal = self.nodes[0].getbalance() inputs = [{ "txid": txId, "vout": vout['n'], "scriptPubKey": vout['scriptPubKey']['hex'], "redeemScript": mSigObjValid['hex'], "amount": vout['value'] }] outputs = {self.nodes[0].getnewaddress(): 2.19} rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned1 = self.nodes[1].signrawtransactionwithwallet( rawTx2, inputs) self.log.debug(rawTxPartialSigned1) assert_equal(rawTxPartialSigned1['complete'], False) #node1 only has one key, can't comp. sign the tx rawTxPartialSigned2 = self.nodes[2].signrawtransactionwithwallet( rawTx2, inputs) self.log.debug(rawTxPartialSigned2) assert_equal(rawTxPartialSigned2['complete'], False) #node2 only has one key, can't comp. sign the tx rawTxComb = self.nodes[2].combinerawtransaction( [rawTxPartialSigned1['hex'], rawTxPartialSigned2['hex']]) self.log.debug(rawTxComb) self.nodes[2].sendrawtransaction(rawTxComb) rawTx2 = self.nodes[0].decoderawtransaction(rawTxComb) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[0].getbalance(), bal + Decimal('50.00000000') + Decimal('2.19000000')) #block reward + tx # decoderawtransaction tests # witness transaction encrawtx = "010000000001010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f50500000000000102616100000000" decrawtx = self.nodes[0].decoderawtransaction( encrawtx, True) # decode as witness transaction assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000')) assert_raises_rpc_error( -22, 'TX decode failed', self.nodes[0].decoderawtransaction, encrawtx, False) # force decode as non-witness transaction # non-witness transaction encrawtx = "01000000010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f505000000000000000000" decrawtx = self.nodes[0].decoderawtransaction( encrawtx, False) # decode as non-witness transaction assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000')) # getrawtransaction tests # 1. valid parameters - only supply txid txId = rawTx["txid"] assert_equal(self.nodes[0].getrawtransaction(txId), rawTxSigned['hex']) # 2. valid parameters - supply txid and 0 for non-verbose assert_equal(self.nodes[0].getrawtransaction(txId, 0), rawTxSigned['hex']) # 3. valid parameters - supply txid and False for non-verbose assert_equal(self.nodes[0].getrawtransaction(txId, False), rawTxSigned['hex']) # 4. valid parameters - supply txid and 1 for verbose. # We only check the "hex" field of the output so we don't need to update this test every time the output format changes. assert_equal(self.nodes[0].getrawtransaction(txId, 1)["hex"], rawTxSigned['hex']) # 5. valid parameters - supply txid and True for non-verbose assert_equal(self.nodes[0].getrawtransaction(txId, True)["hex"], rawTxSigned['hex']) # 6. invalid parameters - supply txid and string "Flase" assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txId, "Flase") # 7. invalid parameters - supply txid and empty array assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txId, []) # 8. invalid parameters - supply txid and empty dict assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txId, {}) inputs = [{ 'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1, 'sequence': 1000 }] outputs = {self.nodes[0].getnewaddress(): 1} rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 1000) # 9. invalid parameters - sequence number out of range inputs = [{ 'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1, 'sequence': -1 }] outputs = {self.nodes[0].getnewaddress(): 1} assert_raises_rpc_error( -8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) # 10. invalid parameters - sequence number out of range inputs = [{ 'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1, 'sequence': 4294967296 }] outputs = {self.nodes[0].getnewaddress(): 1} assert_raises_rpc_error( -8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) inputs = [{ 'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1, 'sequence': 4294967294 }] outputs = {self.nodes[0].getnewaddress(): 1} rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 4294967294) #################################### # TRANSACTION VERSION NUMBER TESTS # #################################### # Test the minimum transaction version number that fits in a signed 32-bit integer. # As transaction version is unsigned, this should convert to its unsigned equivalent. tx = CTransaction() tx.nVersion = -0x80000000 rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['version'], 0x80000000) # Test the maximum transaction version number that fits in a signed 32-bit integer. tx = CTransaction() tx.nVersion = 0x7fffffff rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['version'], 0x7fffffff) self.log.info('sendrawtransaction/testmempoolaccept with maxfeerate') # Test a transaction with a small fee. txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0) rawTx = self.nodes[0].getrawtransaction(txId, True) vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('1.00000000')) self.sync_all() inputs = [{"txid": txId, "vout": vout['n']}] # Fee 10,000 satoshis, (1 - (10000 sat * 0.00000001 BTC/sat)) = 0.9999 outputs = {self.nodes[0].getnewaddress(): Decimal("0.99990000")} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx) assert_equal(rawTxSigned['complete'], True) # Fee 10,000 satoshis, ~100 b transaction, fee rate should land around 100 sat/byte = 0.00100000 BTC/kB # Thus, testmempoolaccept should reject testres = self.nodes[2].testmempoolaccept([rawTxSigned['hex']], 0.00001000)[0] assert_equal(testres['allowed'], False) assert_equal(testres['reject-reason'], 'max-fee-exceeded') # and sendrawtransaction should throw assert_raises_rpc_error( -25, 'Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)', self.nodes[2].sendrawtransaction, rawTxSigned['hex'], 0.00001000) # and the following calls should both succeed testres = self.nodes[2].testmempoolaccept( rawtxs=[rawTxSigned['hex']])[0] assert_equal(testres['allowed'], True) self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex']) # Test a transaction with a large fee. txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0) rawTx = self.nodes[0].getrawtransaction(txId, True) vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('1.00000000')) self.sync_all() inputs = [{"txid": txId, "vout": vout['n']}] # Fee 2,000,000 satoshis, (1 - (2000000 sat * 0.00000001 BTC/sat)) = 0.98 outputs = {self.nodes[0].getnewaddress(): Decimal("0.98000000")} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx) assert_equal(rawTxSigned['complete'], True) # Fee 2,000,000 satoshis, ~100 b transaction, fee rate should land around 20,000 sat/byte = 0.20000000 BTC/kB # Thus, testmempoolaccept should reject testres = self.nodes[2].testmempoolaccept([rawTxSigned['hex']])[0] assert_equal(testres['allowed'], False) assert_equal(testres['reject-reason'], 'max-fee-exceeded') # and sendrawtransaction should throw assert_raises_rpc_error( -25, 'Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)', self.nodes[2].sendrawtransaction, rawTxSigned['hex']) # and the following calls should both succeed testres = self.nodes[2].testmempoolaccept(rawtxs=[rawTxSigned['hex']], maxfeerate='0.20000000')[0] assert_equal(testres['allowed'], True) self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'], maxfeerate='0.20000000')
def test_rbf(self): node = self.nodes[0] coin = self.coins.pop() inputs = [{ "txid": coin["txid"], "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER }] fee = Decimal('0.00125000') output = {node.get_deterministic_priv_key().address: 50 - fee} raw_replaceable_tx = node.createrawtransaction(inputs, output) signed_replaceable_tx = node.signrawtransactionwithkey( hexstring=raw_replaceable_tx, privkeys=self.privkeys) testres_replaceable = node.testmempoolaccept( [signed_replaceable_tx["hex"]]) replaceable_tx = CTransaction() replaceable_tx.deserialize( BytesIO(hex_str_to_bytes(signed_replaceable_tx["hex"]))) assert_equal(testres_replaceable, [{ "txid": replaceable_tx.rehash(), "wtxid": replaceable_tx.getwtxid(), "allowed": True, "vsize": replaceable_tx.get_vsize(), "fees": { "base": fee } }]) # Replacement transaction is identical except has double the fee replacement_tx = CTransaction() replacement_tx.deserialize( BytesIO(hex_str_to_bytes(signed_replaceable_tx["hex"]))) replacement_tx.vout[0].nValue -= int(fee * COIN) # Doubled fee signed_replacement_tx = node.signrawtransactionwithkey( replacement_tx.serialize().hex(), self.privkeys) replacement_tx.deserialize( BytesIO(hex_str_to_bytes(signed_replacement_tx["hex"]))) self.log.info( "Test that transactions within a package cannot replace each other" ) testres_rbf_conflicting = node.testmempoolaccept( [signed_replaceable_tx["hex"], signed_replacement_tx["hex"]]) assert_equal(testres_rbf_conflicting, [{ "txid": replaceable_tx.rehash(), "wtxid": replaceable_tx.getwtxid(), "package-error": "conflict-in-package" }, { "txid": replacement_tx.rehash(), "wtxid": replacement_tx.getwtxid(), "package-error": "conflict-in-package" }]) self.log.info( "Test that packages cannot conflict with mempool transactions, even if a valid BIP125 RBF" ) node.sendrawtransaction(signed_replaceable_tx["hex"]) testres_rbf_single = node.testmempoolaccept( [signed_replacement_tx["hex"]]) # This transaction is a valid BIP125 replace-by-fee assert testres_rbf_single[0]["allowed"] testres_rbf_package = self.independent_txns_testres_blank + [ { "txid": replacement_tx.rehash(), "wtxid": replacement_tx.getwtxid(), "allowed": False, "reject-reason": "bip125-replacement-disallowed" } ] self.assert_testres_equal( self.independent_txns_hex + [signed_replacement_tx["hex"]], testres_rbf_package)
def run_test(self): self.log.info( 'prepare some coins for multiple *rawtransaction commands') self.nodes[2].generate(1) self.sync_all() self.nodes[0].generate(101) self.sync_all() self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.5) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 5.0) self.sync_all() self.nodes[0].generate(5) self.sync_all() self.log.info( 'Test getrawtransaction on genesis block coinbase returns an error' ) block = self.nodes[0].getblock(self.nodes[0].getblockhash(0)) assert_raises_rpc_error( -5, "The genesis block coinbase is not considered an ordinary transaction", self.nodes[0].getrawtransaction, block['merkleroot']) self.log.info( 'Check parameter types and required parameters of createrawtransaction' ) # Test `createrawtransaction` required parameters assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction) assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, []) # Test `createrawtransaction` invalid extra parameters assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, [], {}, 0, False, 'foo') # Test `createrawtransaction` invalid `inputs` txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' assert_raises_rpc_error(-3, "Expected type array", self.nodes[0].createrawtransaction, 'foo', {}) assert_raises_rpc_error(-1, "JSON value is not an object as expected", self.nodes[0].createrawtransaction, ['foo'], {}) assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{}], {}) assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{ 'txid': 'foo' }], {}) assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{ 'txid': txid }], {}) assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{ 'txid': txid, 'vout': 'foo' }], {}) assert_raises_rpc_error(-8, "Invalid parameter, vout must be positive", self.nodes[0].createrawtransaction, [{ 'txid': txid, 'vout': -1 }], {}) assert_raises_rpc_error( -8, "Invalid parameter, sequence number is out of range", self.nodes[0].createrawtransaction, [{ 'txid': txid, 'vout': 0, 'sequence': -1 }], {}) # Test `createrawtransaction` invalid `outputs` address = self.nodes[0].getnewaddress() address2 = self.nodes[0].getnewaddress() assert_raises_rpc_error(-1, "JSON value is not an array as expected", self.nodes[0].createrawtransaction, [], 'foo') self.nodes[0].createrawtransaction( inputs=[], outputs={}) # Should not throw for backwards compatibility self.nodes[0].createrawtransaction(inputs=[], outputs=[]) assert_raises_rpc_error(-8, "Data must be hexadecimal string", self.nodes[0].createrawtransaction, [], {'data': 'foo'}) assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].createrawtransaction, [], {'foo': 0}) assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].createrawtransaction, [], {address: 'foo'}) assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].createrawtransaction, [], {address: -1}) assert_raises_rpc_error( -8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)])) assert_raises_rpc_error( -8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], [{ address: 1 }, { address: 1 }]) assert_raises_rpc_error( -8, "Invalid parameter, key-value pair must contain exactly one key", self.nodes[0].createrawtransaction, [], [{ 'a': 1, 'b': 2 }]) assert_raises_rpc_error( -8, "Invalid parameter, key-value pair not an object as expected", self.nodes[0].createrawtransaction, [], [['key-value pair1'], ['2']]) # Test `createrawtransaction` invalid `locktime` assert_raises_rpc_error(-3, "Expected type number", self.nodes[0].createrawtransaction, [], {}, 'foo') assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, -1) assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, 4294967296) # Test `createrawtransaction` invalid `replaceable` assert_raises_rpc_error(-3, "Expected type bool", self.nodes[0].createrawtransaction, [], {}, 0, 'foo') self.log.info( 'Check that createrawtransaction accepts an array and object as outputs' ) tx = CTransaction() # One output tx.deserialize( BytesIO( hex_str_to_bytes(self.nodes[2].createrawtransaction( inputs=[{ 'txid': txid, 'vout': 9 }], outputs={address: 99})))) assert_equal(len(tx.vout), 1) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{ 'txid': txid, 'vout': 9 }], outputs=[{ address: 99 }]), ) # Two outputs tx.deserialize( BytesIO( hex_str_to_bytes(self.nodes[2].createrawtransaction( inputs=[{ 'txid': txid, 'vout': 9 }], outputs=OrderedDict([(address, 99), (address2, 99)]))))) assert_equal(len(tx.vout), 2) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{ 'txid': txid, 'vout': 9 }], outputs=[{ address: 99 }, { address2: 99 }]), ) # Two data outputs tx.deserialize( BytesIO( hex_str_to_bytes(self.nodes[2].createrawtransaction( inputs=[{ 'txid': txid, 'vout': 9 }], outputs=multidict([('data', '99'), ('data', '99')]))))) assert_equal(len(tx.vout), 2) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{ 'txid': txid, 'vout': 9 }], outputs=[{ 'data': '99' }, { 'data': '99' }]), ) # Multiple mixed outputs tx.deserialize( BytesIO( hex_str_to_bytes(self.nodes[2].createrawtransaction( inputs=[{ 'txid': txid, 'vout': 9 }], outputs=multidict([(address, 99), ('data', '99'), ('data', '99')]))))) assert_equal(len(tx.vout), 3) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{ 'txid': txid, 'vout': 9 }], outputs=[{ address: 99 }, { 'data': '99' }, { 'data': '99' }]), ) self.log.info('sendrawtransaction with missing input') inputs = [{ 'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1 }] #won't exists outputs = {self.nodes[0].getnewaddress(): 4.998} rawtx = self.nodes[2].createrawtransaction(inputs, outputs) rawtx = self.nodes[2].signrawtransactionwithwallet(rawtx) # This will raise an exception since there are missing inputs assert_raises_rpc_error(-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) ##################################### # getrawtransaction with block hash # ##################################### # make a tx by sending then generate 2 blocks; block1 has the tx in it tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1) block1, block2 = self.nodes[2].generate(2) self.sync_all() # We should be able to get the raw transaction by providing the correct block gottx = self.nodes[0].getrawtransaction(tx, True, block1) assert_equal(gottx['txid'], tx) assert_equal(gottx['in_active_chain'], True) # We should not have the 'in_active_chain' flag when we don't provide a block gottx = self.nodes[0].getrawtransaction(tx, True) assert_equal(gottx['txid'], tx) assert 'in_active_chain' not in gottx # We should not get the tx if we provide an unrelated block assert_raises_rpc_error(-5, "No such transaction found", self.nodes[0].getrawtransaction, tx, True, block2) # An invalid block hash should raise the correct errors assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, True) assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, "foobar") assert_raises_rpc_error(-8, "parameter 3 must be of length 64", self.nodes[0].getrawtransaction, tx, True, "abcd1234") assert_raises_rpc_error( -5, "Block hash not found", self.nodes[0].getrawtransaction, tx, True, "0000000000000000000000000000000000000000000000000000000000000000") # Undo the blocks and check in_active_chain self.nodes[0].invalidateblock(block1) gottx = self.nodes[0].getrawtransaction(txid=tx, verbose=True, blockhash=block1) assert_equal(gottx['in_active_chain'], False) self.nodes[0].reconsiderblock(block1) assert_equal(self.nodes[0].getbestblockhash(), block2) ######################### # RAW TX MULTISIG TESTS # ######################### # 2of2 test addr1 = self.nodes[2].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[2].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) # Tests for createmultisig and addmultisigaddress assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, ["01020304"]) self.nodes[0].createmultisig( 2, [addr1Obj['pubkey'], addr2Obj['pubkey'] ]) # createmultisig can only take public keys assert_raises_rpc_error( -5, "Invalid public key", self.nodes[0].createmultisig, 2, [addr1Obj['pubkey'], addr1] ) # addmultisigaddress can take both pubkeys and addresses so long as they are in the wallet, which is tested here. mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj['pubkey'], addr1])['address'] #use balance deltas instead of absolute values bal = self.nodes[2].getbalance() # send 1.2 PTC to msig adr txId = self.nodes[0].sendtoaddress(mSigObj, 1.2) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal( self.nodes[2].getbalance(), bal + Decimal('1.20000000') ) #node2 has both keys of the 2of2 ms addr., tx should affect the balance # 2of3 test from different nodes bal = self.nodes[2].getbalance() addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr3 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) addr3Obj = self.nodes[2].getaddressinfo(addr3) mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey'] ])['address'] txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) rawTx = self.nodes[0].decoderawtransaction(decTx['hex']) self.sync_all() self.nodes[0].generate(1) self.sync_all() #THIS IS AN INCOMPLETE FEATURE #NODE2 HAS TWO OF THREE KEY AND THE FUNDS SHOULD BE SPENDABLE AND COUNT AT BALANCE CALCULATION assert_equal( self.nodes[2].getbalance(), bal ) #for now, assume the funds of a 2of3 multisig tx are not marked as spendable txDetails = self.nodes[0].gettransaction(txId, True) rawTx = self.nodes[0].decoderawtransaction(txDetails['hex']) vout = False for outpoint in rawTx['vout']: if outpoint['value'] == Decimal('2.20000000'): vout = outpoint break bal = self.nodes[0].getbalance() inputs = [{ "txid": txId, "vout": vout['n'], "scriptPubKey": vout['scriptPubKey']['hex'], "amount": vout['value'] }] outputs = {self.nodes[0].getnewaddress(): 2.19} rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned = self.nodes[1].signrawtransactionwithwallet( rawTx, inputs) assert_equal(rawTxPartialSigned['complete'], False) #node1 only has one key, can't comp. sign the tx rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx, inputs) assert_equal( rawTxSigned['complete'], True) #node2 can sign the tx compl., own two of three keys self.nodes[2].sendrawtransaction(rawTxSigned['hex']) rawTx = self.nodes[0].decoderawtransaction(rawTxSigned['hex']) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[0].getbalance(), bal + Decimal('50.00000000') + Decimal('2.19000000')) #block reward + tx # 2of2 test for combining transactions bal = self.nodes[2].getbalance() addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) self.nodes[1].addmultisigaddress( 2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] mSigObj = self.nodes[2].addmultisigaddress( 2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] mSigObjValid = self.nodes[2].getaddressinfo(mSigObj) txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) rawTx2 = self.nodes[0].decoderawtransaction(decTx['hex']) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal( self.nodes[2].getbalance(), bal ) # the funds of a 2of2 multisig tx should not be marked as spendable txDetails = self.nodes[0].gettransaction(txId, True) rawTx2 = self.nodes[0].decoderawtransaction(txDetails['hex']) vout = False for outpoint in rawTx2['vout']: if outpoint['value'] == Decimal('2.20000000'): vout = outpoint break bal = self.nodes[0].getbalance() inputs = [{ "txid": txId, "vout": vout['n'], "scriptPubKey": vout['scriptPubKey']['hex'], "redeemScript": mSigObjValid['hex'], "amount": vout['value'] }] outputs = {self.nodes[0].getnewaddress(): 2.19} rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned1 = self.nodes[1].signrawtransactionwithwallet( rawTx2, inputs) self.log.debug(rawTxPartialSigned1) assert_equal(rawTxPartialSigned1['complete'], False) #node1 only has one key, can't comp. sign the tx rawTxPartialSigned2 = self.nodes[2].signrawtransactionwithwallet( rawTx2, inputs) self.log.debug(rawTxPartialSigned2) assert_equal(rawTxPartialSigned2['complete'], False) #node2 only has one key, can't comp. sign the tx rawTxComb = self.nodes[2].combinerawtransaction( [rawTxPartialSigned1['hex'], rawTxPartialSigned2['hex']]) self.log.debug(rawTxComb) self.nodes[2].sendrawtransaction(rawTxComb) rawTx2 = self.nodes[0].decoderawtransaction(rawTxComb) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[0].getbalance(), bal + Decimal('50.00000000') + Decimal('2.19000000')) #block reward + tx # decoderawtransaction tests # witness transaction encrawtx = "010000000001010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f50500000000000000000000" decrawtx = self.nodes[0].decoderawtransaction( encrawtx, True) # decode as witness transaction assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000')) assert_raises_rpc_error( -22, 'TX decode failed', self.nodes[0].decoderawtransaction, encrawtx, False) # force decode as non-witness transaction # non-witness transaction encrawtx = "01000000010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f505000000000000000000" decrawtx = self.nodes[0].decoderawtransaction( encrawtx, False) # decode as non-witness transaction assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000')) # getrawtransaction tests # 1. valid parameters - only supply txid txHash = rawTx["hash"] assert_equal(self.nodes[0].getrawtransaction(txHash), rawTxSigned['hex']) # 2. valid parameters - supply txid and 0 for non-verbose assert_equal(self.nodes[0].getrawtransaction(txHash, 0), rawTxSigned['hex']) # 3. valid parameters - supply txid and False for non-verbose assert_equal(self.nodes[0].getrawtransaction(txHash, False), rawTxSigned['hex']) # 4. valid parameters - supply txid and 1 for verbose. # We only check the "hex" field of the output so we don't need to update this test every time the output format changes. assert_equal(self.nodes[0].getrawtransaction(txHash, 1)["hex"], rawTxSigned['hex']) # 5. valid parameters - supply txid and True for non-verbose assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex']) # 6. invalid parameters - supply txid and string "Flase" assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, "Flase") # 7. invalid parameters - supply txid and empty array assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, []) # 8. invalid parameters - supply txid and empty dict assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, {}) inputs = [{ 'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1, 'sequence': 1000 }] outputs = {self.nodes[0].getnewaddress(): 1} rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 1000) # 9. invalid parameters - sequence number out of range inputs = [{ 'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1, 'sequence': -1 }] outputs = {self.nodes[0].getnewaddress(): 1} assert_raises_rpc_error( -8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) # 10. invalid parameters - sequence number out of range inputs = [{ 'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1, 'sequence': 4294967296 }] outputs = {self.nodes[0].getnewaddress(): 1} assert_raises_rpc_error( -8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) inputs = [{ 'txid': "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout': 1, 'sequence': 4294967294 }] outputs = {self.nodes[0].getnewaddress(): 1} rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 4294967294) #################################### # TRANSACTION VERSION NUMBER TESTS # #################################### # Test the minimum transaction version number that fits in a signed 32-bit integer. tx = CTransaction() tx.nVersion = -0x80000000 rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['version'], -0x80000000) # Test the maximum transaction version number that fits in a signed 32-bit integer. tx = CTransaction() tx.nVersion = 0x7fffffff rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['version'], 0x7fffffff)
def test_sequence_lock_confirmed_inputs(self): # Create lots of confirmed utxos, and use them to generate lots of random # transactions. max_outputs = 50 addresses = [] while len(addresses) < max_outputs: addresses.append(self.nodes[0].getnewaddress()) while len(self.nodes[0].listunspent()) < 200: import random random.shuffle(addresses) num_outputs = random.randint(1, max_outputs) outputs = {} for i in range(num_outputs): outputs[addresses[i]] = random.randint(1, 20) * 0.01 self.nodes[0].sendmany("", outputs) self.generate(self.nodes[0], 1) utxos = self.nodes[0].listunspent() # Try creating a lot of random transactions. # Each time, choose a random number of inputs, and randomly set # some of those inputs to be sequence locked (and randomly choose # between height/time locking). Small random chance of making the locks # all pass. for _ in range(400): # Randomly choose up to 10 inputs num_inputs = random.randint(1, 10) random.shuffle(utxos) # Track whether any sequence locks used should fail should_pass = True # Track whether this transaction was built with sequence locks using_sequence_locks = False tx = CTransaction() tx.nVersion = 2 value = 0 for j in range(num_inputs): sequence_value = 0xfffffffe # this disables sequence locks # 50% chance we enable sequence locks if random.randint(0, 1): using_sequence_locks = True # 10% of the time, make the input sequence value pass input_will_pass = (random.randint(1, 10) == 1) sequence_value = utxos[j]["confirmations"] if not input_will_pass: sequence_value += 1 should_pass = False # Figure out what the median-time-past was for the confirmed input # Note that if an input has N confirmations, we're going back N blocks # from the tip so that we're looking up MTP of the block # PRIOR to the one the input appears in, as per the BIP68 spec. orig_time = self.get_median_time_past( utxos[j]["confirmations"]) cur_time = self.get_median_time_past(0) # MTP of the tip # can only timelock this input if it's not too old -- otherwise use height can_time_lock = True if ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY ) >= SEQUENCE_LOCKTIME_MASK: can_time_lock = False # if time-lockable, then 50% chance we make this a time lock if random.randint(0, 1) and can_time_lock: # Find first time-lock value that fails, or latest one that succeeds time_delta = sequence_value << SEQUENCE_LOCKTIME_GRANULARITY if input_will_pass and time_delta > cur_time - orig_time: sequence_value = ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY) elif (not input_will_pass and time_delta <= cur_time - orig_time): sequence_value = ( (cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY) + 1 sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG tx.vin.append( CTxIn(COutPoint(int(utxos[j]["txid"], 16), utxos[j]["vout"]), nSequence=sequence_value)) value += utxos[j]["amount"] * COIN # Overestimate the size of the tx - signatures should be less than 120 bytes, and leave 50 for the output tx_size = len(tx.serialize().hex()) // 2 + 120 * num_inputs + 50 tx.vout.append( CTxOut(int(value - self.relayfee * tx_size * COIN / 1000), DUMMY_P2WPKH_SCRIPT)) rawtx = self.nodes[0].signrawtransactionwithwallet( tx.serialize().hex())["hex"] if (using_sequence_locks and not should_pass): # This transaction should be rejected assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx) else: # This raw transaction should be accepted self.nodes[0].sendrawtransaction(rawtx) utxos = self.nodes[0].listunspent()
def decoderawtransaction_asm_sighashtype(self): """Test decoding scripts via RPC command "decoderawtransaction". This test is in with the "decodescript" tests because they are testing the same "asm" script decodes. """ # this test case uses a random plain vanilla mainnet transaction with a single P2PKH input and output tx = '0100000001696a20784a2c70143f634e95227dbdfdf0ecd51647052e70854512235f5986ca010000008a47304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb014104d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536ffffffff0100e1f505000000001976a914eb6c6e0cdb2d256a32d97b8df1fc75d1920d9bca88ac00000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb[ALL] 04d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536', rpc_result['vin'][0]['scriptSig']['asm']) # this test case uses a mainnet transaction that has a P2SH input and both P2PKH and P2SH outputs. # it's from James D'Angelo's awesome introductory videos about multisig: https://www.youtube.com/watch?v=zIbUSaZBJgU and https://www.youtube.com/watch?v=OSA1pwlaypc # verify that we have not altered scriptPubKey decoding. tx = '01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914dc863734a218bfe83ef770ee9d41a27f824a6e5688acee2a02000000000017a9142a5edea39971049a540474c6a99edf0aa4074c588700000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('8e3730608c3b0bb5df54f09076e196bc292a8e39a78e73b44b6ba08c78f5cbb0', rpc_result['txid']) assert_equal('0 3045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea[ALL] 3045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75[ALL] 5221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53ae', rpc_result['vin'][0]['scriptSig']['asm']) assert_equal('OP_DUP OP_HASH160 dc863734a218bfe83ef770ee9d41a27f824a6e56 OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][0]['scriptPubKey']['asm']) assert_equal('OP_HASH160 2a5edea39971049a540474c6a99edf0aa4074c58 OP_EQUAL', rpc_result['vout'][1]['scriptPubKey']['asm']) txSave = CTransaction() txSave.deserialize(BytesIO(hex_str_to_bytes(tx))) # make sure that a specifically crafted op_return value will not pass all the IsDERSignature checks and then get decoded as a sighash type tx = '01000000015ded05872fdbda629c7d3d02b194763ce3b9b1535ea884e3c8e765d42e316724020000006b48304502204c10d4064885c42638cbff3585915b322de33762598321145ba033fc796971e2022100bb153ad3baa8b757e30a2175bd32852d2e1cb9080f84d7e32fcdfd667934ef1b012103163c0ff73511ea1743fb5b98384a2ff09dd06949488028fd819f4d83f56264efffffffff0200000000000000000b6a0930060201000201000180380100000000001976a9141cabd296e753837c086da7a45a6c2fe0d49d7b7b88ac00000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('OP_RETURN 300602010002010001', rpc_result['vout'][0]['scriptPubKey']['asm']) # verify that we have not altered scriptPubKey processing even of a specially crafted P2PKH pubkeyhash and P2SH redeem script hash that is made to pass the der signature checks tx = '01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914301102070101010101010102060101010101010188acee2a02000000000017a91430110207010101010101010206010101010101018700000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('OP_DUP OP_HASH160 3011020701010101010101020601010101010101 OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][0]['scriptPubKey']['asm']) assert_equal('OP_HASH160 3011020701010101010101020601010101010101 OP_EQUAL', rpc_result['vout'][1]['scriptPubKey']['asm']) # some more full transaction tests of varying specific scriptSigs. used instead of # tests in decodescript_script_sig because the decodescript RPC is specifically # for working on scriptPubKeys (argh!). push_signature = bytes_to_hex_str(txSave.vin[0].scriptSig)[2:(0x48*2+4)] signature = push_signature[2:] der_signature = signature[:-2] signature_sighash_decoded = der_signature + '[ALL]' signature_2 = der_signature + '82' push_signature_2 = '48' + signature_2 signature_2_sighash_decoded = der_signature + '[NONE|ANYONECANPAY]' # 1) P2PK scriptSig txSave.vin[0].scriptSig = hex_str_to_bytes(push_signature) rpc_result = self.nodes[0].decoderawtransaction(bytes_to_hex_str(txSave.serialize())) assert_equal(signature_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) # make sure that the sighash decodes come out correctly for a more complex / lesser used case. txSave.vin[0].scriptSig = hex_str_to_bytes(push_signature_2) rpc_result = self.nodes[0].decoderawtransaction(bytes_to_hex_str(txSave.serialize())) assert_equal(signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) # 2) multisig scriptSig txSave.vin[0].scriptSig = hex_str_to_bytes('00' + push_signature + push_signature_2) rpc_result = self.nodes[0].decoderawtransaction(bytes_to_hex_str(txSave.serialize())) assert_equal('0 ' + signature_sighash_decoded + ' ' + signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) # 3) test a scriptSig that contains more than push operations. # in fact, it contains an OP_RETURN with data specially crafted to cause improper decode if the code does not catch it. txSave.vin[0].scriptSig = hex_str_to_bytes('6a143011020701010101010101020601010101010101') rpc_result = self.nodes[0].decoderawtransaction(bytes_to_hex_str(txSave.serialize())) assert_equal('OP_RETURN 3011020701010101010101020601010101010101', rpc_result['vin'][0]['scriptSig']['asm'])
def test_sequence_lock_unconfirmed_inputs(self): # Store height so we can easily reset the chain at the end of the test cur_height = self.nodes[0].getblockcount() # Create a mempool tx. txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2) tx1 = tx_from_hex(self.nodes[0].getrawtransaction(txid)) tx1.rehash() # Anyone-can-spend mempool tx. # Sequence lock of 0 should pass. tx2 = CTransaction() tx2.nVersion = 2 tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)] tx2.vout = [ CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), DUMMY_P2WPKH_SCRIPT) ] tx2_raw = self.nodes[0].signrawtransactionwithwallet( tx2.serialize().hex())["hex"] tx2 = tx_from_hex(tx2_raw) tx2.rehash() self.nodes[0].sendrawtransaction(tx2_raw) # Create a spend of the 0th output of orig_tx with a sequence lock # of 1, and test what happens when submitting. # orig_tx.vout[0] must be an anyone-can-spend output def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock): sequence_value = 1 if not use_height_lock: sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG tx = CTransaction() tx.nVersion = 2 tx.vin = [ CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value) ] tx.vout = [ CTxOut(int(orig_tx.vout[0].nValue - relayfee * COIN), DUMMY_P2WPKH_SCRIPT) ] tx.rehash() if (orig_tx.hash in node.getrawmempool()): # sendrawtransaction should fail if the tx is in the mempool assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, tx.serialize().hex()) else: # sendrawtransaction should succeed if the tx is not in the mempool node.sendrawtransaction(tx.serialize().hex()) return tx test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=True) test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False) # Now mine some blocks, but make sure tx2 doesn't get mined. # Use prioritisetransaction to lower the effective feerate to 0 self.nodes[0].prioritisetransaction(txid=tx2.hash, fee_delta=int(-self.relayfee * COIN)) cur_time = int(time.time()) for _ in range(10): self.nodes[0].setmocktime(cur_time + 600) self.generate(self.nodes[0], 1, sync_fun=self.no_op) cur_time += 600 assert tx2.hash in self.nodes[0].getrawmempool() test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=True) test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False) # Mine tx2, and then try again self.nodes[0].prioritisetransaction(txid=tx2.hash, fee_delta=int(self.relayfee * COIN)) # Advance the time on the node so that we can test timelocks self.nodes[0].setmocktime(cur_time + 600) # Save block template now to use for the reorg later tmpl = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS) self.generate(self.nodes[0], 1) assert tx2.hash not in self.nodes[0].getrawmempool() # Now that tx2 is not in the mempool, a sequence locked spend should # succeed tx3 = test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False) assert tx3.hash in self.nodes[0].getrawmempool() self.generate(self.nodes[0], 1) assert tx3.hash not in self.nodes[0].getrawmempool() # One more test, this time using height locks tx4 = test_nonzero_locks(tx3, self.nodes[0], self.relayfee, use_height_lock=True) assert tx4.hash in self.nodes[0].getrawmempool() # Now try combining confirmed and unconfirmed inputs tx5 = test_nonzero_locks(tx4, self.nodes[0], self.relayfee, use_height_lock=True) assert tx5.hash not in self.nodes[0].getrawmempool() utxos = self.nodes[0].listunspent() tx5.vin.append( CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]), nSequence=1)) tx5.vout[0].nValue += int(utxos[0]["amount"] * COIN) raw_tx5 = self.nodes[0].signrawtransactionwithwallet( tx5.serialize().hex())["hex"] assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5) # Test mempool-BIP68 consistency after reorg # # State of the transactions in the last blocks: # ... -> [ tx2 ] -> [ tx3 ] # tip-1 tip # And currently tx4 is in the mempool. # # If we invalidate the tip, tx3 should get added to the mempool, causing # tx4 to be removed (fails sequence-lock). self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) assert tx4.hash not in self.nodes[0].getrawmempool() assert tx3.hash in self.nodes[0].getrawmempool() # Now mine 2 empty blocks to reorg out the current tip (labeled tip-1 in # diagram above). # This would cause tx2 to be added back to the mempool, which in turn causes # tx3 to be removed. for i in range(2): block = create_block(tmpl=tmpl, ntime=cur_time) block.solve() tip = block.sha256 assert_equal(None if i == 1 else 'inconclusive', self.nodes[0].submitblock(block.serialize().hex())) tmpl = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS) tmpl['previousblockhash'] = '%x' % tip tmpl['transactions'] = [] cur_time += 1 mempool = self.nodes[0].getrawmempool() assert tx3.hash not in mempool assert tx2.hash in mempool # Reset the chain and get rid of the mocktimed-blocks self.nodes[0].setmocktime(0) self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height + 1)) self.generate(self.nodes[0], 10, sync_fun=self.no_op)
def test_namescript_p2sh (self): """ Tests how name prefixes interact with P2SH outputs and redeem scripts. """ self.log.info ("Testing name prefix and P2SH interactions...") # This test only needs a single node and no syncing. node = self.nodes[0] name = "d/p2sh" value = "value" new = node.name_new (name) node.generate (12) self.firstupdateName (0, name, new, value) node.generate (1) baseHeight = node.getblockcount () self.checkNameWithHeight (0, name, value, baseHeight) # Prepare some scripts and P2SH addresses we use later. We build the # name script prefix for an update to our testname, so that we can build # P2SH redeem scripts with (or without) it. nameBytes = codecs.encode (name, 'ascii') valueBytes = codecs.encode (value, 'ascii') updOps = [OP_NAME_UPDATE, nameBytes, valueBytes, OP_2DROP, OP_DROP] anyoneOps = [OP_TRUE] updScript = CScript (updOps) anyoneScript = CScript (anyoneOps) updAndAnyoneScript = CScript (updOps + anyoneOps) anyoneAddr = self.getP2SH (0, anyoneScript) updAndAnyoneAddr = self.getP2SH (0, updAndAnyoneScript) # Send the name to the anyone-can-spend name-update script directly. # This is expected to update the name (verifies the update script is good). tx = CTransaction () tx.nVersion = NAMECOIN_TX_VERSION data = node.name_show (name) tx.vin.append (CTxIn (COutPoint (int (data['txid'], 16), data['vout']))) tx.vout.append (CTxOut (COIN // 100, updAndAnyoneScript)) txHex = tx.serialize ().hex () txHex = node.fundrawtransaction (txHex)['hex'] signed = node.signrawtransactionwithwallet (txHex) assert signed['complete'] node.sendrawtransaction (signed['hex']) node.generate (1) self.checkNameWithHeight (0, name, value, baseHeight + 1) # Send the name to the anyone-can-spend P2SH address. This should just # work fine and update the name. self.updateAnyoneCanSpendName (0, name, "value2", anyoneAddr, []) node.generate (1) self.checkNameWithHeight (0, name, "value2", baseHeight + 2) # Send a coin to the P2SH address with name prefix. This should just # work fine but not update the name. We should be able to spend the coin # again from that address. txid = node.sendtoaddress (updAndAnyoneAddr, 2) tx = node.getrawtransaction (txid) ind = self.rawtxOutputIndex (0, tx, updAndAnyoneAddr) node.generate (1) ins = [{"txid": txid, "vout": ind}] addr = node.getnewaddress () out = {addr: 1} tx = node.createrawtransaction (ins, out) tx = self.setScriptSigOps (tx, 0, [updAndAnyoneScript]) node.sendrawtransaction (tx, 0) node.generate (1) self.checkNameWithHeight (0, name, "value2", baseHeight + 2) found = False for u in node.listunspent (): if u['address'] == addr and u['amount'] == 1: found = True break if not found: raise AssertionError ("Coin not sent to expected address") # Send the name to the P2SH address with name prefix and then spend it # again. Spending should work fine, and the name should just be updated # ordinarily; the name prefix of the redeem script should have no effect. self.updateAnyoneCanSpendName (0, name, "value3", updAndAnyoneAddr, [anyoneScript]) node.generate (1) self.checkNameWithHeight (0, name, "value3", baseHeight + 5) self.updateAnyoneCanSpendName (0, name, "value4", anyoneAddr, [updAndAnyoneScript]) node.generate (1) self.checkNameWithHeight (0, name, "value4", baseHeight + 6)
def test_namescript_p2sh(self): """ Tests how name prefixes interact with P2SH outputs and redeem scripts. """ self.log.info("Testing name prefix and P2SH interactions...") # This test only needs a single node and no syncing. node = self.nodes[0] name = "d/p2sh" value = val("value") node.name_register(name, value) node.generate(1) baseHeight = node.getblockcount() self.checkNameWithHeight(0, name, value, baseHeight) # Prepare some scripts and P2SH addresses we use later. We build the # name script prefix for an update to our testname, so that we can build # P2SH redeem scripts with (or without) it. nameBytes = codecs.encode(name, 'ascii') valueBytes = codecs.encode(value, 'ascii') updOps = [OP_NAME_UPDATE, nameBytes, valueBytes, OP_2DROP, OP_DROP] anyoneOps = [OP_TRUE] updScript = CScript(updOps) anyoneScript = CScript(anyoneOps) updAndAnyoneScript = CScript(updOps + anyoneOps) anyoneAddr = self.getP2SH(0, anyoneScript) updAndAnyoneAddr = self.getP2SH(0, updAndAnyoneScript) # Send the name to the anyone-can-spend name-update script directly. # This is expected to update the name (verifies the update script is good). tx = CTransaction() data = node.name_show(name) tx.vin.append(CTxIn(COutPoint(int(data['txid'], 16), data['vout']))) tx.vout.append(CTxOut(COIN // 100, updAndAnyoneScript)) txHex = tx.serialize().hex() txHex = node.fundrawtransaction(txHex)['hex'] signed = node.signrawtransactionwithwallet(txHex) assert signed['complete'] node.sendrawtransaction(signed['hex']) node.generate(1) self.checkNameWithHeight(0, name, value, baseHeight + 1) # Send the name to the anyone-can-spend P2SH address. This should just # work fine and update the name. self.updateAnyoneCanSpendName(0, name, val("value2"), anyoneAddr, []) node.generate(1) self.checkNameWithHeight(0, name, val("value2"), baseHeight + 2) # Send a coin to the P2SH address with name prefix. This should just # work fine but not update the name. We should be able to spend the coin # again from that address. txid = node.sendtoaddress(updAndAnyoneAddr, 2) tx = node.getrawtransaction(txid) ind = self.rawtxOutputIndex(0, tx, updAndAnyoneAddr) node.generate(1) ins = [{"txid": txid, "vout": ind}] addr = node.getnewaddress() out = {addr: 1} tx = node.createrawtransaction(ins, out) tx = self.setScriptSigOps(tx, 0, [updAndAnyoneScript]) node.sendrawtransaction(tx, 0) node.generate(1) self.checkNameWithHeight(0, name, val("value2"), baseHeight + 2) found = False for u in node.listunspent(): if u['address'] == addr and u['amount'] == 1: found = True break if not found: raise AssertionError("Coin not sent to expected address") # Send the name to the P2SH address with name prefix and then spend it # again. Spending should work fine, and the name should just be updated # ordinarily; the name prefix of the redeem script should have no effect. self.updateAnyoneCanSpendName(0, name, val("value3"), updAndAnyoneAddr, [anyoneScript]) node.generate(1) self.checkNameWithHeight(0, name, val("value3"), baseHeight + 5) self.updateAnyoneCanSpendName(0, name, val("value4"), anyoneAddr, [updAndAnyoneScript]) node.generate(1) self.checkNameWithHeight(0, name, val("value4"), baseHeight + 6)
def decoderawtransaction_asm_sighashtype(self): """Test decoding scripts via RPC command "decoderawtransaction". This test is in with the "decodescript" tests because they are testing the same "asm" script decodes. """ # this test case uses a random plain vanilla mainnet transaction with a single P2PKH input and output tx = '0100000001696a20784a2c70143f634e95227dbdfdf0ecd51647052e70854512235f5986ca010000008a47304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb014104d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536ffffffff0100e1f505000000001976a914eb6c6e0cdb2d256a32d97b8df1fc75d1920d9bca88ac00000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('304402207174775824bec6c2700023309a168231ec80b82c6069282f5133e6f11cbb04460220570edc55c7c5da2ca687ebd0372d3546ebc3f810516a002350cac72dfe192dfb[ALL] 04d3f898e6487787910a690410b7a917ef198905c27fb9d3b0a42da12aceae0544fc7088d239d9a48f2828a15a09e84043001f27cc80d162cb95404e1210161536', rpc_result['vin'][0]['scriptSig']['asm']) # this test case uses a mainnet transaction that has a P2SH input and both P2PKH and P2SH outputs. # it's from James D'Angelo's awesome introductory videos about multisig: https://www.youtube.com/watch?v=zIbUSaZBJgU and https://www.youtube.com/watch?v=OSA1pwlaypc # verify that we have not altered scriptPubKey decoding. tx = '01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914dc863734a218bfe83ef770ee9d41a27f824a6e5688acee2a02000000000017a9142a5edea39971049a540474c6a99edf0aa4074c588700000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('8e3730608c3b0bb5df54f09076e196bc292a8e39a78e73b44b6ba08c78f5cbb0', rpc_result['txid']) assert_equal('0 3045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea[ALL] 3045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75[ALL] 5221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53ae', rpc_result['vin'][0]['scriptSig']['asm']) assert_equal('OP_DUP OP_HASH160 dc863734a218bfe83ef770ee9d41a27f824a6e56 OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][0]['scriptPubKey']['asm']) assert_equal('OP_HASH160 2a5edea39971049a540474c6a99edf0aa4074c58 OP_EQUAL', rpc_result['vout'][1]['scriptPubKey']['asm']) txSave = CTransaction() txSave.deserialize(BytesIO(hex_str_to_bytes(tx))) # make sure that a specifically crafted op_return value will not pass all the IsDERSignature checks and then get decoded as a sighash type tx = '01000000015ded05872fdbda629c7d3d02b194763ce3b9b1535ea884e3c8e765d42e316724020000006b48304502204c10d4064885c42638cbff3585915b322de33762598321145ba033fc796971e2022100bb153ad3baa8b757e30a2175bd32852d2e1cb9080f84d7e32fcdfd667934ef1b012103163c0ff73511ea1743fb5b98384a2ff09dd06949488028fd819f4d83f56264efffffffff0200000000000000000b6a0930060201000201000180380100000000001976a9141cabd296e753837c086da7a45a6c2fe0d49d7b7b88ac00000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('OP_RETURN 300602010002010001', rpc_result['vout'][0]['scriptPubKey']['asm']) # verify that we have not altered scriptPubKey processing even of a specially crafted P2PKH pubkeyhash and P2SH redeem script hash that is made to pass the der signature checks tx = '01000000018d1f5635abd06e2c7e2ddf58dc85b3de111e4ad6e0ab51bb0dcf5e84126d927300000000fdfe0000483045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea01483045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75014c695221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53aeffffffff02611e0000000000001976a914301102070101010101010102060101010101010188acee2a02000000000017a91430110207010101010101010206010101010101018700000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('OP_DUP OP_HASH160 3011020701010101010101020601010101010101 OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][0]['scriptPubKey']['asm']) assert_equal('OP_HASH160 3011020701010101010101020601010101010101 OP_EQUAL', rpc_result['vout'][1]['scriptPubKey']['asm']) # verify that names shorter than 5 bytes aren't interpreted into asm as integers (issue #140) tx = '0071000002c323b5df3dbd501ef8e26683749fd13f785eac6d4f4c2083196a33262605e6ec010000008b48304502207cc397996cf41a4be7ce229226c2e3862557256e372dd325dfefaba26a4a44a20221008e71fc315563d04d7d92d6e2b0ef9c5e095df50777664819623156dcf3ec1c1e0141045bc24b6e33ecbe341e8195b4e8fcb3f8c7937ca6f671f799644968dc00e82d0160c5e14b687cba83840381dda04711939a225588eb0e2401d4f14dc9d3a5ccdaffffffff882be8eb700e1c6eaef2682839293ae9d5bfacf82cee80510d4dee6eed359c98000000004847304402206c6115eb47b4f3010a6c0e79ea5ee7f036fc1da22af172c4bd39a460758bb34402204cc40302837a3a61d9b1e12a99e73572f95da7b5005e5ce842a7b8b4ba90d7f901ffffffff02e007646300000000434104fb090d04c8f110012a9f4545b6cb1f93a6f22db1b35bef7e1545a4a0aef0124cd25c522851ba2120f12b124785adc6f09b13f8eaa294cfe54a601dc75f11df29ac40420f00000000005a5303662f6a394269746d65737361676520616464726573733a20424d2d3263554755687335436a6973526975514756574447334a514a47717a5967713134356d7576a9147368feca713f2a9d7780343b74007e26a4fcfcea88ac00000000' rpc_result = self.nodes[0].decoderawtransaction(tx) assert_equal('OP_NAME_UPDATE 662f6a 4269746d65737361676520616464726573733a20424d2d3263554755687335436a6973526975514756574447334a514a47717a596771313435 OP_2DROP OP_DROP OP_DUP OP_HASH160 7368feca713f2a9d7780343b74007e26a4fcfcea OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][1]['scriptPubKey']['asm']) # some more full transaction tests of varying specific scriptSigs. used instead of # tests in decodescript_script_sig because the decodescript RPC is specifically # for working on scriptPubKeys (argh!). push_signature = txSave.vin[0].scriptSig.hex()[2:(0x48*2+4)] signature = push_signature[2:] der_signature = signature[:-2] signature_sighash_decoded = der_signature + '[ALL]' signature_2 = der_signature + '82' push_signature_2 = '48' + signature_2 signature_2_sighash_decoded = der_signature + '[NONE|ANYONECANPAY]' # 1) P2PK scriptSig txSave.vin[0].scriptSig = hex_str_to_bytes(push_signature) rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex()) assert_equal(signature_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) # make sure that the sighash decodes come out correctly for a more complex / lesser used case. txSave.vin[0].scriptSig = hex_str_to_bytes(push_signature_2) rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex()) assert_equal(signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) # 2) multisig scriptSig txSave.vin[0].scriptSig = hex_str_to_bytes('00' + push_signature + push_signature_2) rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex()) assert_equal('0 ' + signature_sighash_decoded + ' ' + signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm']) # 3) test a scriptSig that contains more than push operations. # in fact, it contains an OP_RETURN with data specially crafted to cause improper decode if the code does not catch it. txSave.vin[0].scriptSig = hex_str_to_bytes('6a143011020701010101010101020601010101010101') rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex()) assert_equal('OP_RETURN 3011020701010101010101020601010101010101', rpc_result['vin'][0]['scriptSig']['asm'])
def test_doublespend_tree(self): """Doublespend of a big tree of transactions""" initial_nValue = 50 * COIN tx0_outpoint = make_utxo(self.nodes[0], initial_nValue) def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001 * COIN, _total_txs=None): if _total_txs is None: _total_txs = [0] if _total_txs[0] >= max_txs: return txout_value = (initial_value - fee) // tree_width if txout_value < fee: return vout = [CTxOut(txout_value, CScript([i+1])) for i in range(tree_width)] tx = CTransaction() tx.vin = [CTxIn(prevout, nSequence=0)] tx.vout = vout tx_hex = tx.serialize().hex() assert len(tx.serialize()) < 100000 txid = self.nodes[0].sendrawtransaction(tx_hex, 0) yield tx _total_txs[0] += 1 txid = int(txid, 16) for i, txout in enumerate(tx.vout): for x in branch(COutPoint(txid, i), txout_value, max_txs, tree_width=tree_width, fee=fee, _total_txs=_total_txs): yield x fee = int(0.0001 * COIN) n = MAX_REPLACEMENT_LIMIT tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee)) assert_equal(len(tree_txs), n) # Attempt double-spend, will fail because too little fee paid dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] dbl_tx.vout = [CTxOut(initial_nValue - fee * n, DUMMY_P2WPKH_SCRIPT)] dbl_tx_hex = dbl_tx.serialize().hex() # This will raise an exception due to insufficient fee assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0) # 1 BTC fee is enough dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] dbl_tx.vout = [CTxOut(initial_nValue - fee * n - 1 * COIN, DUMMY_P2WPKH_SCRIPT)] dbl_tx_hex = dbl_tx.serialize().hex() self.nodes[0].sendrawtransaction(dbl_tx_hex, 0) mempool = self.nodes[0].getrawmempool() for tx in tree_txs: tx.rehash() assert tx.hash not in mempool # Try again, but with more total transactions than the "max txs # double-spent at once" anti-DoS limit. for n in (MAX_REPLACEMENT_LIMIT + 1, MAX_REPLACEMENT_LIMIT * 2): fee = int(0.0001 * COIN) tx0_outpoint = make_utxo(self.nodes[0], initial_nValue) tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee)) assert_equal(len(tree_txs), n) dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] dbl_tx.vout = [CTxOut(initial_nValue - 2 * fee * n, DUMMY_P2WPKH_SCRIPT)] dbl_tx_hex = dbl_tx.serialize().hex() # This will raise an exception assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0) for tx in tree_txs: tx.rehash() self.nodes[0].getrawtransaction(tx.hash)
def run_test(self): if self.options.segwit: output_type = "p2sh-segwit" else: output_type = "legacy" # All nodes should start with 1,250 BTC: starting_balance = 1250 for i in range(4): assert_equal(self.nodes[i].getbalance()['bitcoin'], starting_balance) self.nodes[i].getnewaddress() # bug workaround, coins generated assigned to first getnewaddress! self.nodes[0].settxfee(.001) node0_address1 = self.nodes[0].getnewaddress(address_type=output_type) node0_txid1 = self.nodes[0].sendtoaddress(node0_address1, 1219) node0_tx1 = self.nodes[0].gettransaction(node0_txid1) node0_address2 = self.nodes[0].getnewaddress(address_type=output_type) node0_txid2 = self.nodes[0].sendtoaddress(node0_address2, 29) node0_tx2 = self.nodes[0].gettransaction(node0_txid2) assert_equal(self.nodes[0].getbalance()['bitcoin'], starting_balance + node0_tx1["fee"]['bitcoin'] + node0_tx2["fee"]['bitcoin']) # Coins are sent to node1_address node1_address = self.nodes[1].getnewaddress() # Send tx1, and another transaction tx2 that won't be cloned txid1 = self.nodes[0].sendtoaddress(node1_address, 40) txid2 = self.nodes[0].sendtoaddress(node1_address, 20) # Construct a clone of tx1, to be malleated rawtx1 = self.nodes[0].getrawtransaction(txid1, 1) clone_inputs = [{"txid": rawtx1["vin"][0]["txid"], "vout": rawtx1["vin"][0]["vout"]}] clone_outputs = {rawtx1["vout"][0]["scriptPubKey"]["addresses"][0]: rawtx1["vout"][0]["value"], rawtx1["vout"][1]["scriptPubKey"]["addresses"][0]: rawtx1["vout"][1]["value"]} assert_equal(rawtx1["vout"][2]["scriptPubKey"]["type"], "fee") clone_outputs["fee"] = rawtx1["vout"][2]["value"] clone_locktime = rawtx1["locktime"] clone_raw = self.nodes[0].createrawtransaction(clone_inputs, clone_outputs, clone_locktime) # createrawtransaction randomizes the order of its outputs, so swap them if necessary. clone_tx = CTransaction() clone_tx.deserialize(io.BytesIO(bytes.fromhex(clone_raw))) if (rawtx1["vout"][0]["value"] == 40 and clone_tx.vout[0].nValue.getAmount() != 40*COIN or rawtx1["vout"][0]["value"] != 40 and clone_tx.vout[0].nValue.getAmount() == 40*COIN): (clone_tx.vout[0], clone_tx.vout[1]) = (clone_tx.vout[1], clone_tx.vout[0]) # Use a different signature hash type to sign. This creates an equivalent but malleated clone. # Don't send the clone anywhere yet tx1_clone = self.nodes[0].signrawtransactionwithwallet(clone_tx.serialize().hex(), None, "ALL|ANYONECANPAY") assert_equal(tx1_clone["complete"], True) # Have node0 mine a block, if requested: if (self.options.mine_block): self.nodes[0].generate(1) sync_blocks(self.nodes[0:2]) tx1 = self.nodes[0].gettransaction(txid1) tx2 = self.nodes[0].gettransaction(txid2) # Node0's balance should be starting balance, plus 50BTC for another # matured block, minus tx1 and tx2 amounts, and minus transaction fees: expected = starting_balance + node0_tx1["fee"]['bitcoin'] + node0_tx2["fee"]['bitcoin'] if self.options.mine_block: expected += 50 expected += tx1["amount"]['bitcoin'] + tx1["fee"]['bitcoin'] expected += tx2["amount"]['bitcoin'] + tx2["fee"]['bitcoin'] assert_equal(self.nodes[0].getbalance()['bitcoin'], expected) if self.options.mine_block: assert_equal(tx1["confirmations"], 1) assert_equal(tx2["confirmations"], 1) else: assert_equal(tx1["confirmations"], 0) assert_equal(tx2["confirmations"], 0) # Send clone and its parent to miner self.nodes[2].sendrawtransaction(node0_tx1["hex"]) txid1_clone = self.nodes[2].sendrawtransaction(tx1_clone["hex"]) if self.options.segwit: assert_equal(txid1, txid1_clone) return # ... mine a block... self.nodes[2].generate(1) # Reconnect the split network, and sync chain: connect_nodes(self.nodes[1], 2) self.nodes[2].sendrawtransaction(node0_tx2["hex"]) self.nodes[2].sendrawtransaction(tx2["hex"]) self.nodes[2].generate(1) # Mine another block to make sure we sync sync_blocks(self.nodes) # Re-fetch transaction info: tx1 = self.nodes[0].gettransaction(txid1) tx1_clone = self.nodes[0].gettransaction(txid1_clone) tx2 = self.nodes[0].gettransaction(txid2) # Verify expected confirmations assert_equal(tx1["confirmations"], -2) assert_equal(tx1_clone["confirmations"], 2) assert_equal(tx2["confirmations"], 1) # Check node0's total balance; should be same as before the clone, + 100 BTC for 2 matured, # less possible orphaned matured subsidy expected += 100 if (self.options.mine_block): expected -= 50 assert_equal(self.nodes[0].getbalance()['bitcoin'], expected)
def test_opt_in(self): """Replacing should only work if orig tx opted in""" tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN)) # Create a non-opting in transaction tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0xffffffff)] tx1a.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)] tx1a_hex = tx1a.serialize().hex() tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0) # This transaction isn't shown as replaceable assert_equal(self.nodes[0].getmempoolentry(tx1a_txid)['bip125-replaceable'], False) # Shouldn't be able to double-spend tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] tx1b.vout = [CTxOut(int(0.9 * COIN), DUMMY_P2WPKH_SCRIPT)] tx1b_hex = tx1b.serialize().hex() # This will raise an exception assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, 0) tx1_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN)) # Create a different non-opting in transaction tx2a = CTransaction() tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0xfffffffe)] tx2a.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)] tx2a_hex = tx2a.serialize().hex() tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, 0) # Still shouldn't be able to double-spend tx2b = CTransaction() tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] tx2b.vout = [CTxOut(int(0.9 * COIN), DUMMY_P2WPKH_SCRIPT)] tx2b_hex = tx2b.serialize().hex() # This will raise an exception assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, 0) # Now create a new transaction that spends from tx1a and tx2a # opt-in on one of the inputs # Transaction should be replaceable on either input tx1a_txid = int(tx1a_txid, 16) tx2a_txid = int(tx2a_txid, 16) tx3a = CTransaction() tx3a.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0xffffffff), CTxIn(COutPoint(tx2a_txid, 0), nSequence=0xfffffffd)] tx3a.vout = [CTxOut(int(0.9 * COIN), CScript([b'c'])), CTxOut(int(0.9 * COIN), CScript([b'd']))] tx3a_hex = tx3a.serialize().hex() tx3a_txid = self.nodes[0].sendrawtransaction(tx3a_hex, 0) # This transaction is shown as replaceable assert_equal(self.nodes[0].getmempoolentry(tx3a_txid)['bip125-replaceable'], True) tx3b = CTransaction() tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] tx3b.vout = [CTxOut(int(0.5 * COIN), DUMMY_P2WPKH_SCRIPT)] tx3b_hex = tx3b.serialize().hex() tx3c = CTransaction() tx3c.vin = [CTxIn(COutPoint(tx2a_txid, 0), nSequence=0)] tx3c.vout = [CTxOut(int(0.5 * COIN), DUMMY_P2WPKH_SCRIPT)] tx3c_hex = tx3c.serialize().hex() self.nodes[0].sendrawtransaction(tx3b_hex, 0) # If tx3b was accepted, tx3c won't look like a replacement, # but make sure it is accepted anyway self.nodes[0].sendrawtransaction(tx3c_hex, 0)
def run_test(self): print("Testing wallet secret recovery") self.test_wallet_recovery() print("Test blech32 python roundtrip") # blech/bech are aliased, both are blech32 for addrtype in ["bech32", "blech32"]: addr_to_rt = self.nodes[0].getnewaddress("", addrtype) hrp = addr_to_rt[:2] assert_equal(hrp, "el") (witver, witprog) = decode(hrp, addr_to_rt) assert_equal(encode(hrp, witver, witprog), addr_to_rt) # Test that "blech32" gives a blinded segwit address. blech32_addr = self.nodes[0].getnewaddress("", "blech32") blech32_addr_info = self.nodes[0].getaddressinfo(blech32_addr) assert_equal(blech32_addr_info["iswitness"], True) assert_equal(blech32_addr_info["confidential"], blech32_addr) print("General Confidential tests") # Running balances node0 = self.nodes[0].getbalance()["bitcoin"] assert_equal(node0, 21000000) # just making sure initialfreecoins is working node1 = 0 node2 = 0 self.nodes[0].generate(101) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), node0, "", "", True) self.nodes[0].generate(101) self.sync_all() assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance("*", 1, False, False, "bitcoin"), node2) # Send 3 BTC from 0 to a new unconfidential address of 2 with # the sendtoaddress call address = self.nodes[2].getnewaddress() unconfidential_address = self.nodes[2].validateaddress( address)["unconfidential"] value0 = 3 self.nodes[0].sendtoaddress(unconfidential_address, value0) self.nodes[0].generate(101) self.sync_all() node0 = node0 - value0 node2 = node2 + value0 assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance()["bitcoin"], node2) # Send 5 BTC from 0 to a new address of 2 with the sendtoaddress call address2 = self.nodes[2].getnewaddress() unconfidential_address2 = self.nodes[2].validateaddress( address2)["unconfidential"] value1 = 5 confidential_tx_id = self.nodes[0].sendtoaddress(address2, value1) self.nodes[0].generate(101) self.sync_all() node0 = node0 - value1 node2 = node2 + value1 assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance()["bitcoin"], node2) # Send 7 BTC from 0 to the unconfidential address of 2 and 11 BTC to the # confidential address using the raw transaction interface change_address = self.nodes[0].getnewaddress() value2 = 7 value3 = 11 value23 = value2 + value3 unspent = self.nodes[0].listunspent(1, 9999999, [], True, {"asset": "bitcoin"}) unspent = [i for i in unspent if i['amount'] > value23] assert_equal(len(unspent), 1) fee = Decimal('0.0001') tx = self.nodes[0].createrawtransaction( [{ "txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "nValue": unspent[0]["amount"] }], { unconfidential_address: value2, address2: value3, change_address: unspent[0]["amount"] - value2 - value3 - fee, "fee": fee }) tx = self.nodes[0].blindrawtransaction(tx) tx_signed = self.nodes[0].signrawtransactionwithwallet(tx) raw_tx_id = self.nodes[0].sendrawtransaction(tx_signed['hex']) self.nodes[0].generate(101) self.sync_all() node0 -= (value2 + value3) node2 += value2 + value3 assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance()["bitcoin"], node2) # Check 2's listreceivedbyaddress received_by_address = self.nodes[2].listreceivedbyaddress( 0, False, False, "", "bitcoin") validate_by_address = [(address2, value1 + value3), (address, value0 + value2)] assert_equal( sorted([(ele['address'], ele['amount']) for ele in received_by_address], key=lambda t: t[0]), sorted(validate_by_address, key=lambda t: t[0])) received_by_address = self.nodes[2].listreceivedbyaddress( 0, False, False, "") validate_by_address = [(address2, { "bitcoin": value1 + value3 }), (address, { "bitcoin": value0 + value2 })] assert_equal( sorted([(ele['address'], ele['amount']) for ele in received_by_address], key=lambda t: t[0]), sorted(validate_by_address, key=lambda t: t[0])) # Give an auditor (node 1) a blinding key to allow her to look at # transaction values self.nodes[1].importaddress(address2) received_by_address = self.nodes[1].listreceivedbyaddress( 1, False, True) #Node sees nothing unless it understands the values assert_equal(len(received_by_address), 0) assert_equal( len(self.nodes[1].listunspent(1, 9999999, [], True, {"asset": "bitcoin"})), 0) # Import the blinding key blindingkey = self.nodes[2].dumpblindingkey(address2) self.nodes[1].importblindingkey(address2, blindingkey) # Check the auditor's gettransaction and listreceivedbyaddress # Needs rescan to update wallet txns conf_tx = self.nodes[1].gettransaction(confidential_tx_id, True) assert_equal(conf_tx['amount']["bitcoin"], value1) # Make sure wallet can now deblind part of transaction deblinded_tx = self.nodes[1].unblindrawtransaction( conf_tx['hex'])['hex'] for output in self.nodes[1].decoderawtransaction(deblinded_tx)["vout"]: if "value" in output and output["scriptPubKey"]["type"] != "fee": assert_equal( output["scriptPubKey"]["addresses"][0], self.nodes[1].validateaddress(address2)['unconfidential']) found_unblinded = True assert found_unblinded assert_equal( self.nodes[1].gettransaction(raw_tx_id, True)['amount']["bitcoin"], value3) assert_equal( self.nodes[1].gettransaction(raw_tx_id, True, False, "bitcoin")['amount'], value3) list_unspent = self.nodes[1].listunspent(1, 9999999, [], True, {"asset": "bitcoin"}) assert_equal(list_unspent[0]['amount'] + list_unspent[1]['amount'], value1 + value3) received_by_address = self.nodes[1].listreceivedbyaddress( 1, False, True) assert_equal(len(received_by_address), 1) assert_equal((received_by_address[0]['address'], received_by_address[0]['amount']['bitcoin']), (unconfidential_address2, value1 + value3)) # Spending a single confidential output and sending it to a # unconfidential output is not possible with CT. Test the # correct behavior of blindrawtransaction. unspent = self.nodes[0].listunspent(1, 9999999, [], True, {"asset": "bitcoin"}) unspent = [i for i in unspent if i['amount'] > value23] assert_equal(len(unspent), 1) tx = self.nodes[0].createrawtransaction( [{ "txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "nValue": unspent[0]["amount"] }], { unconfidential_address: unspent[0]["amount"] - fee, "fee": fee }) # Test that blindrawtransaction adds an OP_RETURN output to balance blinders temptx = self.nodes[0].blindrawtransaction(tx) decodedtx = self.nodes[0].decoderawtransaction(temptx) assert_equal(decodedtx["vout"][-1]["scriptPubKey"]["asm"], "OP_RETURN") assert_equal(len(decodedtx["vout"]), 3) # Create same transaction but with a change/dummy output. # It should pass the blinding step. value4 = 17 change_address = self.nodes[0].getrawchangeaddress() tx = self.nodes[0].createrawtransaction( [{ "txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "nValue": unspent[0]["amount"] }], { unconfidential_address: value4, change_address: unspent[0]["amount"] - value4 - fee, "fee": fee }) tx = self.nodes[0].blindrawtransaction(tx) tx_signed = self.nodes[0].signrawtransactionwithwallet(tx) txid = self.nodes[0].sendrawtransaction(tx_signed['hex']) decodedtx = self.nodes[0].decoderawtransaction(tx_signed["hex"]) self.nodes[0].generate(101) self.sync_all() unblindfound = False for i in range(len(decodedtx["vout"])): txout = self.nodes[0].gettxout(txid, i) if txout is not None and "asset" in txout: unblindfound = True if unblindfound == False: raise Exception( "No unconfidential output detected when one should exist") node0 -= value4 node2 += value4 assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance()["bitcoin"], node2) # Testing wallet's ability to deblind its own outputs addr = self.nodes[0].getnewaddress() addr2 = self.nodes[0].getnewaddress() # We add two to-blind outputs, fundraw adds an already-blinded change output # If we only add one, the newly blinded will be 0-blinded because input = -output raw = self.nodes[0].createrawtransaction([], { addr: Decimal('1.1'), addr2: 1 }) funded = self.nodes[0].fundrawtransaction(raw) # fund again to make sure no blinded outputs were created (would fail) funded = self.nodes[0].fundrawtransaction(funded["hex"]) blinded = self.nodes[0].blindrawtransaction(funded["hex"]) # blind again to make sure we know output blinders blinded2 = self.nodes[0].blindrawtransaction(blinded) # then sign and send signed = self.nodes[0].signrawtransactionwithwallet(blinded2) self.nodes[0].sendrawtransaction(signed["hex"]) # Aside: Check all outputs after fundraw are properly marked for blinding fund_decode = self.nodes[0].decoderawtransaction(funded["hex"]) for output in fund_decode["vout"][:-1]: assert "asset" in output assert "value" in output assert output["scriptPubKey"]["type"] != "fee" assert output["commitmentnonce_fully_valid"] assert fund_decode["vout"][-1]["scriptPubKey"]["type"] == "fee" assert not fund_decode["vout"][-1]["commitmentnonce_fully_valid"] # Also check that all fundraw outputs marked for blinding are blinded later for blind_tx in [blinded, blinded2]: blind_decode = self.nodes[0].decoderawtransaction(blind_tx) for output in blind_decode["vout"][:-1]: assert "asset" not in output assert "value" not in output assert output["scriptPubKey"]["type"] != "fee" assert output["commitmentnonce_fully_valid"] assert blind_decode["vout"][-1]["scriptPubKey"]["type"] == "fee" assert "asset" in blind_decode["vout"][-1] assert "value" in blind_decode["vout"][-1] assert not blind_decode["vout"][-1]["commitmentnonce_fully_valid"] # Check createblindedaddress functionality blinded_addr = self.nodes[0].getnewaddress() validated_addr = self.nodes[0].validateaddress(blinded_addr) blinding_pubkey = self.nodes[0].validateaddress( blinded_addr)["confidential_key"] blinding_key = self.nodes[0].dumpblindingkey(blinded_addr) assert_equal( blinded_addr, self.nodes[1].createblindedaddress( validated_addr["unconfidential"], blinding_pubkey)) # If a blinding key is over-ridden by a newly imported one, funds may be unaccounted for new_addr = self.nodes[0].getnewaddress() new_validated = self.nodes[0].validateaddress(new_addr) self.nodes[2].sendtoaddress(new_addr, 1) self.sync_all() diff_blind = self.nodes[1].createblindedaddress( new_validated["unconfidential"], blinding_pubkey) assert_equal( len(self.nodes[0].listunspent(0, 0, [new_validated["unconfidential"]])), 1) self.nodes[0].importblindingkey(diff_blind, blinding_key) # CT values for this wallet transaction have been cached via importblindingkey # therefore result will be same even though we change blinding keys assert_equal( len(self.nodes[0].listunspent(0, 0, [new_validated["unconfidential"]])), 1) # Confidential Assets Tests print("Assets tests...") # Bitcoin is the first issuance assert_equal(self.nodes[0].listissuances()[0]["assetlabel"], "bitcoin") assert_equal(len(self.nodes[0].listissuances()), 1) # Unblinded issuance of asset issued = self.nodes[0].issueasset(1, 1, False) self.nodes[0].reissueasset(issued["asset"], 1) # Compare resulting fields with getrawtransaction raw_details = self.nodes[0].getrawtransaction(issued["txid"], 1) assert_equal( issued["entropy"], raw_details["vin"][issued["vin"]]["issuance"]["assetEntropy"]) assert_equal(issued["asset"], raw_details["vin"][issued["vin"]]["issuance"]["asset"]) assert_equal(issued["token"], raw_details["vin"][issued["vin"]]["issuance"]["token"]) self.nodes[0].generate(1) self.sync_all() issued2 = self.nodes[0].issueasset(2, 1) test_asset = issued2["asset"] assert_equal(self.nodes[0].getwalletinfo()['balance'][test_asset], Decimal(2)) assert test_asset not in self.nodes[1].getwalletinfo()['balance'] # Assets balance checking, note that accounts are completely ignored because # balance queries with accounts are horrifically broken upstream assert_equal(self.nodes[0].getbalance("*", 0, False, False, "bitcoin"), self.nodes[0].getbalance("*", 0, False, False, "bitcoin")) assert_equal(self.nodes[0].getbalance("*", 0, False, False)["bitcoin"], self.nodes[0].getbalance("*", 0, False, False, "bitcoin")) assert_equal(self.nodes[0].getwalletinfo()['balance']['bitcoin'], self.nodes[0].getbalance("*", 0, False, False, "bitcoin")) # Send some bitcoin and other assets over as well to fund wallet addr = self.nodes[2].getnewaddress() txid = self.nodes[0].sendtoaddress(addr, 5) # Make sure we're doing 52 bits of hiding which covers 21M BTC worth assert_equal( self.nodes[0].getrawtransaction(txid, 1)["vout"][0]["ct-bits"], 52) self.nodes[0].sendmany("", { addr: 1, self.nodes[2].getnewaddress(): 13 }, 0, "", [], False, 1, "UNSET", {addr: test_asset}) self.sync_all() # Should have exactly 1 in change(trusted, though not confirmed) after sending one off assert_equal( self.nodes[0].getbalance("*", 0, False, False, test_asset), 1) assert_equal(self.nodes[2].getunconfirmedbalance()[test_asset], Decimal(1)) b_utxos = self.nodes[2].listunspent(0, 0, [], True, {"asset": "bitcoin"}) t_utxos = self.nodes[2].listunspent(0, 0, [], True, {"asset": test_asset}) assert_equal(len(self.nodes[2].listunspent(0, 0, [])), len(b_utxos) + len(t_utxos)) # Now craft a blinded transaction via raw api rawaddrs = [] for i in range(2): rawaddrs.append(self.nodes[1].getnewaddress()) raw_assets = self.nodes[2].createrawtransaction( [{ "txid": b_utxos[0]['txid'], "vout": b_utxos[0]['vout'], "nValue": b_utxos[0]['amount'] }, { "txid": b_utxos[1]['txid'], "vout": b_utxos[1]['vout'], "nValue": b_utxos[1]['amount'], "asset": b_utxos[1]['asset'] }, { "txid": t_utxos[0]['txid'], "vout": t_utxos[0]['vout'], "nValue": t_utxos[0]['amount'], "asset": t_utxos[0]['asset'] }], { rawaddrs[1]: Decimal(t_utxos[0]['amount']), rawaddrs[0]: Decimal(b_utxos[0]['amount'] + b_utxos[1]['amount'] - Decimal("0.01")), "fee": Decimal("0.01") }, 0, False, { rawaddrs[0]: b_utxos[0]['asset'], rawaddrs[1]: t_utxos[0]['asset'], "fee": b_utxos[0]['asset'] }) # Sign unblinded, then blinded signed_assets = self.nodes[2].signrawtransactionwithwallet(raw_assets) blind_assets = self.nodes[2].blindrawtransaction(raw_assets) signed_assets = self.nodes[2].signrawtransactionwithwallet( blind_assets) # And finally send self.nodes[2].sendrawtransaction(signed_assets['hex']) self.nodes[2].generate(101) self.sync_all() issuancedata = self.nodes[2].issueasset( 0, Decimal('0.00000006')) #0 of asset, 6 reissuance token # Node 2 will send node 1 a reissuance token, both will generate assets self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), Decimal('0.00000001'), "", "", False, False, 1, "UNSET", False, issuancedata["token"]) # node 1 needs to know about a (re)issuance to reissue itself self.nodes[1].importaddress(self.nodes[2].gettransaction( issuancedata["txid"])["details"][0]["address"]) # also send some bitcoin self.nodes[2].generate(1) self.sync_all() self.nodes[1].reissueasset(issuancedata["asset"], Decimal('0.05')) self.nodes[2].reissueasset(issuancedata["asset"], Decimal('0.025')) self.nodes[1].generate(1) self.sync_all() # Check for value accounting when asset issuance is null but token not, ie unblinded # HACK: Self-send to sweep up bitcoin inputs into blinded output. # We were hitting https://github.com/ElementsProject/elements/issues/473 for the following issuance self.nodes[0].sendtoaddress( self.nodes[0].getnewaddress(), self.nodes[0].getwalletinfo()["balance"]["bitcoin"], "", "", True) issued = self.nodes[0].issueasset(0, 1, False) walletinfo = self.nodes[0].getwalletinfo() assert issued["asset"] not in walletinfo["balance"] assert_equal(walletinfo["balance"][issued["token"]], Decimal(1)) assert issued["asset"] not in walletinfo["unconfirmed_balance"] assert issued["token"] not in walletinfo["unconfirmed_balance"] # Check for value when receiving different assets by same address. self.nodes[0].sendtoaddress(unconfidential_address2, Decimal('0.00000001'), "", "", False, False, 1, "UNSET", False, test_asset) self.nodes[0].sendtoaddress(unconfidential_address2, Decimal('0.00000002'), "", "", False, False, 1, "UNSET", False, test_asset) self.nodes[0].generate(1) self.sync_all() received_by_address = self.nodes[1].listreceivedbyaddress( 0, False, True) multi_asset_amount = [ x for x in received_by_address if x['address'] == unconfidential_address2 ][0]['amount'] assert_equal(multi_asset_amount['bitcoin'], value1 + value3) assert_equal(multi_asset_amount[test_asset], Decimal('0.00000003')) # Check blinded multisig functionality and partial blinding functionality # Get two pubkeys blinded_addr = self.nodes[0].getnewaddress() pubkey = self.nodes[0].getaddressinfo(blinded_addr)["pubkey"] blinded_addr2 = self.nodes[1].getnewaddress() pubkey2 = self.nodes[1].getaddressinfo(blinded_addr2)["pubkey"] pubkeys = [pubkey, pubkey2] # Add multisig address unconfidential_addr = self.nodes[0].addmultisigaddress( 2, pubkeys)["address"] self.nodes[1].addmultisigaddress(2, pubkeys) self.nodes[0].importaddress(unconfidential_addr) self.nodes[1].importaddress(unconfidential_addr) # Use blinding key from node 0's original getnewaddress call blinding_pubkey = self.nodes[0].getaddressinfo( blinded_addr)["confidential_key"] blinding_key = self.nodes[0].dumpblindingkey(blinded_addr) # Create blinded address from p2sh address and import corresponding privkey blinded_multisig_addr = self.nodes[0].createblindedaddress( unconfidential_addr, blinding_pubkey) self.nodes[0].importblindingkey(blinded_multisig_addr, blinding_key) # Issue new asset, to use different assets in one transaction when doing # partial blinding. Just to make these tests a bit more elaborate :-) issued3 = self.nodes[2].issueasset(1, 0) self.nodes[2].generate(1) self.sync_all() node2_balance = self.nodes[2].getbalance() assert issued3['asset'] in node2_balance assert_equal(node2_balance[issued3['asset']], Decimal(1)) # Send asset to blinded multisig address and check that it was received self.nodes[2].sendtoaddress(address=blinded_multisig_addr, amount=1, assetlabel=issued3['asset']) self.sync_all() # We will use this multisig UTXO in our partially-blinded transaction, # and will also check that multisig UTXO can be successfully spent # after the transaction is signed by node1 and node0 in succession. unspent_asset = self.nodes[0].listunspent(0, 0, [unconfidential_addr], True, {"asset": issued3['asset']}) assert_equal(len(unspent_asset), 1) assert issued3['asset'] not in self.nodes[2].getbalance() # Create new UTXO on node0 to be used in our partially-blinded transaction blinded_addr = self.nodes[0].getnewaddress() addr = self.nodes[0].validateaddress(blinded_addr)["unconfidential"] self.nodes[0].sendtoaddress(blinded_addr, 0.1) unspent = self.nodes[0].listunspent(0, 0, [addr]) assert_equal(len(unspent), 1) # Create new UTXO on node1 to be used in our partially-blinded transaction blinded_addr2 = self.nodes[1].getnewaddress() addr2 = self.nodes[1].validateaddress(blinded_addr2)["unconfidential"] self.nodes[1].sendtoaddress(blinded_addr2, 0.11) unspent2 = self.nodes[1].listunspent(0, 0, [addr2]) assert_equal(len(unspent2), 1) # The transaction will have three non-fee outputs dst_addr = self.nodes[0].getnewaddress() dst_addr2 = self.nodes[1].getnewaddress() dst_addr3 = self.nodes[2].getnewaddress() # Inputs are selected up front inputs = [{ "txid": unspent2[0]["txid"], "vout": unspent2[0]["vout"] }, { "txid": unspent[0]["txid"], "vout": unspent[0]["vout"] }, { "txid": unspent_asset[0]["txid"], "vout": unspent_asset[0]["vout"] }] # Create one part of the transaction to partially blind rawtx = self.nodes[0].createrawtransaction( inputs[:1], {dst_addr2: Decimal("0.01")}) # Create another part of the transaction to partially blind rawtx2 = self.nodes[0].createrawtransaction( inputs[1:], { dst_addr: Decimal("0.1"), dst_addr3: Decimal("1.0") }, 0, False, { dst_addr: unspent[0]['asset'], dst_addr3: unspent_asset[0]['asset'] }) sum_i = unspent2[0]["amount"] + unspent[0]["amount"] sum_o = 0.01 + 0.10 + 0.1 assert_equal(int(round(sum_i * COIN)), int(round(sum_o * COIN))) # Blind the first part of the transaction - we need to supply the # assetcommmitments for all of the inputs, for the surjectionproof # to be valid after we combine the transactions blindtx = self.nodes[1].blindrawtransaction(rawtx, True, [ unspent2[0]['assetcommitment'], unspent[0]['assetcommitment'], unspent_asset[0]['assetcommitment'] ]) # Combine the transactions # Blinded, but incomplete transaction. # 1 inputs and 1 output, but no fee output, and # it was blinded with 3 asset commitments, that means # the final transaction should have 3 inputs. btx = CTransaction() btx.deserialize(io.BytesIO(hex_str_to_bytes(blindtx))) # Unblinded transaction, with 2 inputs and 2 outputs. # We will add them to the other transaction to make it complete. ubtx = CTransaction() ubtx.deserialize(io.BytesIO(hex_str_to_bytes(rawtx2))) # We will add outputs of unblinded transaction # on top of inputs and outputs of the blinded, but incomplete transaction. # We also append empty witness instances to make witness arrays match # vin/vout arrays btx.vin.append(ubtx.vin[0]) btx.wit.vtxinwit.append(CTxInWitness()) btx.vout.append(ubtx.vout[0]) btx.wit.vtxoutwit.append(CTxOutWitness()) btx.vin.append(ubtx.vin[1]) btx.wit.vtxinwit.append(CTxInWitness()) btx.vout.append(ubtx.vout[1]) btx.wit.vtxoutwit.append(CTxOutWitness()) # Add explicit fee output btx.vout.append( CTxOut(nValue=CTxOutValue(10000000), nAsset=CTxOutAsset(BITCOIN_ASSET_OUT))) btx.wit.vtxoutwit.append(CTxOutWitness()) # Input 0 is bitcoin asset (already blinded) # Input 1 is also bitcoin asset # Input 2 is our new asset # Blind with wrong order of assetcommitments - such transaction should be rejected blindtx = self.nodes[0].blindrawtransaction( btx.serialize().hex(), True, [ unspent_asset[0]['assetcommitment'], unspent[0]['assetcommitment'], unspent2[0]['assetcommitment'] ]) stx2 = self.nodes[1].signrawtransactionwithwallet(blindtx) stx = self.nodes[0].signrawtransactionwithwallet(stx2['hex']) self.sync_all() assert_raises_rpc_error(-26, "bad-txns-in-ne-out", self.nodes[2].sendrawtransaction, stx['hex']) # Blind with correct order of assetcommitments blindtx = self.nodes[0].blindrawtransaction( btx.serialize().hex(), True, [ unspent2[0]['assetcommitment'], unspent[0]['assetcommitment'], unspent_asset[0]['assetcommitment'] ]) stx2 = self.nodes[1].signrawtransactionwithwallet(blindtx) stx = self.nodes[0].signrawtransactionwithwallet(stx2['hex']) txid = self.nodes[2].sendrawtransaction(stx['hex']) self.nodes[2].generate(1) assert self.nodes[2].gettransaction(txid)['confirmations'] == 1 self.sync_all() # Check that the sent asset has reached its destination unconfidential_dst_addr3 = self.nodes[2].validateaddress( dst_addr3)["unconfidential"] unspent_asset2 = self.nodes[2].listunspent(1, 1, [unconfidential_dst_addr3], True, {"asset": issued3['asset']}) assert_equal(len(unspent_asset2), 1) assert_equal(unspent_asset2[0]['amount'], Decimal(1)) # And that the balance was correctly updated assert_equal(self.nodes[2].getbalance()[issued3['asset']], Decimal(1)) # Basic checks of rawblindrawtransaction functionality blinded_addr = self.nodes[0].getnewaddress() addr = self.nodes[0].validateaddress(blinded_addr)["unconfidential"] self.nodes[0].sendtoaddress(blinded_addr, 1) self.nodes[0].sendtoaddress(blinded_addr, 3) unspent = self.nodes[0].listunspent(0, 0) rawtx = self.nodes[0].createrawtransaction( [{ "txid": unspent[0]["txid"], "vout": unspent[0]["vout"] }, { "txid": unspent[1]["txid"], "vout": unspent[1]["vout"] }], { addr: unspent[0]["amount"] + unspent[1]["amount"] - Decimal("0.2"), "fee": Decimal("0.2") }) # Blinding will fail with 2 blinded inputs and 0 blinded outputs # since it has no notion of a wallet to fill in a 0-value OP_RETURN output try: self.nodes[0].rawblindrawtransaction( rawtx, [unspent[0]["amountblinder"], unspent[1]["amountblinder"]], [unspent[0]["amount"], unspent[1]["amount"]], [unspent[0]["asset"], unspent[1]["asset"]], [unspent[0]["assetblinder"], unspent[1]["assetblinder"]]) raise AssertionError( "Shouldn't be able to blind 2 input 0 output transaction via rawblindraw" ) except JSONRPCException: pass # Blinded destination added, can blind, sign and send rawtx = self.nodes[0].createrawtransaction( [{ "txid": unspent[0]["txid"], "vout": unspent[0]["vout"] }, { "txid": unspent[1]["txid"], "vout": unspent[1]["vout"] }], { blinded_addr: unspent[0]["amount"] + unspent[1]["amount"] - Decimal("0.002"), "fee": Decimal("0.002") }) signtx = self.nodes[0].signrawtransactionwithwallet(rawtx) try: self.nodes[0].sendrawtransaction(signtx["hex"]) raise AssertionError( "Shouldn't be able to send unblinded tx with emplaced pubkey in output without additional argument" ) except JSONRPCException: pass # Make sure RPC throws when an invalid blinding factor is provided. bad_blinder = 'FF' * 32 assert_raises_rpc_error( -8, "Unable to blind transaction: Are you sure each asset type to blind is represented in the inputs?", self.nodes[0].rawblindrawtransaction, rawtx, [unspent[0]["amountblinder"], bad_blinder], [unspent[0]["amount"], unspent[1]["amount"]], [unspent[0]["asset"], unspent[1]["asset"]], [unspent[0]["assetblinder"], unspent[1]["assetblinder"]]) assert_raises_rpc_error( -8, "Unable to blind transaction: Are you sure each asset type to blind is represented in the inputs?", self.nodes[0].rawblindrawtransaction, rawtx, [unspent[0]["amountblinder"], unspent[1]["amountblinder"]], [unspent[0]["amount"], unspent[1]["amount"]], [unspent[0]["asset"], unspent[1]["asset"]], [unspent[0]["assetblinder"], bad_blinder]) blindtx = self.nodes[0].rawblindrawtransaction( rawtx, [unspent[0]["amountblinder"], unspent[1]["amountblinder"]], [unspent[0]["amount"], unspent[1]["amount"]], [unspent[0]["asset"], unspent[1]["asset"]], [unspent[0]["assetblinder"], unspent[1]["assetblinder"]]) signtx = self.nodes[0].signrawtransactionwithwallet(blindtx) txid = self.nodes[0].sendrawtransaction(signtx["hex"]) for output in self.nodes[0].decoderawtransaction(blindtx)["vout"]: if "asset" in output and output["scriptPubKey"]["type"] != "fee": raise AssertionError("An unblinded output exists") # Test fundrawtransaction with multiple assets issue = self.nodes[0].issueasset(1, 0) assetaddr = self.nodes[0].getnewaddress() rawtx = self.nodes[0].createrawtransaction( [], { assetaddr: 1, self.nodes[0].getnewaddress(): 2 }, 0, False, {assetaddr: issue["asset"]}) funded = self.nodes[0].fundrawtransaction(rawtx) blinded = self.nodes[0].blindrawtransaction(funded["hex"]) signed = self.nodes[0].signrawtransactionwithwallet(blinded) txid = self.nodes[0].sendrawtransaction(signed["hex"]) # Test fundrawtransaction with multiple inputs, creating > vout.size change rawtx = self.nodes[0].createrawtransaction( [{ "txid": txid, "vout": 0 }, { "txid": txid, "vout": 1 }], {self.nodes[0].getnewaddress(): 5}) funded = self.nodes[0].fundrawtransaction(rawtx) blinded = self.nodes[0].blindrawtransaction(funded["hex"]) signed = self.nodes[0].signrawtransactionwithwallet(blinded) txid = self.nodes[0].sendrawtransaction(signed["hex"]) # Test corner case where wallet appends a OP_RETURN output, yet doesn't blind it # due to the fact that the output value is 0-value and input pedersen commitments # self-balance. This is rare corner case, but ok. unblinded = self.nodes[0].validateaddress( self.nodes[0].getnewaddress())["unconfidential"] self.nodes[0].sendtoaddress(unblinded, self.nodes[0].getbalance()["bitcoin"], "", "", True) # Make tx with blinded destination and change outputs only self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), self.nodes[0].getbalance()["bitcoin"] / 2) # Send back again, this transaction should have 3 outputs, all unblinded txid = self.nodes[0].sendtoaddress( unblinded, self.nodes[0].getbalance()["bitcoin"], "", "", True) outputs = self.nodes[0].getrawtransaction(txid, 1)["vout"] assert_equal(len(outputs), 3) assert "value" in outputs[0] and "value" in outputs[ 1] and "value" in outputs[2] assert_equal(outputs[2]["scriptPubKey"]["type"], 'nulldata') # Test burn argument in createrawtransaction raw_burn1 = self.nodes[0].createrawtransaction( [], { self.nodes[0].getnewaddress(): 1, "burn": 2 }) decode_burn1 = self.nodes[0].decoderawtransaction(raw_burn1) assert_equal(len(decode_burn1["vout"]), 2) found_pay = False found_burn = False for output in decode_burn1["vout"]: if output["scriptPubKey"]["asm"] == "OP_RETURN": found_burn = True if output["asset"] != self.nodes[0].dumpassetlabels( )["bitcoin"]: raise Exception( "Burn should have been bitcoin(policyAsset)") if output["scriptPubKey"]["type"] == "witness_v0_keyhash": found_pay = True assert found_pay and found_burn raw_burn2 = self.nodes[0].createrawtransaction( [], { self.nodes[0].getnewaddress(): 1, "burn": 2 }, 101, False, {"burn": "deadbeef" * 8}) decode_burn2 = self.nodes[0].decoderawtransaction(raw_burn2) assert_equal(len(decode_burn2["vout"]), 2) found_pay = False found_burn = False for output in decode_burn2["vout"]: if output["scriptPubKey"]["asm"] == "OP_RETURN": found_burn = True if output["asset"] != "deadbeef" * 8: raise Exception("Burn should have been deadbeef") if output["scriptPubKey"]["type"] == "witness_v0_keyhash": found_pay = True assert found_pay and found_burn
def run_test(self): self.generate(self.nodes[0], 161) # block 161 self.log.info( "Verify sigops are counted in GBT with pre-BIP141 rules before the fork" ) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert_equal(tmpl['sizelimit'], 1000000) assert 'weightlimit' not in tmpl assert_equal(tmpl['sigoplimit'], 20000) assert_equal(tmpl['transactions'][0]['hash'], txid) assert_equal(tmpl['transactions'][0]['sigops'], 2) assert '!segwit' not in tmpl['rules'] self.generate(self.nodes[0], 1) # block 162 balance_presetup = self.nodes[0].getbalance() self.pubkey = [] p2sh_ids = [ ] # p2sh_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE embedded in p2sh wit_ids = [ ] # wit_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE via bare witness for i in range(3): key = get_generate_key() self.pubkey.append(key.pubkey) multiscript = keys_to_multisig_script([self.pubkey[-1]]) p2sh_ms_addr = self.nodes[i].createmultisig( 1, [self.pubkey[-1]], 'p2sh-segwit')['address'] bip173_ms_addr = self.nodes[i].createmultisig( 1, [self.pubkey[-1]], 'bech32')['address'] assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript)) assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript)) p2sh_ms_desc = descsum_create(f"sh(wsh(multi(1,{key.privkey})))") bip173_ms_desc = descsum_create(f"wsh(multi(1,{key.privkey}))") assert_equal(self.nodes[i].deriveaddresses(p2sh_ms_desc)[0], p2sh_ms_addr) assert_equal(self.nodes[i].deriveaddresses(bip173_ms_desc)[0], bip173_ms_addr) sh_wpkh_desc = descsum_create(f"sh(wpkh({key.privkey}))") wpkh_desc = descsum_create(f"wpkh({key.privkey})") assert_equal(self.nodes[i].deriveaddresses(sh_wpkh_desc)[0], key.p2sh_p2wpkh_addr) assert_equal(self.nodes[i].deriveaddresses(wpkh_desc)[0], key.p2wpkh_addr) if self.options.descriptors: res = self.nodes[i].importdescriptors([ { "desc": p2sh_ms_desc, "timestamp": "now" }, { "desc": bip173_ms_desc, "timestamp": "now" }, { "desc": sh_wpkh_desc, "timestamp": "now" }, { "desc": wpkh_desc, "timestamp": "now" }, ]) else: # The nature of the legacy wallet is that this import results in also adding all of the necessary scripts res = self.nodes[i].importmulti([ { "desc": p2sh_ms_desc, "timestamp": "now" }, ]) assert all([r["success"] for r in res]) p2sh_ids.append([]) wit_ids.append([]) for _ in range(2): p2sh_ids[i].append([]) wit_ids[i].append([]) for _ in range(5): for n in range(3): for v in range(2): wit_ids[n][v].append( send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], False, Decimal("49.999"))) p2sh_ids[n][v].append( send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], True, Decimal("49.999"))) self.generate(self.nodes[0], 1) # block 163 # Make sure all nodes recognize the transactions as theirs assert_equal(self.nodes[0].getbalance(), balance_presetup - 60 * 50 + 20 * Decimal("49.999") + 50) assert_equal(self.nodes[1].getbalance(), 20 * Decimal("49.999")) assert_equal(self.nodes[2].getbalance(), 20 * Decimal("49.999")) self.generate(self.nodes[0], 260) # block 423 self.log.info( "Verify witness txs are skipped for mining before the fork") self.skip_mine(self.nodes[2], wit_ids[NODE_2][P2WPKH][0], True) # block 424 self.skip_mine(self.nodes[2], wit_ids[NODE_2][P2WSH][0], True) # block 425 self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][P2WPKH][0], True) # block 426 self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][P2WSH][0], True) # block 427 self.log.info( "Verify unsigned p2sh witness txs without a redeem script are invalid" ) self.fail_accept( self.nodes[2], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WPKH][1], sign=False) self.fail_accept( self.nodes[2], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WSH][1], sign=False) self.generate(self.nodes[2], 4) # blocks 428-431 self.log.info( "Verify previous witness txs skipped for mining can now be mined") assert_equal(len(self.nodes[2].getrawmempool()), 4) blockhash = self.generate( self.nodes[2], 1)[0] # block 432 (first block with new rules; 432 = 144 * 3) assert_equal(len(self.nodes[2].getrawmempool()), 0) segwit_tx_list = self.nodes[2].getblock(blockhash)["tx"] assert_equal(len(segwit_tx_list), 5) self.log.info( "Verify default node can't accept txs with missing witness") # unsigned, no scriptsig self.fail_accept( self.nodes[0], "non-mandatory-script-verify-flag (Witness program hash mismatch)", wit_ids[NODE_0][P2WPKH][0], sign=False) self.fail_accept( self.nodes[0], "non-mandatory-script-verify-flag (Witness program was passed an empty witness)", wit_ids[NODE_0][P2WSH][0], sign=False) self.fail_accept( self.nodes[0], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WPKH][0], sign=False) self.fail_accept( self.nodes[0], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WSH][0], sign=False) # unsigned with redeem script self.fail_accept( self.nodes[0], "non-mandatory-script-verify-flag (Witness program hash mismatch)", p2sh_ids[NODE_0][P2WPKH][0], sign=False, redeem_script=witness_script(False, self.pubkey[0])) self.fail_accept( self.nodes[0], "non-mandatory-script-verify-flag (Witness program was passed an empty witness)", p2sh_ids[NODE_0][P2WSH][0], sign=False, redeem_script=witness_script(True, self.pubkey[0])) self.log.info( "Verify block and transaction serialization rpcs return differing serializations depending on rpc serialization flag" ) assert self.nodes[2].getblock( blockhash, False) != self.nodes[0].getblock(blockhash, False) assert self.nodes[1].getblock(blockhash, False) == self.nodes[2].getblock( blockhash, False) for tx_id in segwit_tx_list: tx = tx_from_hex(self.nodes[2].gettransaction(tx_id)["hex"]) assert self.nodes[2].getrawtransaction( tx_id, False, blockhash) != self.nodes[0].getrawtransaction( tx_id, False, blockhash) assert self.nodes[1].getrawtransaction( tx_id, False, blockhash) == self.nodes[2].getrawtransaction( tx_id, False, blockhash) assert self.nodes[0].getrawtransaction( tx_id, False, blockhash) != self.nodes[2].gettransaction(tx_id)["hex"] assert self.nodes[1].getrawtransaction( tx_id, False, blockhash) == self.nodes[2].gettransaction(tx_id)["hex"] assert self.nodes[0].getrawtransaction( tx_id, False, blockhash) == tx.serialize_without_witness().hex() # Coinbase contains the witness commitment nonce, check that RPC shows us coinbase_txid = self.nodes[2].getblock(blockhash)['tx'][0] coinbase_tx = self.nodes[2].gettransaction(txid=coinbase_txid, verbose=True) witnesses = coinbase_tx["decoded"]["vin"][0]["txinwitness"] assert_equal(len(witnesses), 1) assert_is_hex_string(witnesses[0]) assert_equal(witnesses[0], '00' * 32) self.log.info( "Verify witness txs without witness data are invalid after the fork" ) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch)', wit_ids[NODE_2][P2WPKH][2], sign=False) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness)', wit_ids[NODE_2][P2WSH][2], sign=False) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch)', p2sh_ids[NODE_2][P2WPKH][2], sign=False, redeem_script=witness_script(False, self.pubkey[2])) self.fail_accept( self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness)', p2sh_ids[NODE_2][P2WSH][2], sign=False, redeem_script=witness_script(True, self.pubkey[2])) self.log.info("Verify default node can now use witness txs") self.success_mine(self.nodes[0], wit_ids[NODE_0][P2WPKH][0], True) # block 432 self.success_mine(self.nodes[0], wit_ids[NODE_0][P2WSH][0], True) # block 433 self.success_mine(self.nodes[0], p2sh_ids[NODE_0][P2WPKH][0], True) # block 434 self.success_mine(self.nodes[0], p2sh_ids[NODE_0][P2WSH][0], True) # block 435 self.log.info( "Verify sigops are counted in GBT with BIP141 rules after the fork" ) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) raw_tx = self.nodes[0].getrawtransaction(txid, True) tmpl = self.nodes[0].getblocktemplate({'rules': ['segwit']}) assert_greater_than_or_equal( tmpl['sizelimit'], 3999577 ) # actual maximum size is lower due to minimum mandatory non-witness data assert_equal(tmpl['weightlimit'], 4000000) assert_equal(tmpl['sigoplimit'], 80000) assert_equal(tmpl['transactions'][0]['txid'], txid) expected_sigops = 9 if 'txinwitness' in raw_tx["vin"][0] else 8 assert_equal(tmpl['transactions'][0]['sigops'], expected_sigops) assert '!segwit' in tmpl['rules'] self.generate(self.nodes[0], 1) # Mine a block to clear the gbt cache self.log.info( "Non-segwit miners are able to use GBT response after activation.") # Create a 3-tx chain: tx1 (non-segwit input, paying to a segwit output) -> # tx2 (segwit input, paying to a non-segwit output) -> # tx3 (non-segwit input, paying to a non-segwit output). # tx1 is allowed to appear in the block, but no others. txid1 = send_to_witness(1, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[0], False, Decimal("49.996")) hex_tx = self.nodes[0].gettransaction(txid)['hex'] tx = tx_from_hex(hex_tx) assert tx.wit.is_null() # This should not be a segwit input assert txid1 in self.nodes[0].getrawmempool() tx1_hex = self.nodes[0].gettransaction(txid1)['hex'] tx1 = tx_from_hex(tx1_hex) # Check that hash and wtxid are properly reported in mempool entry (txid1) assert_equal(int(self.nodes[0].getmempoolentry(txid1)["hash"], 16), tx1.calc_sha256(True)) assert_equal(int(self.nodes[0].getmempoolentry(txid1)["wtxid"], 16), tx1.calc_sha256(True)) # Check that weight and vsize are properly reported in mempool entry (txid1) assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize"], tx1.get_vsize()) assert_equal(self.nodes[0].getmempoolentry(txid1)["weight"], tx1.get_weight()) # Now create tx2, which will spend from txid1. tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b'')) tx.vout.append( CTxOut(int(49.99 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx2_hex = self.nodes[0].signrawtransactionwithwallet( tx.serialize().hex())['hex'] txid2 = self.nodes[0].sendrawtransaction(tx2_hex) tx = tx_from_hex(tx2_hex) assert not tx.wit.is_null() # Check that hash and wtxid are properly reported in mempool entry (txid2) assert_equal(int(self.nodes[0].getmempoolentry(txid2)["hash"], 16), tx.calc_sha256(True)) assert_equal(int(self.nodes[0].getmempoolentry(txid2)["wtxid"], 16), tx.calc_sha256(True)) # Check that weight and vsize are properly reported in mempool entry (txid2) assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize"], tx.get_vsize()) assert_equal(self.nodes[0].getmempoolentry(txid2)["weight"], tx.get_weight()) # Now create tx3, which will spend from txid2 tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b"")) tx.vout.append( CTxOut(int(49.95 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) # Huge fee tx.calc_sha256() txid3 = self.nodes[0].sendrawtransaction( hexstring=tx.serialize().hex(), maxfeerate=0) assert tx.wit.is_null() assert txid3 in self.nodes[0].getrawmempool() # Check that getblocktemplate includes all transactions. template = self.nodes[0].getblocktemplate({"rules": ["segwit"]}) template_txids = [t['txid'] for t in template['transactions']] assert txid1 in template_txids assert txid2 in template_txids assert txid3 in template_txids # Check that hash and wtxid are properly reported in mempool entry (txid3) assert_equal(int(self.nodes[0].getmempoolentry(txid3)["hash"], 16), tx.calc_sha256(True)) assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True)) # Check that weight and vsize are properly reported in mempool entry (txid3) assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize"], tx.get_vsize()) assert_equal(self.nodes[0].getmempoolentry(txid3)["weight"], tx.get_weight()) # Mine a block to clear the gbt cache again. self.generate(self.nodes[0], 1) self.log.info("Signing with all-segwit inputs reveals fee rate") addr = self.nodes[0].getnewaddress(address_type='p2sh-segwit') txid = self.nodes[0].sendtoaddress(addr, 1) tx = self.nodes[0].getrawtransaction(txid, True) n = -1 value = -1 for o in tx["vout"]: if o["scriptPubKey"]["address"] == addr: n = o["n"] value = Decimal(o["value"]) break assert n > -1 # failure means we could not find the address in the outputs despite sending to it assert_equal( value, 1 ) # failure means we got an unexpected amount of coins, despite trying to send 1 fee = Decimal("0.00010000") value_out = value - fee self.generatetoaddress(self.nodes[0], 1, self.nodes[0].getnewaddress()) raw = self.nodes[0].createrawtransaction([{ "txid": txid, "vout": n }], [{ self.nodes[0].getnewaddress(): value_out }]) signed = self.nodes[0].signrawtransactionwithwallet(raw) assert_equal(signed["complete"], True) txsize = self.nodes[0].decoderawtransaction(signed['hex'])['vsize'] exp_feerate = 1000 * fee / Decimal(txsize) assert_approx(signed["feerate"], exp_feerate, Decimal("0.00000010")) # discrepancy = 100000000 * (exp_feerate - signed["feerate"]) # assert -10 < discrepancy < 10 assert_equal(Decimal(signed["fee"]), fee) if not self.options.descriptors: self.log.info("Verify behaviour of importaddress and listunspent") # Some public keys to be used later pubkeys = [ "0363D44AABD0F1699138239DF2F042C3282C0671CC7A76826A55C8203D90E39242", # cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb "02D3E626B3E616FC8662B489C123349FECBFC611E778E5BE739B257EAE4721E5BF", # cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97 "04A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538A62F5BD8EC85C2477F39650BD391EA6250207065B2A81DA8B009FC891E898F0E", # 91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV "02A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538", # cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd "036722F784214129FEB9E8129D626324F3F6716555B603FFE8300BBCB882151228", # cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66 "0266A8396EE936BF6D99D17920DB21C6C7B1AB14C639D5CD72B300297E416FD2EC", # cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K "0450A38BD7F0AC212FEBA77354A9B036A32E0F7C81FC4E0C5ADCA7C549C4505D2522458C2D9AE3CEFD684E039194B72C8A10F9CB9D4764AB26FCC2718D421D3B84", # 92h2XPssjBpsJN5CqSP7v9a7cf2kgDunBC6PDFwJHMACM1rrVBJ ] # Import a compressed key and an uncompressed key, generate some multisig addresses self.nodes[0].importprivkey( "92e6XLo5jVAVwrQKPNTs93oQco8f8sDNBcpv73Dsrs397fQtFQn") uncompressed_spendable_address = [ "mvozP4UwyGD2mGZU4D2eMvMLPB9WkMmMQu" ] self.nodes[0].importprivkey( "cNC8eQ5dg3mFAVePDX4ddmPYpPbw41r9bm2jd1nLJT77e6RrzTRR") compressed_spendable_address = [ "mmWQubrDomqpgSYekvsU7HWEVjLFHAakLe" ] assert not self.nodes[0].getaddressinfo( uncompressed_spendable_address[0])['iscompressed'] assert self.nodes[0].getaddressinfo( compressed_spendable_address[0])['iscompressed'] self.nodes[0].importpubkey(pubkeys[0]) compressed_solvable_address = [key_to_p2pkh(pubkeys[0])] self.nodes[0].importpubkey(pubkeys[1]) compressed_solvable_address.append(key_to_p2pkh(pubkeys[1])) self.nodes[0].importpubkey(pubkeys[2]) uncompressed_solvable_address = [key_to_p2pkh(pubkeys[2])] spendable_anytime = [ ] # These outputs should be seen anytime after importprivkey and addmultisigaddress spendable_after_importaddress = [ ] # These outputs should be seen after importaddress solvable_after_importaddress = [ ] # These outputs should be seen after importaddress but not spendable unsolvable_after_importaddress = [ ] # These outputs should be unsolvable after importaddress solvable_anytime = [ ] # These outputs should be solvable after importpubkey unseen_anytime = [] # These outputs should never be seen uncompressed_spendable_address.append( self.nodes[0].addmultisigaddress(2, [ uncompressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_spendable_address.append( self.nodes[0].addmultisigaddress(2, [ uncompressed_spendable_address[0], uncompressed_spendable_address[0] ])['address']) compressed_spendable_address.append( self.nodes[0].addmultisigaddress(2, [ compressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_solvable_address.append( self.nodes[0].addmultisigaddress(2, [ compressed_spendable_address[0], uncompressed_solvable_address[0] ])['address']) compressed_solvable_address.append( self.nodes[0].addmultisigaddress(2, [ compressed_spendable_address[0], compressed_solvable_address[0] ])['address']) compressed_solvable_address.append( self.nodes[0].addmultisigaddress(2, [ compressed_solvable_address[0], compressed_solvable_address[1] ])['address']) # Test multisig_without_privkey # We have 2 public keys without private keys, use addmultisigaddress to add to wallet. # Money sent to P2SH of multisig of this should only be seen after importaddress with the BASE58 P2SH address. multisig_without_privkey_address = self.nodes[ 0].addmultisigaddress(2, [pubkeys[3], pubkeys[4]])['address'] script = keys_to_multisig_script([pubkeys[3], pubkeys[4]]) solvable_after_importaddress.append(script_to_p2sh_script(script)) for i in compressed_spendable_address: v = self.nodes[0].getaddressinfo(i) if v['isscript']: [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # p2sh multisig with compressed keys should always be spendable spendable_anytime.extend([p2sh]) # bare multisig can be watched and signed, but is not treated as ours solvable_after_importaddress.extend([bare]) # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after direct importaddress spendable_after_importaddress.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with compressed keys should always be spendable spendable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK, P2SH_P2PKH with compressed keys are spendable after direct importaddress spendable_after_importaddress.extend([ p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) # P2WPKH and P2SH_P2WPKH with compressed keys should always be spendable spendable_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in uncompressed_spendable_address: v = self.nodes[0].getaddressinfo(i) if v['isscript']: [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # p2sh multisig with uncompressed keys should always be spendable spendable_anytime.extend([p2sh]) # bare multisig can be watched and signed, but is not treated as ours solvable_after_importaddress.extend([bare]) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with uncompressed keys should always be spendable spendable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress spendable_after_importaddress.extend( [p2sh_p2pk, p2sh_p2pkh]) # Witness output types with uncompressed keys are never seen unseen_anytime.extend([ p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) for i in compressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if v['isscript']: # Multisig without private is not seen after addmultisigaddress, but seen after importaddress [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) solvable_after_importaddress.extend( [bare, p2sh, p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH, P2PK, P2WPKH and P2SH_P2WPKH with compressed keys should always be seen solvable_anytime.extend([p2pkh, p2pk, p2wpkh, p2sh_p2wpkh]) # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after direct importaddress solvable_after_importaddress.extend([ p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) for i in uncompressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if v['isscript']: [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # Base uncompressed multisig without private is not seen after addmultisigaddress, but seen after importaddress solvable_after_importaddress.extend([bare, p2sh]) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with uncompressed keys should always be seen solvable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress solvable_after_importaddress.extend( [p2sh_p2pk, p2sh_p2pkh]) # Witness output types with uncompressed keys are never seen unseen_anytime.extend([ p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ]) op1 = CScript([OP_1]) op0 = CScript([OP_0]) # 2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe is the P2SH(P2PKH) version of mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V unsolvable_address_key = bytes.fromhex( "02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D" ) unsolvablep2pkh = key_to_p2pkh_script(unsolvable_address_key) unsolvablep2wshp2pkh = script_to_p2wsh_script(unsolvablep2pkh) p2shop0 = script_to_p2sh_script(op0) p2wshop1 = script_to_p2wsh_script(op1) unsolvable_after_importaddress.append(unsolvablep2pkh) unsolvable_after_importaddress.append(unsolvablep2wshp2pkh) unsolvable_after_importaddress.append( op1) # OP_1 will be imported as script unsolvable_after_importaddress.append(p2wshop1) unseen_anytime.append( op0 ) # OP_0 will be imported as P2SH address with no script provided unsolvable_after_importaddress.append(p2shop0) spendable_txid = [] solvable_txid = [] spendable_txid.append( self.mine_and_test_listunspent(spendable_anytime, 2)) solvable_txid.append( self.mine_and_test_listunspent(solvable_anytime, 1)) self.mine_and_test_listunspent( spendable_after_importaddress + solvable_after_importaddress + unseen_anytime + unsolvable_after_importaddress, 0) importlist = [] for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if v['isscript']: bare = bytes.fromhex(v['hex']) importlist.append(bare.hex()) importlist.append(script_to_p2wsh_script(bare).hex()) else: pubkey = bytes.fromhex(v['pubkey']) p2pk = key_to_p2pk_script(pubkey) p2pkh = key_to_p2pkh_script(pubkey) importlist.append(p2pk.hex()) importlist.append(p2pkh.hex()) importlist.append(key_to_p2wpkh_script(pubkey).hex()) importlist.append(script_to_p2wsh_script(p2pk).hex()) importlist.append(script_to_p2wsh_script(p2pkh).hex()) importlist.append(unsolvablep2pkh.hex()) importlist.append(unsolvablep2wshp2pkh.hex()) importlist.append(op1.hex()) importlist.append(p2wshop1.hex()) for i in importlist: # import all generated addresses. The wallet already has the private keys for some of these, so catch JSON RPC # exceptions and continue. try_rpc( -4, "The wallet already contains the private key for this address or script", self.nodes[0].importaddress, i, "", False, True) self.nodes[0].importaddress( script_to_p2sh(op0)) # import OP_0 as address only self.nodes[0].importaddress(multisig_without_privkey_address ) # Test multisig_without_privkey spendable_txid.append( self.mine_and_test_listunspent( spendable_anytime + spendable_after_importaddress, 2)) solvable_txid.append( self.mine_and_test_listunspent( solvable_anytime + solvable_after_importaddress, 1)) self.mine_and_test_listunspent(unsolvable_after_importaddress, 1) self.mine_and_test_listunspent(unseen_anytime, 0) spendable_txid.append( self.mine_and_test_listunspent( spendable_anytime + spendable_after_importaddress, 2)) solvable_txid.append( self.mine_and_test_listunspent( solvable_anytime + solvable_after_importaddress, 1)) self.mine_and_test_listunspent(unsolvable_after_importaddress, 1) self.mine_and_test_listunspent(unseen_anytime, 0) # Repeat some tests. This time we don't add witness scripts with importaddress # Import a compressed key and an uncompressed key, generate some multisig addresses self.nodes[0].importprivkey( "927pw6RW8ZekycnXqBQ2JS5nPyo1yRfGNN8oq74HeddWSpafDJH") uncompressed_spendable_address = [ "mguN2vNSCEUh6rJaXoAVwY3YZwZvEmf5xi" ] self.nodes[0].importprivkey( "cMcrXaaUC48ZKpcyydfFo8PxHAjpsYLhdsp6nmtB3E2ER9UUHWnw") compressed_spendable_address = [ "n1UNmpmbVUJ9ytXYXiurmGPQ3TRrXqPWKL" ] self.nodes[0].importpubkey(pubkeys[5]) compressed_solvable_address = [key_to_p2pkh(pubkeys[5])] self.nodes[0].importpubkey(pubkeys[6]) uncompressed_solvable_address = [key_to_p2pkh(pubkeys[6])] unseen_anytime = [] # These outputs should never be seen solvable_anytime = [ ] # These outputs should be solvable after importpubkey unseen_anytime = [] # These outputs should never be seen uncompressed_spendable_address.append( self.nodes[0].addmultisigaddress(2, [ uncompressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_spendable_address.append( self.nodes[0].addmultisigaddress(2, [ uncompressed_spendable_address[0], uncompressed_spendable_address[0] ])['address']) compressed_spendable_address.append( self.nodes[0].addmultisigaddress(2, [ compressed_spendable_address[0], compressed_spendable_address[0] ])['address']) uncompressed_solvable_address.append( self.nodes[0].addmultisigaddress(2, [ compressed_solvable_address[0], uncompressed_solvable_address[0] ])['address']) compressed_solvable_address.append( self.nodes[0].addmultisigaddress(2, [ compressed_spendable_address[0], compressed_solvable_address[0] ])['address']) premature_witaddress = [] for i in compressed_spendable_address: v = self.nodes[0].getaddressinfo(i) if v['isscript']: [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) premature_witaddress.append(script_to_p2sh(p2wsh)) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # P2WPKH, P2SH_P2WPKH are always spendable spendable_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in uncompressed_spendable_address + uncompressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if v['isscript']: [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # P2WPKH, P2SH_P2WPKH with uncompressed keys are never seen unseen_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in compressed_solvable_address: v = self.nodes[0].getaddressinfo(i) if v['isscript']: [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) premature_witaddress.append(script_to_p2sh(p2wsh)) else: [ p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh ] = self.p2pkh_address_to_script(v) # P2SH_P2PK, P2SH_P2PKH with compressed keys are always solvable solvable_anytime.extend([p2wpkh, p2sh_p2wpkh]) self.mine_and_test_listunspent(spendable_anytime, 2) self.mine_and_test_listunspent(solvable_anytime, 1) self.mine_and_test_listunspent(unseen_anytime, 0) # Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works v1_addr = program_to_witness(1, [3, 5]) v1_tx = self.nodes[0].createrawtransaction( [getutxo(spendable_txid[0])], {v1_addr: 1}) v1_decoded = self.nodes[1].decoderawtransaction(v1_tx) assert_equal(v1_decoded['vout'][0]['scriptPubKey']['address'], v1_addr) assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305") # Check that spendable outputs are really spendable self.create_and_mine_tx_from_txids(spendable_txid) # import all the private keys so solvable addresses become spendable self.nodes[0].importprivkey( "cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb") self.nodes[0].importprivkey( "cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97") self.nodes[0].importprivkey( "91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV") self.nodes[0].importprivkey( "cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd") self.nodes[0].importprivkey( "cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66") self.nodes[0].importprivkey( "cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K") self.create_and_mine_tx_from_txids(solvable_txid) # Test that importing native P2WPKH/P2WSH scripts works for use_p2wsh in [False, True]: if use_p2wsh: scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a" transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000" else: scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87" transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000" self.nodes[1].importaddress(scriptPubKey, "", False) rawtxfund = self.nodes[1].fundrawtransaction( transaction)['hex'] rawtxfund = self.nodes[1].signrawtransactionwithwallet( rawtxfund)["hex"] txid = self.nodes[1].sendrawtransaction(rawtxfund) assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) assert_equal( self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid) # Assert it is properly saved self.restart_node(1) assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) assert_equal( self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
def run_test(self): print("Testing wallet secret recovery") self.test_wallet_recovery() print("General Confidential tests") # Running balances node0 = self.nodes[0].getbalance()["bitcoin"] assert_equal(node0, 21000000) # just making sure initialfreecoins is working node1 = 0 node2 = 0 self.nodes[0].generate(101) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), node0, "", "", True) self.nodes[0].generate(101) self.sync_all() assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance("*", 1, False, "bitcoin"), node2) # Send 3 BTC from 0 to a new unconfidential address of 2 with # the sendtoaddress call address = self.nodes[2].getnewaddress() unconfidential_address = self.nodes[2].validateaddress(address)["unconfidential"] value0 = 3 self.nodes[0].sendtoaddress(unconfidential_address, value0) self.nodes[0].generate(101) self.sync_all() node0 = node0 - value0 node2 = node2 + value0 assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance()["bitcoin"], node2) # Send 5 BTC from 0 to a new address of 2 with the sendtoaddress call address2 = self.nodes[2].getnewaddress() unconfidential_address2 = self.nodes[2].validateaddress(address2)["unconfidential"] value1 = 5 confidential_tx_id = self.nodes[0].sendtoaddress(address2, value1) self.nodes[0].generate(101) self.sync_all() node0 = node0 - value1 node2 = node2 + value1 assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance()["bitcoin"], node2) # Send 7 BTC from 0 to the unconfidential address of 2 and 11 BTC to the # confidential address using the raw transaction interface change_address = self.nodes[0].getnewaddress() value2 = 7 value3 = 11 value23 = value2 + value3 unspent = self.nodes[0].listunspent(1, 9999999, [], True, {"asset": "bitcoin"}) unspent = [i for i in unspent if i['amount'] > value23] assert_equal(len(unspent), 1) fee = Decimal('0.0001') tx = self.nodes[0].createrawtransaction([{"txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "nValue": unspent[0]["amount"]}], {unconfidential_address: value2, address2: value3, change_address: unspent[0]["amount"] - value2 - value3 - fee, "fee":fee}) tx = self.nodes[0].blindrawtransaction(tx) tx_signed = self.nodes[0].signrawtransactionwithwallet(tx) raw_tx_id = self.nodes[0].sendrawtransaction(tx_signed['hex']) self.nodes[0].generate(101) self.sync_all() node0 -= (value2 + value3) node2 += value2 + value3 assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance()["bitcoin"], node2) # Check 2's listreceivedbyaddress received_by_address = self.nodes[2].listreceivedbyaddress(0, False, False, "", "bitcoin") validate_by_address = [(address2, {"bitcoin": value1 + value3}), (address, {"bitcoin": value0 + value2})] assert_equal(sorted([(ele['address'], ele['amount']) for ele in received_by_address], key=lambda t: t[0]), sorted(validate_by_address, key = lambda t: t[0])) # Give an auditor (node 1) a blinding key to allow her to look at # transaction values self.nodes[1].importaddress(address2) received_by_address = self.nodes[1].listreceivedbyaddress(1, False, True) #Node sees nothing unless it understands the values assert_equal(len(received_by_address), 0) assert_equal(len(self.nodes[1].listunspent(1, 9999999, [], True, {"asset": "bitcoin"})), 0) # Import the blinding key blindingkey = self.nodes[2].dumpblindingkey(address2) self.nodes[1].importblindingkey(address2, blindingkey) # Check the auditor's gettransaction and listreceivedbyaddress # Needs rescan to update wallet txns conf_tx = self.nodes[1].gettransaction(confidential_tx_id, True) assert_equal(conf_tx['amount']["bitcoin"], value1) # Make sure wallet can now deblind part of transaction deblinded_tx = self.nodes[1].unblindrawtransaction(conf_tx['hex'])['hex'] for output in self.nodes[1].decoderawtransaction(deblinded_tx)["vout"]: if "value" in output and output["scriptPubKey"]["type"] != "fee": assert_equal(output["scriptPubKey"]["addresses"][0], self.nodes[1].validateaddress(address2)['unconfidential']) found_unblinded = True assert(found_unblinded) assert_equal(self.nodes[1].gettransaction(raw_tx_id, True)['amount']["bitcoin"], value3) list_unspent = self.nodes[1].listunspent(1, 9999999, [], True, {"asset": "bitcoin"}) assert_equal(list_unspent[0]['amount']+list_unspent[1]['amount'], value1+value3) received_by_address = self.nodes[1].listreceivedbyaddress(1, False, True) assert_equal(len(received_by_address), 1) assert_equal((received_by_address[0]['address'], received_by_address[0]['amount']['bitcoin']), (unconfidential_address2, value1 + value3)) # Spending a single confidential output and sending it to a # unconfidential output is not possible with CT. Test the # correct behavior of blindrawtransaction. unspent = self.nodes[0].listunspent(1, 9999999, [], True, {"asset": "bitcoin"}) unspent = [i for i in unspent if i['amount'] > value23] assert_equal(len(unspent), 1) tx = self.nodes[0].createrawtransaction([{"txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "nValue": unspent[0]["amount"]}], {unconfidential_address: unspent[0]["amount"] - fee, "fee":fee}) # Test that blindrawtransaction adds an OP_RETURN output to balance blinders temptx = self.nodes[0].blindrawtransaction(tx) decodedtx = self.nodes[0].decoderawtransaction(temptx) assert_equal(decodedtx["vout"][-1]["scriptPubKey"]["asm"], "OP_RETURN") assert_equal(len(decodedtx["vout"]), 3) # Create same transaction but with a change/dummy output. # It should pass the blinding step. value4 = 17 change_address = self.nodes[0].getrawchangeaddress() tx = self.nodes[0].createrawtransaction([{"txid": unspent[0]["txid"], "vout": unspent[0]["vout"], "nValue": unspent[0]["amount"]}], {unconfidential_address: value4, change_address: unspent[0]["amount"] - value4 - fee, "fee":fee}) tx = self.nodes[0].blindrawtransaction(tx) tx_signed = self.nodes[0].signrawtransactionwithwallet(tx) txid = self.nodes[0].sendrawtransaction(tx_signed['hex']) decodedtx = self.nodes[0].decoderawtransaction(tx_signed["hex"]) self.nodes[0].generate(101) self.sync_all() unblindfound = False for i in range(len(decodedtx["vout"])): txout = self.nodes[0].gettxout(txid, i) if txout is not None and "asset" in txout: unblindfound = True if unblindfound == False: raise Exception("No unconfidential output detected when one should exist") node0 -= value4 node2 += value4 assert_equal(self.nodes[0].getbalance()["bitcoin"], node0) assert_equal(self.nodes[1].getbalance("*", 1, False, "bitcoin"), node1) assert_equal(self.nodes[2].getbalance()["bitcoin"], node2) # Testing wallet's ability to deblind its own outputs addr = self.nodes[0].getnewaddress() addr2 = self.nodes[0].getnewaddress() # We add two to-blind outputs, fundraw adds an already-blinded change output # If we only add one, the newly blinded will be 0-blinded because input = -output raw = self.nodes[0].createrawtransaction([], {addr:Decimal('1.1'), addr2:1}) funded = self.nodes[0].fundrawtransaction(raw) # fund again to make sure no blinded outputs were created (would fail) funded = self.nodes[0].fundrawtransaction(funded["hex"]) blinded = self.nodes[0].blindrawtransaction(funded["hex"]) # blind again to make sure we know output blinders blinded2 = self.nodes[0].blindrawtransaction(blinded) # then sign and send signed = self.nodes[0].signrawtransactionwithwallet(blinded2) self.nodes[0].sendrawtransaction(signed["hex"]) # Aside: Check all outputs after fundraw are properly marked for blinding fund_decode = self.nodes[0].decoderawtransaction(funded["hex"]) for output in fund_decode["vout"][:-1]: assert "asset" in output assert "value" in output assert output["scriptPubKey"]["type"] != "fee" assert output["commitmentnonce_fully_valid"] assert fund_decode["vout"][-1]["scriptPubKey"]["type"] == "fee" assert not fund_decode["vout"][-1]["commitmentnonce_fully_valid"] # Also check that all fundraw outputs marked for blinding are blinded later for blind_tx in [blinded, blinded2]: blind_decode = self.nodes[0].decoderawtransaction(blind_tx) for output in blind_decode["vout"][:-1]: assert "asset" not in output assert "value" not in output assert output["scriptPubKey"]["type"] != "fee" assert output["commitmentnonce_fully_valid"] assert blind_decode["vout"][-1]["scriptPubKey"]["type"] == "fee" assert "asset" in blind_decode["vout"][-1] assert "value" in blind_decode["vout"][-1] assert not blind_decode["vout"][-1]["commitmentnonce_fully_valid"] # Check createblindedaddress functionality blinded_addr = self.nodes[0].getnewaddress() validated_addr = self.nodes[0].validateaddress(blinded_addr) blinding_pubkey = self.nodes[0].validateaddress(blinded_addr)["confidential_key"] blinding_key = self.nodes[0].dumpblindingkey(blinded_addr) assert_equal(blinded_addr, self.nodes[1].createblindedaddress(validated_addr["unconfidential"], blinding_pubkey)) # If a blinding key is over-ridden by a newly imported one, funds may be unaccounted for new_addr = self.nodes[0].getnewaddress() new_validated = self.nodes[0].validateaddress(new_addr) self.nodes[2].sendtoaddress(new_addr, 1) self.sync_all() diff_blind = self.nodes[1].createblindedaddress(new_validated["unconfidential"], blinding_pubkey) assert_equal(len(self.nodes[0].listunspent(0, 0, [new_validated["unconfidential"]])), 1) self.nodes[0].importblindingkey(diff_blind, blinding_key) # CT values for this wallet transaction have been cached via importblindingkey # therefore result will be same even though we change blinding keys assert_equal(len(self.nodes[0].listunspent(0, 0, [new_validated["unconfidential"]])), 1) # Confidential Assets Tests print("Assets tests...") # Bitcoin is the first issuance assert_equal(self.nodes[0].listissuances()[0]["assetlabel"], "bitcoin") assert_equal(len(self.nodes[0].listissuances()), 1) # Unblinded issuance of asset issued = self.nodes[0].issueasset(1, 1, False) self.nodes[0].reissueasset(issued["asset"], 1) # Compare resulting fields with getrawtransaction raw_details = self.nodes[0].getrawtransaction(issued["txid"], 1) assert_equal(issued["entropy"], raw_details["vin"][issued["vin"]]["issuance"]["assetEntropy"]) assert_equal(issued["asset"], raw_details["vin"][issued["vin"]]["issuance"]["asset"]) assert_equal(issued["token"], raw_details["vin"][issued["vin"]]["issuance"]["token"]) self.nodes[0].generate(1) self.sync_all() issued2 = self.nodes[0].issueasset(2, 1) test_asset = issued2["asset"] assert_equal(self.nodes[0].getwalletinfo()['balance'][test_asset], Decimal(2)) assert(test_asset not in self.nodes[1].getwalletinfo()['balance']) # Assets balance checking, note that accounts are completely ignored because # balance queries with accounts are horrifically broken upstream assert_equal(self.nodes[0].getbalance("*", 0, False, "bitcoin"), self.nodes[0].getbalance("*", 0, False, "bitcoin")) assert_equal(self.nodes[0].getwalletinfo()['balance']['bitcoin'], self.nodes[0].getbalance("*", 0, False, "bitcoin")) # Send some bitcoin and other assets over as well to fund wallet addr = self.nodes[2].getnewaddress() self.nodes[0].sendtoaddress(addr, 5) self.nodes[0].sendmany("", {addr: 1, self.nodes[2].getnewaddress(): 13}, 0, "", [], False, 1, "UNSET", {addr: test_asset}) self.sync_all() # Should have exactly 1 in change(trusted, though not confirmed) after sending one off assert_equal(self.nodes[0].getbalance("*", 0, False, test_asset), 1) assert_equal(self.nodes[2].getunconfirmedbalance()[test_asset], Decimal(1)) b_utxos = self.nodes[2].listunspent(0, 0, [], True, {"asset": "bitcoin"}) t_utxos = self.nodes[2].listunspent(0, 0, [], True, {"asset": test_asset}) assert_equal(len(self.nodes[2].listunspent(0, 0, [])), len(b_utxos)+len(t_utxos)) # Now craft a blinded transaction via raw api rawaddrs = [] for i in range(2): rawaddrs.append(self.nodes[1].getnewaddress()) raw_assets = self.nodes[2].createrawtransaction([{"txid":b_utxos[0]['txid'], "vout":b_utxos[0]['vout'], "nValue":b_utxos[0]['amount']}, {"txid":b_utxos[1]['txid'], "vout":b_utxos[1]['vout'], "nValue":b_utxos[1]['amount'], "asset":b_utxos[1]['asset']}, {"txid":t_utxos[0]['txid'], "vout":t_utxos[0]['vout'], "nValue":t_utxos[0]['amount'], "asset":t_utxos[0]['asset']}], {rawaddrs[1]:Decimal(t_utxos[0]['amount']), rawaddrs[0]:Decimal(b_utxos[0]['amount']+b_utxos[1]['amount']-Decimal("0.01")), "fee":Decimal("0.01")}, 0, False, {rawaddrs[0]:b_utxos[0]['asset'], rawaddrs[1]:t_utxos[0]['asset'], "fee":b_utxos[0]['asset']}) # Sign unblinded, then blinded signed_assets = self.nodes[2].signrawtransactionwithwallet(raw_assets) blind_assets = self.nodes[2].blindrawtransaction(raw_assets) signed_assets = self.nodes[2].signrawtransactionwithwallet(blind_assets) # And finally send self.nodes[2].sendrawtransaction(signed_assets['hex']) self.nodes[2].generate(101) self.sync_all() issuancedata = self.nodes[2].issueasset(0, Decimal('0.00000006')) #0 of asset, 6 reissuance token # Node 2 will send node 1 a reissuance token, both will generate assets self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), Decimal('0.00000001'), "", "", False, False, 1, "UNSET", issuancedata["token"]) # node 1 needs to know about a (re)issuance to reissue itself self.nodes[1].importaddress(self.nodes[2].gettransaction(issuancedata["txid"])["details"][0]["address"]) # also send some bitcoin self.nodes[2].generate(1) self.sync_all() self.nodes[1].reissueasset(issuancedata["asset"], Decimal('0.05')) self.nodes[2].reissueasset(issuancedata["asset"], Decimal('0.025')) self.nodes[1].generate(1) self.sync_all() # Check for value accounting when asset issuance is null but token not, ie unblinded # HACK: Self-send to sweep up bitcoin inputs into blinded output. # We were hitting https://github.com/ElementsProject/elements/issues/473 for the following issuance self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), self.nodes[0].getwalletinfo()["balance"]["bitcoin"], "", "", True) issued = self.nodes[0].issueasset(0, 1, False) walletinfo = self.nodes[0].getwalletinfo() assert(issued["asset"] not in walletinfo["balance"]) assert_equal(walletinfo["balance"][issued["token"]], Decimal(1)) assert(issued["asset"] not in walletinfo["unconfirmed_balance"]) assert(issued["token"] not in walletinfo["unconfirmed_balance"]) # Check for value when receiving different assets by same address. self.nodes[0].sendtoaddress(unconfidential_address2, Decimal('0.00000001'), "", "", False, False, 1, "UNSET", test_asset) self.nodes[0].sendtoaddress(unconfidential_address2, Decimal('0.00000002'), "", "", False, False, 1, "UNSET", test_asset) self.nodes[0].generate(1) self.sync_all() received_by_address = self.nodes[1].listreceivedbyaddress(0, False, True) multi_asset_amount = [x for x in received_by_address if x['address'] == unconfidential_address2][0]['amount'] assert_equal(multi_asset_amount['bitcoin'], value1 + value3) assert_equal(multi_asset_amount[test_asset], Decimal('0.00000003')) # Check blinded multisig functionality and partial blinding functionality # Get two pubkeys blinded_addr = self.nodes[0].getnewaddress() pubkey = self.nodes[0].getaddressinfo(blinded_addr)["pubkey"] blinded_addr2 = self.nodes[1].getnewaddress() pubkey2 = self.nodes[1].getaddressinfo(blinded_addr2)["pubkey"] pubkeys = [pubkey, pubkey2] # Add multisig address unconfidential_addr = self.nodes[0].addmultisigaddress(2, pubkeys)["address"] self.nodes[1].addmultisigaddress(2, pubkeys) self.nodes[0].importaddress(unconfidential_addr) self.nodes[1].importaddress(unconfidential_addr) # Use blinding key from node 0's original getnewaddress call blinding_pubkey = self.nodes[0].getaddressinfo(blinded_addr)["confidential_key"] blinding_key = self.nodes[0].dumpblindingkey(blinded_addr) # Create blinded address from p2sh address and import corresponding privkey blinded_multisig_addr = self.nodes[0].createblindedaddress(unconfidential_addr, blinding_pubkey) self.nodes[0].importblindingkey(blinded_multisig_addr, blinding_key) # Issue new asset, to use different assets in one transaction when doing # partial blinding. Just to make these tests a bit more elaborate :-) issued3 = self.nodes[2].issueasset(1, 0) self.nodes[2].generate(1) self.sync_all() node2_balance = self.nodes[2].getbalance() assert(issued3['asset'] in node2_balance) assert_equal(node2_balance[issued3['asset']], Decimal(1)) # Send asset to blinded multisig address and check that it was received self.nodes[2].sendtoaddress(address=blinded_multisig_addr, amount=1, assetlabel=issued3['asset']) self.sync_all() # We will use this multisig UTXO in our partially-blinded transaction, # and will also check that multisig UTXO can be successfully spent # after the transaction is signed by node1 and node0 in succession. unspent_asset = self.nodes[0].listunspent(0, 0, [unconfidential_addr], True, {"asset":issued3['asset']}) assert_equal(len(unspent_asset), 1) assert(issued3['asset'] not in self.nodes[2].getbalance()) # Create new UTXO on node0 to be used in our partially-blinded transaction blinded_addr = self.nodes[0].getnewaddress() addr = self.nodes[0].validateaddress(blinded_addr)["unconfidential"] self.nodes[0].sendtoaddress(blinded_addr, 0.1) unspent = self.nodes[0].listunspent(0, 0, [addr]) assert_equal(len(unspent), 1) # Create new UTXO on node1 to be used in our partially-blinded transaction blinded_addr2 = self.nodes[1].getnewaddress() addr2 = self.nodes[1].validateaddress(blinded_addr2)["unconfidential"] self.nodes[1].sendtoaddress(blinded_addr2, 0.11) unspent2 = self.nodes[1].listunspent(0, 0, [addr2]) assert_equal(len(unspent2), 1) # The transaction will have three non-fee outputs dst_addr = self.nodes[0].getnewaddress() dst_addr2 = self.nodes[1].getnewaddress() dst_addr3 = self.nodes[2].getnewaddress() # Inputs are selected up front inputs = [{"txid": unspent2[0]["txid"], "vout": unspent2[0]["vout"]}, {"txid": unspent[0]["txid"], "vout": unspent[0]["vout"]}, {"txid": unspent_asset[0]["txid"], "vout": unspent_asset[0]["vout"]}] # Create one part of the transaction to partially blind rawtx = self.nodes[0].createrawtransaction( inputs, {dst_addr2: Decimal("0.01")}) # Create another part of the transaction to partially blind rawtx2 = self.nodes[0].createrawtransaction( inputs, {dst_addr: Decimal("0.1"), dst_addr3: Decimal("1.0")}, 0, False, {dst_addr: unspent[0]['asset'], dst_addr3: unspent_asset[0]['asset']}) sum_i = unspent2[0]["amount"] + unspent[0]["amount"] sum_o = 0.01 + 0.10 + 0.1 assert_equal(int(round(sum_i*COIN)), int(round(sum_o*COIN))) # Blind the first part of the transaction - we need to supply the # assetcommmitments for all of the inputs, for the surjectionproof # to be valid after we combine the transactions blindtx = self.nodes[1].blindrawtransaction( rawtx, True, [ unspent2[0]['assetcommitment'], unspent[0]['assetcommitment'], unspent_asset[0]['assetcommitment'] ]) # Combine the transactions # Blinded, but incomplete transaction. # 3 inputs and 1 output, but no fee output, and # it was blinded with 3 asset commitments, that means # the final transaction should have 3 inputs. btx = CTransaction() btx.deserialize(io.BytesIO(hex_str_to_bytes(blindtx))) # Unblinded transaction, with 3 inputs and 2 outputs. # We will add them to the other transaction to make it complete. ubtx = CTransaction() ubtx.deserialize(io.BytesIO(hex_str_to_bytes(rawtx2))) # We will add outputs of unblinded transaction # on top of inputs and outputs of the blinded, but incomplete transaction. # We also append empty witness instances to make witness arrays match # vin/vout arrays btx.wit.vtxinwit.append(CTxInWitness()) btx.vout.append(ubtx.vout[0]) btx.wit.vtxoutwit.append(CTxOutWitness()) btx.wit.vtxinwit.append(CTxInWitness()) btx.vout.append(ubtx.vout[1]) btx.wit.vtxoutwit.append(CTxOutWitness()) # Add explicit fee output btx.vout.append(CTxOut(nValue=CTxOutValue(10000000), nAsset=CTxOutAsset(BITCOIN_ASSET_OUT))) btx.wit.vtxoutwit.append(CTxOutWitness()) # Input 0 is bitcoin asset (already blinded) # Input 1 is also bitcoin asset # Input 2 is our new asset # Blind with wrong order of assetcommitments - such transaction should be rejected blindtx = self.nodes[0].blindrawtransaction( bytes_to_hex_str(btx.serialize()), True, [ unspent_asset[0]['assetcommitment'], unspent[0]['assetcommitment'], unspent2[0]['assetcommitment'] ]) stx2 = self.nodes[1].signrawtransactionwithwallet(blindtx) stx = self.nodes[0].signrawtransactionwithwallet(stx2['hex']) self.sync_all() assert_raises_rpc_error(-26, "bad-txns-in-ne-out", self.nodes[2].sendrawtransaction, stx['hex']) # Blind with correct order of assetcommitments blindtx = self.nodes[0].blindrawtransaction( bytes_to_hex_str(btx.serialize()), True, [ unspent2[0]['assetcommitment'], unspent[0]['assetcommitment'], unspent_asset[0]['assetcommitment'] ]) stx2 = self.nodes[1].signrawtransactionwithwallet(blindtx) stx = self.nodes[0].signrawtransactionwithwallet(stx2['hex']) txid = self.nodes[2].sendrawtransaction(stx['hex']) self.nodes[2].generate(1) assert self.nodes[2].getrawtransaction(txid, 1)['confirmations'] == 1 self.sync_all() # Check that the sent asset has reached its destination unconfidential_dst_addr3 = self.nodes[2].validateaddress(dst_addr3)["unconfidential"] unspent_asset2 = self.nodes[2].listunspent(1, 1, [unconfidential_dst_addr3], True, {"asset":issued3['asset']}) assert_equal(len(unspent_asset2), 1) assert_equal(unspent_asset2[0]['amount'], Decimal(1)) # And that the balance was correctly updated assert_equal(self.nodes[2].getbalance()[issued3['asset']], Decimal(1)) # Basic checks of rawblindrawtransaction functionality blinded_addr = self.nodes[0].getnewaddress() addr = self.nodes[0].validateaddress(blinded_addr)["unconfidential"] self.nodes[0].sendtoaddress(blinded_addr, 1) self.nodes[0].sendtoaddress(blinded_addr, 3) unspent = self.nodes[0].listunspent(0, 0) rawtx = self.nodes[0].createrawtransaction([{"txid":unspent[0]["txid"], "vout":unspent[0]["vout"]}, {"txid":unspent[1]["txid"], "vout":unspent[1]["vout"]}], {addr:unspent[0]["amount"]+unspent[1]["amount"]-Decimal("0.2"), "fee":Decimal("0.2")}) # Blinding will fail with 2 blinded inputs and 0 blinded outputs # since it has no notion of a wallet to fill in a 0-value OP_RETURN output try: self.nodes[0].rawblindrawtransaction(rawtx, [unspent[0]["amountblinder"], unspent[1]["amountblinder"]], [unspent[0]["amount"], unspent[1]["amount"]], [unspent[0]["asset"], unspent[1]["asset"]], [unspent[0]["assetblinder"], unspent[1]["assetblinder"]]) raise AssertionError("Shouldn't be able to blind 2 input 0 output transaction via rawblindraw") except JSONRPCException: pass # Blinded destination added, can blind, sign and send rawtx = self.nodes[0].createrawtransaction([{"txid":unspent[0]["txid"], "vout":unspent[0]["vout"]}, {"txid":unspent[1]["txid"], "vout":unspent[1]["vout"]}], {blinded_addr:unspent[0]["amount"]+unspent[1]["amount"]-Decimal("0.002"), "fee":Decimal("0.002")}) signtx = self.nodes[0].signrawtransactionwithwallet(rawtx) try: self.nodes[0].sendrawtransaction(signtx["hex"]) raise AssertionError("Shouldn't be able to send unblinded tx with emplaced pubkey in output without additional argument") except JSONRPCException: pass blindtx = self.nodes[0].rawblindrawtransaction(rawtx, [unspent[0]["amountblinder"], unspent[1]["amountblinder"]], [unspent[0]["amount"], unspent[1]["amount"]], [unspent[0]["asset"], unspent[1]["asset"]], [unspent[0]["assetblinder"], unspent[1]["assetblinder"]]) signtx = self.nodes[0].signrawtransactionwithwallet(blindtx) txid = self.nodes[0].sendrawtransaction(signtx["hex"]) for output in self.nodes[0].decoderawtransaction(blindtx)["vout"]: if "asset" in output and output["scriptPubKey"]["type"] != "fee": raise AssertionError("An unblinded output exists") # Test fundrawtransaction with multiple assets issue = self.nodes[0].issueasset(1, 0) assetaddr = self.nodes[0].getnewaddress() rawtx = self.nodes[0].createrawtransaction([], {assetaddr:1, self.nodes[0].getnewaddress():2}, 0, False, {assetaddr:issue["asset"]}) funded = self.nodes[0].fundrawtransaction(rawtx) blinded = self.nodes[0].blindrawtransaction(funded["hex"]) signed = self.nodes[0].signrawtransactionwithwallet(blinded) txid = self.nodes[0].sendrawtransaction(signed["hex"]) # Test fundrawtransaction with multiple inputs, creating > vout.size change rawtx = self.nodes[0].createrawtransaction([{"txid":txid, "vout":0}, {"txid":txid, "vout":1}], {self.nodes[0].getnewaddress():5}) funded = self.nodes[0].fundrawtransaction(rawtx) blinded = self.nodes[0].blindrawtransaction(funded["hex"]) signed = self.nodes[0].signrawtransactionwithwallet(blinded) txid = self.nodes[0].sendrawtransaction(signed["hex"]) # Test corner case where wallet appends a OP_RETURN output, yet doesn't blind it # due to the fact that the output value is 0-value and input pedersen commitments # self-balance. This is rare corner case, but ok. unblinded = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())["unconfidential"] self.nodes[0].sendtoaddress(unblinded, self.nodes[0].getbalance()["bitcoin"], "", "", True) # Make tx with blinded destination and change outputs only self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), self.nodes[0].getbalance()["bitcoin"]/2) # Send back again, this transaction should have 3 outputs, all unblinded txid = self.nodes[0].sendtoaddress(unblinded, self.nodes[0].getbalance()["bitcoin"], "", "", True) outputs = self.nodes[0].getrawtransaction(txid, 1)["vout"] assert_equal(len(outputs), 3) assert("value" in outputs[0] and "value" in outputs[1] and "value" in outputs[2]) assert_equal(outputs[2]["scriptPubKey"]["type"], 'nulldata') # Test burn argument in createrawtransaction raw_burn1 = self.nodes[0].createrawtransaction([], {self.nodes[0].getnewaddress():1, "burn":2}) decode_burn1 = self.nodes[0].decoderawtransaction(raw_burn1) assert_equal(len(decode_burn1["vout"]), 2) found_pay = False found_burn = False for output in decode_burn1["vout"]: if output["scriptPubKey"]["asm"] == "OP_RETURN": found_burn = True if output["asset"] != self.nodes[0].dumpassetlabels()["bitcoin"]: raise Exception("Burn should have been bitcoin(policyAsset)") if output["scriptPubKey"]["type"] == "scripthash": found_pay = True assert(found_pay and found_burn) raw_burn2 = self.nodes[0].createrawtransaction([], {self.nodes[0].getnewaddress():1, "burn":2}, 101, False, {"burn":"deadbeef"*8}) decode_burn2 = self.nodes[0].decoderawtransaction(raw_burn2) assert_equal(len(decode_burn2["vout"]), 2) found_pay = False found_burn = False for output in decode_burn2["vout"]: if output["scriptPubKey"]["asm"] == "OP_RETURN": found_burn = True if output["asset"] != "deadbeef"*8: raise Exception("Burn should have been deadbeef") if output["scriptPubKey"]["type"] == "scripthash": found_pay = True assert(found_pay and found_burn)
def run_test(self): node = self.nodes[0] # Generate 6 keys. rawkeys = [] pubkeys = [] for i in range(6): raw_key = CECKey() raw_key.set_secretbytes(('privkey%d' % i).encode('ascii')) rawkeys.append(raw_key) pubkeys = [CPubKey(key.get_pubkey()) for key in rawkeys] # Create a 4-of-6 multi-sig wallet with CLTV. height = 210 redeem_script = CScript( [CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP ] # CLTV (lock_time >= 210) + [OP_4] + pubkeys + [OP_6, OP_CHECKMULTISIG]) # multi-sig hex_redeem_script = bytes_to_hex_str(redeem_script) p2sh_address = script_to_p2sh(redeem_script, main=False) # Send 1 coin to the mult-sig wallet. txid = node.sendtoaddress(p2sh_address, 1.0) raw_tx = node.getrawtransaction(txid, True) try: node.importaddress(hex_redeem_script, 'cltv', True, True) except Exception: pass assert_equal( sig(node.getreceivedbyaddress(p2sh_address, 0) - Decimal(1.0)), 0) # Mine one block to confirm the transaction. node.generate(1) # block 201 assert_equal( sig(node.getreceivedbyaddress(p2sh_address, 1) - Decimal(1.0)), 0) # Try to spend the coin. addr_to = node.getnewaddress('') # (1) Find the UTXO for vout in raw_tx['vout']: if vout['scriptPubKey']['addresses'] == [p2sh_address]: vout_n = vout['n'] hex_script_pubkey = raw_tx['vout'][vout_n]['scriptPubKey']['hex'] value = raw_tx['vout'][vout_n]['value'] # (2) Create a tx inputs = [{ "txid": txid, "vout": vout_n, "scriptPubKey": hex_script_pubkey, "redeemScript": hex_redeem_script, "amount": value, }] outputs = {addr_to: 0.999} lock_time = height hex_spend_raw_tx = node.createrawtransaction(inputs, outputs, lock_time) hex_funding_raw_tx = node.getrawtransaction(txid, False) # (3) Try to sign the spending tx. tx0 = CTransaction() tx0.deserialize(io.BytesIO(hex_str_to_bytes(hex_funding_raw_tx))) tx1 = CTransaction() tx1.deserialize(io.BytesIO(hex_str_to_bytes(hex_spend_raw_tx))) self.sign_tx(tx1, tx0, vout_n, redeem_script, 0, rawkeys[:4]) # Sign with key[0:4] # Mine some blocks to pass the lock time. node.generate(10) # Spend the CLTV multi-sig coins. raw_tx1 = tx1.serialize() hex_raw_tx1 = bytes_to_hex_str(raw_tx1) node.sendrawtransaction(hex_raw_tx1) # Check the tx is accepted by mempool but not confirmed. assert_equal( sig(node.getreceivedbyaddress(addr_to, 0) - Decimal(0.999)), 0) assert_equal(sig(node.getreceivedbyaddress(addr_to, 1)), 0) # Mine a block to confirm the tx. node.generate(1) assert_equal( sig(node.getreceivedbyaddress(addr_to, 1) - Decimal(0.999)), 0)
def run_test(self): self.log.info('prepare some coins for multiple *rawtransaction commands') self.nodes[2].generate(1) self.sync_all() self.nodes[0].generate(101) self.sync_all() self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.5) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.0) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),5.0) self.sync_all() self.nodes[0].generate(5) self.sync_all() self.log.info('Test getrawtransaction on genesis block coinbase returns an error') block = self.nodes[0].getblock(self.nodes[0].getblockhash(0)) assert_raises_rpc_error(-5, "The genesis block coinbase is not considered an ordinary transaction", self.nodes[0].getrawtransaction, block['merkleroot']) self.log.info('Check parameter types and required parameters of createrawtransaction') # Test `createrawtransaction` required parameters assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction) assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, []) # Test `createrawtransaction` invalid extra parameters assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, [], {}, 0, False, 'foo') # Test `createrawtransaction` invalid `inputs` txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' assert_raises_rpc_error(-3, "Expected type array", self.nodes[0].createrawtransaction, 'foo', {}) assert_raises_rpc_error(-1, "JSON value is not an object as expected", self.nodes[0].createrawtransaction, ['foo'], {}) assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].createrawtransaction, [{}], {}) assert_raises_rpc_error(-8, "txid must be of length 64 (not 3, for 'foo')", self.nodes[0].createrawtransaction, [{'txid': 'foo'}], {}) assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", self.nodes[0].createrawtransaction, [{'txid': 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844'}], {}) assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid}], {}) assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 'foo'}], {}) assert_raises_rpc_error(-8, "Invalid parameter, vout must be positive", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': -1}], {}) assert_raises_rpc_error(-8, "Invalid parameter, sequence number is out of range", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 0, 'sequence': -1}], {}) # Test `createrawtransaction` invalid `outputs` address = self.nodes[0].getnewaddress() address2 = self.nodes[0].getnewaddress() assert_raises_rpc_error(-1, "JSON value is not an array as expected", self.nodes[0].createrawtransaction, [], 'foo') self.nodes[0].createrawtransaction(inputs=[], outputs={}) # Should not throw for backwards compatibility self.nodes[0].createrawtransaction(inputs=[], outputs=[]) assert_raises_rpc_error(-8, "Data must be hexadecimal string", self.nodes[0].createrawtransaction, [], {'data': 'foo'}) assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].createrawtransaction, [], {'foo': 0}) assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].createrawtransaction, [], {address: 'foo'}) assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].createrawtransaction, [], {address: -1}) assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)])) assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], [{address: 1}, {address: 1}]) assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], [{"data": 'aa'}, {"data": "bb"}]) assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], multidict([("data", 'aa'), ("data", "bb")])) assert_raises_rpc_error(-8, "Invalid parameter, key-value pair must contain exactly one key", self.nodes[0].createrawtransaction, [], [{'a': 1, 'b': 2}]) assert_raises_rpc_error(-8, "Invalid parameter, key-value pair not an object as expected", self.nodes[0].createrawtransaction, [], [['key-value pair1'], ['2']]) # Test `createrawtransaction` invalid `locktime` assert_raises_rpc_error(-3, "Expected type number", self.nodes[0].createrawtransaction, [], {}, 'foo') assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, -1) assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, 4294967296) # Test `createrawtransaction` invalid `replaceable` assert_raises_rpc_error(-3, "Expected type bool", self.nodes[0].createrawtransaction, [], {}, 0, 'foo') self.log.info('Check that createrawtransaction accepts an array and object as outputs') tx = CTransaction() # One output tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs={address: 99})))) assert_equal(len(tx.vout), 1) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}]), ) # Two outputs tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=OrderedDict([(address, 99), (address2, 99)]))))) assert_equal(len(tx.vout), 2) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {address2: 99}]), ) # Multiple mixed outputs tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([(address, 99), (address2, 99), ('data', '99')]))))) assert_equal(len(tx.vout), 3) assert_equal( bytes_to_hex_str(tx.serialize()), self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {address2: 99}, {'data': '99'}]), ) for type in ["bech32", "p2sh-segwit", "legacy"]: addr = self.nodes[0].getnewaddress("", type) addrinfo = self.nodes[0].getaddressinfo(addr) pubkey = addrinfo["scriptPubKey"] self.log.info('sendrawtransaction with missing prevtx info (%s)' %(type)) # Test `signrawtransactionwithwallet` invalid `prevtxs` inputs = [ {'txid' : txid, 'vout' : 3, 'sequence' : 1000}] outputs = { self.nodes[0].getnewaddress() : 1 } rawtx = self.nodes[0].createrawtransaction(inputs, outputs) prevtx = dict(txid=txid, scriptPubKey=pubkey, vout=3, amount=1) succ = self.nodes[0].signrawtransactionwithwallet(rawtx, [prevtx]) assert succ["complete"] if type == "legacy": del prevtx["amount"] succ = self.nodes[0].signrawtransactionwithwallet(rawtx, [prevtx]) assert succ["complete"] if type != "legacy": assert_raises_rpc_error(-3, "Missing amount", self.nodes[0].signrawtransactionwithwallet, rawtx, [ { "txid": txid, "scriptPubKey": pubkey, "vout": 3, } ]) assert_raises_rpc_error(-3, "Missing vout", self.nodes[0].signrawtransactionwithwallet, rawtx, [ { "txid": txid, "scriptPubKey": pubkey, "amount": 1, } ]) assert_raises_rpc_error(-3, "Missing txid", self.nodes[0].signrawtransactionwithwallet, rawtx, [ { "scriptPubKey": pubkey, "vout": 3, "amount": 1, } ]) assert_raises_rpc_error(-3, "Missing scriptPubKey", self.nodes[0].signrawtransactionwithwallet, rawtx, [ { "txid": txid, "vout": 3, "amount": 1 } ]) ######################################### # sendrawtransaction with missing input # ######################################### self.log.info('sendrawtransaction with missing input') inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1}] #won't exists outputs = { self.nodes[0].getnewaddress() : 4.998 } rawtx = self.nodes[2].createrawtransaction(inputs, outputs) rawtx = self.nodes[2].signrawtransactionwithwallet(rawtx) # This will raise an exception since there are missing inputs assert_raises_rpc_error(-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) ##################################### # getrawtransaction with block hash # ##################################### # make a tx by sending then generate 2 blocks; block1 has the tx in it tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1) block1, block2 = self.nodes[2].generate(2) self.sync_all() # We should be able to get the raw transaction by providing the correct block gottx = self.nodes[0].getrawtransaction(tx, True, block1) assert_equal(gottx['txid'], tx) assert_equal(gottx['in_active_chain'], True) # We should not have the 'in_active_chain' flag when we don't provide a block gottx = self.nodes[0].getrawtransaction(tx, True) assert_equal(gottx['txid'], tx) assert 'in_active_chain' not in gottx # We should not get the tx if we provide an unrelated block assert_raises_rpc_error(-5, "No such transaction found", self.nodes[0].getrawtransaction, tx, True, block2) # An invalid block hash should raise the correct errors assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].getrawtransaction, tx, True, True) assert_raises_rpc_error(-8, "parameter 3 must be of length 64 (not 6, for 'foobar')", self.nodes[0].getrawtransaction, tx, True, "foobar") assert_raises_rpc_error(-8, "parameter 3 must be of length 64 (not 8, for 'abcd1234')", self.nodes[0].getrawtransaction, tx, True, "abcd1234") assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].getrawtransaction, tx, True, "ZZZ0000000000000000000000000000000000000000000000000000000000000") assert_raises_rpc_error(-5, "Block hash not found", self.nodes[0].getrawtransaction, tx, True, "0000000000000000000000000000000000000000000000000000000000000000") # Undo the blocks and check in_active_chain self.nodes[0].invalidateblock(block1) gottx = self.nodes[0].getrawtransaction(txid=tx, verbose=True, blockhash=block1) assert_equal(gottx['in_active_chain'], False) self.nodes[0].reconsiderblock(block1) assert_equal(self.nodes[0].getbestblockhash(), block2) ######################### # RAW TX MULTISIG TESTS # ######################### # 2of2 test addr1 = self.nodes[2].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[2].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) # Tests for createmultisig and addmultisigaddress assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, ["01020304"]) self.nodes[0].createmultisig(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) # createmultisig can only take public keys assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 2, [addr1Obj['pubkey'], addr1]) # addmultisigaddress can take both pubkeys and addresses so long as they are in the wallet, which is tested here. mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr1])['address'] #use balance deltas instead of absolute values bal = self.nodes[2].getbalance() # send 1.2 BTC to msig adr txId = self.nodes[0].sendtoaddress(mSigObj, 1.2) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[2].getbalance(), bal+Decimal('1.20000000')) #node2 has both keys of the 2of2 ms addr., tx should affect the balance # 2of3 test from different nodes bal = self.nodes[2].getbalance() addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr3 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) addr3Obj = self.nodes[2].getaddressinfo(addr3) mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey']])['address'] txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) rawTx = self.nodes[0].decoderawtransaction(decTx['hex']) self.sync_all() self.nodes[0].generate(1) self.sync_all() #THIS IS AN INCOMPLETE FEATURE #NODE2 HAS TWO OF THREE KEY AND THE FUNDS SHOULD BE SPENDABLE AND COUNT AT BALANCE CALCULATION assert_equal(self.nodes[2].getbalance(), bal) #for now, assume the funds of a 2of3 multisig tx are not marked as spendable txDetails = self.nodes[0].gettransaction(txId, True) rawTx = self.nodes[0].decoderawtransaction(txDetails['hex']) vout = False for outpoint in rawTx['vout']: if outpoint['value'] == Decimal('2.20000000'): vout = outpoint break bal = self.nodes[0].getbalance() inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "amount" : vout['value']}] outputs = { self.nodes[0].getnewaddress() : 2.19 } rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned = self.nodes[1].signrawtransactionwithwallet(rawTx, inputs) assert_equal(rawTxPartialSigned['complete'], False) #node1 only has one key, can't comp. sign the tx rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx, inputs) assert_equal(rawTxSigned['complete'], True) #node2 can sign the tx compl., own two of three keys self.nodes[2].sendrawtransaction(rawTxSigned['hex']) rawTx = self.nodes[0].decoderawtransaction(rawTxSigned['hex']) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[0].getbalance(), bal+Decimal('50.00000000')+Decimal('2.19000000')) #block reward + tx # 2of2 test for combining transactions bal = self.nodes[2].getbalance() addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() addr1Obj = self.nodes[1].getaddressinfo(addr1) addr2Obj = self.nodes[2].getaddressinfo(addr2) self.nodes[1].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] mSigObjValid = self.nodes[2].getaddressinfo(mSigObj) txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) rawTx2 = self.nodes[0].decoderawtransaction(decTx['hex']) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[2].getbalance(), bal) # the funds of a 2of2 multisig tx should not be marked as spendable txDetails = self.nodes[0].gettransaction(txId, True) rawTx2 = self.nodes[0].decoderawtransaction(txDetails['hex']) vout = False for outpoint in rawTx2['vout']: if outpoint['value'] == Decimal('2.20000000'): vout = outpoint break bal = self.nodes[0].getbalance() inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "redeemScript" : mSigObjValid['hex'], "amount" : vout['value']}] outputs = { self.nodes[0].getnewaddress() : 2.19 } rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned1 = self.nodes[1].signrawtransactionwithwallet(rawTx2, inputs) self.log.debug(rawTxPartialSigned1) assert_equal(rawTxPartialSigned1['complete'], False) #node1 only has one key, can't comp. sign the tx rawTxPartialSigned2 = self.nodes[2].signrawtransactionwithwallet(rawTx2, inputs) self.log.debug(rawTxPartialSigned2) assert_equal(rawTxPartialSigned2['complete'], False) #node2 only has one key, can't comp. sign the tx rawTxComb = self.nodes[2].combinerawtransaction([rawTxPartialSigned1['hex'], rawTxPartialSigned2['hex']]) self.log.debug(rawTxComb) self.nodes[2].sendrawtransaction(rawTxComb) rawTx2 = self.nodes[0].decoderawtransaction(rawTxComb) self.sync_all() self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[0].getbalance(), bal+Decimal('50.00000000')+Decimal('2.19000000')) #block reward + tx # decoderawtransaction tests # witness transaction encrawtx = "010000000001010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f50500000000000102616100000000" decrawtx = self.nodes[0].decoderawtransaction(encrawtx, True) # decode as witness transaction assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000')) assert_raises_rpc_error(-22, 'TX decode failed', self.nodes[0].decoderawtransaction, encrawtx, False) # force decode as non-witness transaction # non-witness transaction encrawtx = "01000000010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f505000000000000000000" decrawtx = self.nodes[0].decoderawtransaction(encrawtx, False) # decode as non-witness transaction assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000')) # getrawtransaction tests # 1. valid parameters - only supply txid txHash = rawTx["hash"] assert_equal(self.nodes[0].getrawtransaction(txHash), rawTxSigned['hex']) # 2. valid parameters - supply txid and 0 for non-verbose assert_equal(self.nodes[0].getrawtransaction(txHash, 0), rawTxSigned['hex']) # 3. valid parameters - supply txid and False for non-verbose assert_equal(self.nodes[0].getrawtransaction(txHash, False), rawTxSigned['hex']) # 4. valid parameters - supply txid and 1 for verbose. # We only check the "hex" field of the output so we don't need to update this test every time the output format changes. assert_equal(self.nodes[0].getrawtransaction(txHash, 1)["hex"], rawTxSigned['hex']) # 5. valid parameters - supply txid and True for non-verbose assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex']) # 6. invalid parameters - supply txid and string "Flase" assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, "Flase") # 7. invalid parameters - supply txid and empty array assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, []) # 8. invalid parameters - supply txid and empty dict assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, {}) inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 1000}] outputs = { self.nodes[0].getnewaddress() : 1 } rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx= self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 1000) # 9. invalid parameters - sequence number out of range inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : -1}] outputs = { self.nodes[0].getnewaddress() : 1 } assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) # 10. invalid parameters - sequence number out of range inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967296}] outputs = { self.nodes[0].getnewaddress() : 1 } assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967294}] outputs = { self.nodes[0].getnewaddress() : 1 } rawtx = self.nodes[0].createrawtransaction(inputs, outputs) decrawtx= self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 4294967294) #################################### # TRANSACTION VERSION NUMBER TESTS # #################################### # Test the minimum transaction version number that fits in a signed 32-bit integer. tx = CTransaction() tx.nVersion = -0x80000000 rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['version'], -0x80000000) # Test the maximum transaction version number that fits in a signed 32-bit integer. tx = CTransaction() tx.nVersion = 0x7fffffff rawtx = ToHex(tx) decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['version'], 0x7fffffff)
def run_test(self): node = self.nodes[0] node.add_p2p_connection(P2PDataStore()) # Allocate as many UTXOs as are needed num_utxos = sum( len(test_case['sig_hash_types']) for test_case in TESTCASES if isinstance(test_case, dict)) value = int(SUBSIDY * 1_000_000) fee = 10_000 max_utxo_value = (value - fee) // num_utxos private_keys = [] public_keys = [] spendable_outputs = [] executed_scripts = [] utxo_idx = 0 # Prepare UTXOs for the tests below for test_case in TESTCASES: if test_case == 'ENABLE_REPLAY_PROTECTION': continue for _ in test_case['sig_hash_types']: private_key = ECKey() private_key.generate() private_keys.append(private_key) public_key = private_key.get_pubkey() public_keys.append(public_key) utxo_value = max_utxo_value - utxo_idx * 100 # deduct 100*i coins for unique amounts if test_case.get('opcodes', False): opcodes = test_case['opcodes'] redeem_script = CScript( opcodes + [public_key.get_bytes(), OP_CHECKSIG]) executed_scripts.append(redeem_script) utxo_script = CScript( [OP_HASH160, hash160(redeem_script), OP_EQUAL]) elif test_case.get('is_p2pk', False): utxo_script = CScript( [public_key.get_bytes(), OP_CHECKSIG]) executed_scripts.append(utxo_script) else: utxo_script = CScript([ OP_DUP, OP_HASH160, hash160(public_key.get_bytes()), OP_EQUALVERIFY, OP_CHECKSIG ]) executed_scripts.append(utxo_script) spendable_outputs.append(CTxOut(utxo_value, utxo_script)) utxo_idx += 1 anyonecanspend_address = node.decodescript('51')['p2sh'] burn_address = node.decodescript('00')['p2sh'] p2sh_script = CScript([OP_HASH160, bytes(20), OP_EQUAL]) node.generatetoaddress(1, anyonecanspend_address) node.generatetoaddress(100, burn_address) # Build and send fan-out transaction creating all the UTXOs block_hash = node.getblockhash(1) coin = int(node.getblock(block_hash)['tx'][0], 16) tx_fan_out = CTransaction() tx_fan_out.vin.append(CTxIn(COutPoint(coin, 1), CScript([b'\x51']))) tx_fan_out.vout = spendable_outputs tx_fan_out.rehash() node.p2p.send_txs_and_test([tx_fan_out], node) utxo_idx = 0 key_idx = 0 for test_case in TESTCASES: if test_case == 'ENABLE_REPLAY_PROTECTION': node.setmocktime(ACTIVATION_TIME) node.generatetoaddress(11, burn_address) continue # Build tx for this test, will broadcast later tx = CTransaction() num_inputs = len(test_case['sig_hash_types']) spent_outputs = spendable_outputs[:num_inputs] del spendable_outputs[:num_inputs] assert len(spent_outputs) == num_inputs total_input_amount = sum(output.nValue for output in spent_outputs) max_output_amount = (total_input_amount - fee) // test_case['outputs'] for i in range(test_case['outputs']): output_amount = max_output_amount - i * 77 output_script = CScript( [OP_HASH160, i.to_bytes(20, 'big'), OP_EQUAL]) tx.vout.append(CTxOut(output_amount, output_script)) for _ in test_case['sig_hash_types']: tx.vin.append( CTxIn(COutPoint(tx_fan_out.txid, utxo_idx), CScript())) utxo_idx += 1 # Keep unsigned tx for signrawtransactionwithkey below unsigned_tx = tx.serialize().hex() private_keys_wif = [] sign_inputs = [] # Make list of inputs for signrawtransactionwithkey for i, spent_output in enumerate(spent_outputs): sign_inputs.append({ 'txid': tx_fan_out.txid_hex, 'vout': key_idx + i, 'amount': Decimal(spent_output.nValue) / COIN, 'scriptPubKey': spent_output.scriptPubKey.hex(), }) for i, sig_hash_type in enumerate(test_case['sig_hash_types']): # Compute sighash for this input; we sign it manually using sign_ecdsa/sign_schnorr # and then broadcast the complete transaction sighash = SignatureHashLotus( tx_to=tx, spent_utxos=spent_outputs, sig_hash_type=sig_hash_type, input_index=i, executed_script_hash=hash256(executed_scripts[key_idx]), codeseparator_pos=test_case.get('codesep', 0xffff_ffff), ) if test_case.get('schnorr', False): signature = private_keys[key_idx].sign_schnorr(sighash) else: signature = private_keys[key_idx].sign_ecdsa(sighash) signature += bytes( [test_case.get('suffix', sig_hash_type & 0xff)]) # Build correct scriptSig if test_case.get('opcodes'): tx.vin[i].scriptSig = CScript( [signature, executed_scripts[key_idx]]) elif test_case.get('is_p2pk'): tx.vin[i].scriptSig = CScript([signature]) else: tx.vin[i].scriptSig = CScript( [signature, public_keys[key_idx].get_bytes()]) sig_hash_type_str = self.get_sig_hash_type_str(sig_hash_type) if sig_hash_type_str is not None and 'opcodes' not in test_case and 'error' not in test_case: # If we're a simple output type (P2PKH or P2KH) and aren't supposed to fail, # we sign using signrawtransactionwithkey and verify the transaction signed # the expected sighash. We won't broadcast it though. # Note: signrawtransactionwithkey will not sign using replay-protection. private_key_wif = bytes_to_wif( private_keys[key_idx].get_bytes()) raw_tx_signed = self.nodes[0].signrawtransactionwithkey( unsigned_tx, [private_key_wif], sign_inputs, sig_hash_type_str)['hex'] # Extract signature from signed signed_tx = CTransaction() signed_tx.deserialize( io.BytesIO(bytes.fromhex(raw_tx_signed))) sig = list(CScript(signed_tx.vin[i].scriptSig))[0] pubkey = private_keys[key_idx].get_pubkey() sighash = SignatureHashLotus( tx_to=tx, spent_utxos=spent_outputs, sig_hash_type=sig_hash_type & 0xff, input_index=i, executed_script_hash=hash256( executed_scripts[key_idx]), ) # Verify sig signs the above sighash and has the expected sighash type assert pubkey.verify_ecdsa(sig[:-1], sighash) assert sig[-1] == sig_hash_type & 0xff key_idx += 1 # Broadcast transaction and check success/failure tx.rehash() if 'error' not in test_case: node.p2p.send_txs_and_test([tx], node) else: node.p2p.send_txs_and_test([tx], node, success=False, reject_reason=test_case['error'])