def create_segwit_fund_and_spend_tx(spend, case0=False): if not case0: # Spending from a P2SH-P2WPKH coin, # txhash:a45698363249312f8d3d93676aa714be59b0bd758e62fa054fb1ea6218480691 redeem_script0 = bytearray.fromhex( '0014fcf9969ce1c98a135ed293719721fb69f0b686cb') # Spending from a P2SH-P2WSH coin, # txhash:6b536caf727ccd02c395a1d00b752098ec96e8ec46c96bee8582be6b5060fa2f redeem_script1 = bytearray.fromhex( '0020fc8b08ed636cb23afcb425ff260b3abd03380a2333b54cfa5d51ac52d803baf4' ) else: redeem_script0 = bytearray.fromhex('51020000') redeem_script1 = bytearray.fromhex('53020080') redeem_scripts = [redeem_script0, redeem_script1] # Fund transaction to segwit addresses txfund = CTransaction() txfund.vin = [CTxIn(COutPoint(spend.tx.sha256, spend.n))] amount = (50 * COIN - 1000) // len(redeem_scripts) for redeem_script in redeem_scripts: txfund.vout.append( CTxOut( amount, CScript([OP_HASH160, hash160(redeem_script), OP_EQUAL]))) txfund.rehash() # Segwit spending transaction # We'll test if a node that checks for standardness accepts this # txn. It should fail exclusively because of the restriction in # the scriptSig (non clean stack..), so all other characteristcs # must pass standardness checks. For this reason, we create # standard P2SH outputs. txspend = CTransaction() for i in range(len(redeem_scripts)): txspend.vin.append( CTxIn(COutPoint(txfund.sha256, i), CScript([redeem_scripts[i]]))) txspend.vout = [ CTxOut( 50 * COIN - 2000, CScript( [OP_HASH160, hash160(CScript([OP_TRUE])), OP_EQUAL])) ] txspend.rehash() return txfund, txspend
def create_fund_and_spend_tx(dummy=OP_0, sigtype='ecdsa'): spendfrom = spendable_outputs.pop() script = CScript([OP_1, public_key, OP_1, OP_CHECKMULTISIG]) value = spendfrom.vout[0].nValue # Fund transaction txfund = create_transaction(spendfrom, 0, b'', value, script) txfund.rehash() fundings.append(txfund) # Spend transaction txspend = CTransaction() txspend.vout.append(CTxOut(value - 1000, CScript([OP_TRUE]))) txspend.vin.append(CTxIn(COutPoint(txfund.sha256, 0), b'')) # Sign the transaction sighashtype = SIGHASH_ALL | SIGHASH_FORKID hashbyte = bytes([sighashtype & 0xff]) sighash = SignatureHashForkId(script, txspend, 0, sighashtype, value) if sigtype == 'schnorr': txsig = schnorr.sign(privkeybytes, sighash) + hashbyte elif sigtype == 'ecdsa': txsig = private_key.sign(sighash) + hashbyte txspend.vin[0].scriptSig = CScript([dummy, txsig]) txspend.rehash() return txspend
def create_fund_and_spend_tx(): spendfrom = spendable_outputs.pop() script = CScript([OP_ADD]) value = spendfrom.vout[0].nValue # Fund transaction txfund = create_transaction(spendfrom, 0, b'', value, script) pad_tx(txfund) txfund.rehash() fundings.append(txfund) # Spend transaction txspend = CTransaction() txspend.vout.append(CTxOut(value - 1000, CScript([OP_TRUE]))) txspend.vin.append(CTxIn(COutPoint(txfund.sha256, 0), b'')) # Sign the transaction txspend.vin[0].scriptSig = CScript( b'\x01\x01\x51') # PUSH1(0x01) OP_1 pad_tx(txspend) txspend.rehash() return txspend
def make_spend(sigcheckcount): # Add a funding tx to fundings, and return a tx spending that using # scriptsig. logging.debug("Gen tx with {} sigchecks.".format(sigcheckcount)) def get_script_with_sigcheck(count): return CScript([cds_message, cds_pubkey] + (count - 1) * [OP_3DUP, OP_CHECKDATASIGVERIFY] + [OP_CHECKDATASIG]) # get funds locked with OP_1 sourcetx = self.spendable_outputs.popleft() # make funding that forwards to scriptpubkey last_sigcheck_count = ((sigcheckcount - 1) % 30) + 1 fundtx = create_transaction( sourcetx, get_script_with_sigcheck(last_sigcheck_count)) fill_sigcheck_script = get_script_with_sigcheck(30) remaining_sigcheck = sigcheckcount while remaining_sigcheck > 30: fundtx.vout[0].nValue -= 1000 fundtx.vout.append(CTxOut(100, bytes(fill_sigcheck_script))) remaining_sigcheck -= 30 fundtx.rehash() fundings.append(fundtx) # make the spending scriptsig = CScript([cds_signature]) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(fundtx.sha256, 1), scriptsig)) input_index = 2 remaining_sigcheck = sigcheckcount while remaining_sigcheck > 30: tx.vin.append( CTxIn(COutPoint(fundtx.sha256, input_index), scriptsig)) remaining_sigcheck -= 30 input_index += 1 tx.vout.append(CTxOut(0, CScript([OP_RETURN]))) pad_tx(tx) tx.rehash() return tx
def create_transaction(spendfrom, custom_script, amount=None): # Fund and sign a transaction to a given output. # spendfrom should be a CTransaction with first output to OP_TRUE. # custom output will go on position 1, after position 0 which will be # OP_TRUE (so it can be reused). customout = CTxOut(0, bytes(custom_script)) # set output amount to required dust if not given customout.nValue = amount or (len(customout.serialize()) + 148) * 3 ctx = CTransaction() ctx.vin.append(CTxIn(COutPoint(spendfrom.sha256, 0), bytes([OP_TRUE]))) ctx.vout.append(CTxOut(0, bytes([OP_TRUE]))) ctx.vout.append(customout) pad_tx(ctx) fee = len(ctx.serialize()) ctx.vout[0].nValue = spendfrom.vout[0].nValue - customout.nValue - fee ctx.rehash() return ctx
def create_cashaccount_tx(self, n, spend, name, address): fee = 500 tx = create_transaction( FromHex(CTransaction(), n.getrawtransaction(spend['txid'])), spend['vout'], b"", spend['satoshi'] - fee) _, _, keyhash = decode_addr(address) name = bytes(name, 'ascii') keyhash = DATATYPE_KEYHASH + keyhash cashaccount_script = CScript( [OP_RETURN, CASHACCONT_PREFIX, name, keyhash]) tx.vout.append(CTxOut(0, cashaccount_script)) tx.rehash() return n.signrawtransaction(ToHex(tx))['hex']
def make_spend(scriptpubkey, scriptsig): # Add a funding tx to fundings, and return a tx spending that using # scriptsig. logging.debug( "Gen tx with locking script {} unlocking script {} .".format( scriptpubkey.hex(), scriptsig.hex())) # get funds locked with OP_1 sourcetx = self.spendable_outputs.popleft() # make funding that forwards to scriptpubkey fundtx = create_transaction(sourcetx, scriptpubkey) fundings.append(fundtx) # make the spending tx = CTransaction() tx.vin.append(CTxIn(COutPoint(fundtx.sha256, 1), scriptsig)) tx.vout.append(CTxOut(0, CScript([OP_RETURN]))) pad_tx(tx) tx.rehash() return tx
def build_block(self, parent, transactions=(), nTime=None, cbextrascript=None): """Make a new block with an OP_1 coinbase output. Requires parent to have its height registered.""" parent.calc_sha256() block_height = self.block_heights[parent.sha256] + 1 block_time = (parent.nTime + 1) if nTime is None else nTime block = create_block(parent.sha256, create_coinbase(block_height), block_time) if cbextrascript is not None: block.vtx[0].vout.append(CTxOut(0, cbextrascript)) block.vtx[0].rehash() block.vtx.extend(transactions) make_conform_to_ctor(block) block.hashMerkleRoot = block.calc_merkle_root() block.solve() self.block_heights[block.sha256] = block_height return block
def create_fund_and_spend_tx(): spend_from = spendable_outputs.pop() value = spend_from.vout[0].nValue # Reversed data data = bytes.fromhex('0123456789abcdef') rev_data = bytes(reversed(data)) # Lockscript: provide a bytestring that reverses to X script = CScript([OP_REVERSEBYTES, rev_data, OP_EQUAL]) # Fund transaction: REVERSEBYTES <reversed(x)> EQUAL tx_fund = create_tx_with_script(spend_from, 0, b'', value, script) tx_fund.rehash() # Spend transaction: <x> tx_spend = CTransaction() tx_spend.vout.append( CTxOut(value - 1000, CScript([b'x' * 100, OP_RETURN]))) tx_spend.vin.append(CTxIn(COutPoint(tx_fund.sha256, 0), b'')) tx_spend.vin[0].scriptSig = CScript([data]) tx_spend.rehash() return tx_spend, tx_fund
def run_test(self): logging.info("Initializing test directory "+self.options.tmpdir) node = self.nodes[0] self.bootstrap_p2p() tip = self.get_best_block(node) logging.info("Create some blocks with OP_1 coinbase for spending.") blocks = [] for _ in range(10): tip = self.build_block(tip) blocks.append(tip) self.p2p.send_blocks_and_test(blocks, node, success=True) spendable_outputs = [block.vtx[0] for block in blocks] logging.info("Mature the blocks and get out of IBD.") node.generate(100) tip = self.get_best_block(node) logging.info( "Set up spending transactions to test and mine the funding transactions.") # Generate a key pair privkeybytes = b"xyzxyzhh" * 4 private_key = CECKey() private_key.set_secretbytes(privkeybytes) # get uncompressed public key serialization public_key = private_key.get_pubkey() # Create funding/spending transaction pair spend_from = spendable_outputs.pop() value = spend_from.vout[0].nValue # Reversed data data = bytes.fromhex('0123456789abcdef') rev_data = bytes(reversed(data)) # Lockscript: provide a bytestring that reverses to X script = CScript([OP_REVERSEBYTES, rev_data, OP_EQUAL]) # Fund transaction: REVERSEBYTES <reversed(x)> EQUAL tx_reversebytes_fund = create_tx_with_script(spend_from, 0, b'', value, script) tx_reversebytes_fund.rehash() # Spend transaction: <x> tx_reversebytes_spend = CTransaction() tx_reversebytes_spend.vout.append(CTxOut(value - 1000, CScript([b'x' * 100, OP_RETURN]))) tx_reversebytes_spend.vin.append(CTxIn(COutPoint(tx_reversebytes_fund.sha256, 0), b'')) tx_reversebytes_spend.vin[0].scriptSig = CScript([data]) tx_reversebytes_spend.rehash() # Mine funding transaction into block. Pre-upgrade output scripts can have # OP_REVERSEBYTES and still be fully valid, but they cannot spend it. tip = self.build_block(tip, [tx_reversebytes_fund]) self.p2p.send_blocks_and_test([tip], node) logging.info( "Submitting a new OP_REVERSEBYTES tx via net, and mining it in a block") # Send OP_REVERSEBYTES tx self.p2p.send_txs_and_test([tx_reversebytes_spend], node) # Verify OP_REVERSEBYTES tx is in mempool waitFor(10, lambda: set(node.getrawmempool()) == {tx_reversebytes_spend.hash}) # Mine OP_REVERSEBYTES tx into block tip = self.build_block(tip, [tx_reversebytes_spend]) self.p2p.send_blocks_and_test([tip], node)
def run_test(self): node = self.nodes[0] self.pynode = P2PDataStore() self.connection = NodeConn('127.0.0.1', p2p_port(0), node, self.pynode) self.pynode.add_connection(self.connection) NetworkThread().start() self.pynode.wait_for_verack() # Get out of IBD node.generate(1) tip = self.getbestblock(node) logging.info("Create some blocks with OP_1 coinbase for spending.") blocks = [] for _ in range(20): tip = self.build_block(tip) blocks.append(tip) self.pynode.send_blocks_and_test(blocks, node, success=True) self.spendable_outputs = deque(block.vtx[0] for block in blocks) logging.info("Mature the blocks.") node.generate(100) tip = self.getbestblock(node) # To make compact and fast-to-verify transactions, we'll use # CHECKDATASIG over and over with the same data. # (Using the same stuff over and over again means we get to hit the # node's signature cache and don't need to make new signatures every # time.) cds_message = b'' # r=1 and s=1 ecdsa, the minimum values. cds_signature = bytes.fromhex('3006020101020101') # Recovered pubkey cds_pubkey = bytes.fromhex( '03089b476b570d66fad5a20ae6188ebbaf793a4c2a228c65f3d79ee8111d56c932' ) def minefunding2(n): """ Mine a block with a bunch of outputs that are very dense sigchecks when spent (2 sigchecks each); return the inputs that can be used to spend. """ cds_scriptpubkey = CScript([ cds_message, cds_pubkey, OP_3DUP, OP_CHECKDATASIGVERIFY, OP_CHECKDATASIGVERIFY ]) # The scriptsig is carefully padded to have size 26, which is the # shortest allowed for 2 sigchecks for mempool admission. # The resulting inputs have size 67 bytes, 33.5 bytes/sigcheck. cds_scriptsig = CScript([b'x' * 16, cds_signature]) assert_equal(len(cds_scriptsig), 26) logging.debug( "Gen {} with locking script {} unlocking script {} .".format( n, cds_scriptpubkey.hex(), cds_scriptsig.hex())) tx = self.spendable_outputs.popleft() usable_inputs = [] txes = [] for i in range(n): tx = create_transaction(tx, cds_scriptpubkey, bytes([OP_TRUE]) if i == 0 else b"") txes.append(tx) usable_inputs.append( CTxIn(COutPoint(tx.sha256, 1), cds_scriptsig)) newtip = self.build_block(tip, txes) self.pynode.send_blocks_and_test([newtip], node, timeout=10) return usable_inputs, newtip logging.info("Funding special coins that have high sigchecks") # mine 5000 funded outputs (10000 sigchecks) # will be used pre-activation and post-activation usable_inputs, tip = minefunding2(5000) # assemble them into 50 txes with 100 inputs each (200 sigchecks) submittxes_1 = [] while len(usable_inputs) >= 100: tx = CTransaction() tx.vin = [usable_inputs.pop() for _ in range(100)] tx.vout = [CTxOut(0, CScript([OP_RETURN]))] tx.rehash() submittxes_1.append(tx) # mine 5000 funded outputs (10000 sigchecks) # will be used post-activation usable_inputs, tip = minefunding2(5000) # assemble them into 50 txes with 100 inputs each (200 sigchecks) submittxes_2 = [] while len(usable_inputs) >= 100: tx = CTransaction() tx.vin = [usable_inputs.pop() for _ in range(100)] tx.vout = [CTxOut(0, CScript([OP_RETURN]))] tx.rehash() submittxes_2.append(tx) # Check high sigcheck transactions logging.info("Create transaction that have high sigchecks") fundings = [] def make_spend(sigcheckcount): # Add a funding tx to fundings, and return a tx spending that using # scriptsig. logging.debug("Gen tx with {} sigchecks.".format(sigcheckcount)) def get_script_with_sigcheck(count): return CScript([cds_message, cds_pubkey] + (count - 1) * [OP_3DUP, OP_CHECKDATASIGVERIFY] + [OP_CHECKDATASIG]) # get funds locked with OP_1 sourcetx = self.spendable_outputs.popleft() # make funding that forwards to scriptpubkey last_sigcheck_count = ((sigcheckcount - 1) % 30) + 1 fundtx = create_transaction( sourcetx, get_script_with_sigcheck(last_sigcheck_count)) fill_sigcheck_script = get_script_with_sigcheck(30) remaining_sigcheck = sigcheckcount while remaining_sigcheck > 30: fundtx.vout[0].nValue -= 1000 fundtx.vout.append(CTxOut(100, bytes(fill_sigcheck_script))) remaining_sigcheck -= 30 fundtx.rehash() fundings.append(fundtx) # make the spending scriptsig = CScript([cds_signature]) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(fundtx.sha256, 1), scriptsig)) input_index = 2 remaining_sigcheck = sigcheckcount while remaining_sigcheck > 30: tx.vin.append( CTxIn(COutPoint(fundtx.sha256, input_index), scriptsig)) remaining_sigcheck -= 30 input_index += 1 tx.vout.append(CTxOut(0, CScript([OP_RETURN]))) pad_tx(tx) tx.rehash() return tx # Create transactions with many sigchecks. good_tx = make_spend(MAX_TX_SIGCHECK) bad_tx = make_spend(MAX_TX_SIGCHECK + 1) tip = self.build_block(tip, fundings) self.pynode.send_blocks_and_test([tip], node) # Both tx are accepted before the activation. pre_activation_sigcheck_block = self.build_block( tip, [good_tx, bad_tx]) self.pynode.send_blocks_and_test([pre_activation_sigcheck_block], node) node.invalidateblock(pre_activation_sigcheck_block.hash) # after block is invalidated these tx are put back into the mempool. Test uses them later so evict. waitFor(10, lambda: node.getmempoolinfo()["size"] == 2) node.evicttransaction(good_tx.hash) node.evicttransaction(bad_tx.hash) # Activation tests logging.info("Approach to just before upgrade activation") # Move our clock to the uprade time so we will accept such # future-timestamped blocks. node.setmocktime(SIGCHECKS_ACTIVATION_TIME + 10) # Mine six blocks with timestamp starting at # SIGCHECKS_ACTIVATION_TIME-1 blocks = [] for i in range(-1, 5): tip = self.build_block(tip, nTime=SIGCHECKS_ACTIVATION_TIME + i) blocks.append(tip) self.pynode.send_blocks_and_test(blocks, node, timeout=TIMEOUT) assert_equal(node.getblockchaininfo()['mediantime'], SIGCHECKS_ACTIVATION_TIME - 1) logging.info( "The next block will activate, but the activation block itself must follow old rules" ) # Send the 50 txes and get the node to mine as many as possible (it should do all) # The node is happy mining and validating a 10000 sigcheck block before # activation. self.pynode.send_txs_and_test(submittxes_1, node, timeout=TIMEOUT) [blockhash] = node.generate(1) assert_equal(set(node.getblock(blockhash, 1)["tx"][1:]), {t.hash for t in submittxes_1}) # We have activated, but let's invalidate that. assert_equal(node.getblockchaininfo()['mediantime'], SIGCHECKS_ACTIVATION_TIME) node.invalidateblock(blockhash) # Try again manually and invalidate that too goodblock = self.build_block(tip, submittxes_1) self.pynode.send_blocks_and_test([goodblock], node, timeout=TIMEOUT) node.invalidateblock(goodblock.hash) # All transactions should be back in mempool: validation is very slow in debug build waitFor( 60, lambda: set(node.getrawmempool()) == {t.hash for t in submittxes_1}) logging.info("Mine the activation block itself") tip = self.build_block(tip) self.pynode.send_blocks_and_test([tip], node, timeout=TIMEOUT) logging.info("We have activated!") assert_equal(node.getblockchaininfo()['mediantime'], SIGCHECKS_ACTIVATION_TIME) # All transactions get re-evaluated to count sigchecks, so wait for them waitFor( 60, lambda: set(node.getrawmempool()) == {t.hash for t in submittxes_1}) logging.info( "Try a block with a transaction going over the limit (limit: {})". format(MAX_TX_SIGCHECK)) bad_tx_block = self.build_block(tip, [bad_tx]) check_for_ban_on_rejected_block( self.pynode, node, bad_tx_block, reject_reason=BLOCK_SIGCHECKS_BAD_TX_SIGCHECKS) logging.info( "Try a block with a transaction just under the limit (limit: {})". format(MAX_TX_SIGCHECK)) good_tx_block = self.build_block(tip, [good_tx]) self.pynode.send_blocks_and_test([good_tx_block], node, timeout=TIMEOUT) node.invalidateblock(good_tx_block.hash) # save this tip for later # ~ upgrade_block = tip # Transactions still in pool: waitFor( 60, lambda: set(node.getrawmempool()) == {t.hash for t in submittxes_1}) logging.info( "Try sending 10000-sigcheck blocks after activation (limit: {})". format(MAXBLOCKSIZE // BLOCK_MAXBYTES_MAXSIGCHECKS_RATIO)) # Send block with same txes we just tried before activation badblock = self.build_block(tip, submittxes_1) check_for_ban_on_rejected_block( self.pynode, node, badblock, reject_reason="Invalid block due to bad-blk-sigchecks", expect_ban=True) logging.info( "There are too many sigchecks in mempool to mine in a single block. Make sure the node won't mine invalid blocks. Num tx: %s" % str(node.getmempoolinfo())) blk = node.generate(1) tip = self.getbestblock(node) # only 39 txes got mined. assert_equal(len(node.getrawmempool()), 11) logging.info( "Try sending 10000-sigcheck block with fresh transactions after activation (limit: {})" .format(MAXBLOCKSIZE // BLOCK_MAXBYTES_MAXSIGCHECKS_RATIO)) # Note: in the following tests we'll be bumping timestamp in order # to bypass any kind of 'bad block' cache on the node, and get a # fresh evaluation each time. # Try another block with 10000 sigchecks but all fresh transactions badblock = self.build_block(tip, submittxes_2, nTime=SIGCHECKS_ACTIVATION_TIME + 5) check_for_ban_on_rejected_block( self.pynode, node, badblock, reject_reason=BLOCK_SIGCHECKS_BAD_BLOCK_SIGCHECKS) # Send the same txes again with different block hash. Currently we don't # cache valid transactions in invalid blocks so nothing changes. badblock = self.build_block(tip, submittxes_2, nTime=SIGCHECKS_ACTIVATION_TIME + 6) check_for_ban_on_rejected_block( self.pynode, node, badblock, reject_reason=BLOCK_SIGCHECKS_BAD_BLOCK_SIGCHECKS) # Put all the txes in mempool, in order to get them cached: self.pynode.send_txs_and_test(submittxes_2, node, timeout=TIMEOUT) # Send them again, the node still doesn't like it. But the log # error message has now changed because the txes failed from cache. badblock = self.build_block(tip, submittxes_2, nTime=SIGCHECKS_ACTIVATION_TIME + 7) check_for_ban_on_rejected_block( self.pynode, node, badblock, reject_reason=BLOCK_SIGCHECKS_BAD_BLOCK_SIGCHECKS) logging.info( "Try sending 8000-sigcheck block after activation (limit: {})". format(MAXBLOCKSIZE // BLOCK_MAXBYTES_MAXSIGCHECKS_RATIO)) badblock = self.build_block(tip, submittxes_2[:40], nTime=SIGCHECKS_ACTIVATION_TIME + 5) check_for_ban_on_rejected_block( self.pynode, node, badblock, reject_reason=BLOCK_SIGCHECKS_BAD_BLOCK_SIGCHECKS) # redundant, but just to mirror the following test... node.set("consensus.maxBlockSigChecks=%d" % MAX_BLOCK_SIGCHECKS) logging.info( "Bump the excessiveblocksize limit by 1 byte, and send another block with same txes (new sigchecks limit: {})" .format((MAXBLOCKSIZE + 1) // BLOCK_MAXBYTES_MAXSIGCHECKS_RATIO)) node.set("consensus.maxBlockSigChecks=%d" % (MAX_BLOCK_SIGCHECKS + 1)) tip = self.build_block(tip, submittxes_2[:40], nTime=SIGCHECKS_ACTIVATION_TIME + 6) # It should succeed now since limit should be 8000. self.pynode.send_blocks_and_test([tip], node, timeout=TIMEOUT)