def make_separator_tx(tx_to_spend, key_for_tx_to_spend, n_sgnings): """create Transaction with scriptPubKey in form of: <pk1> OP_CHECKSIGVERIFY OP_CODESEPARATOR <pk2> OP_CHECKSIGVERIFY OP_CODESEPARATOR ... <pk N_signings> OP_CHECKSIG """ tx = CTransaction() tx.vin.append(CTxIn(COutPoint(tx_to_spend.sha256, 0), b"", 0xffffffff)) keys = [] script_list = [] for i in range(n_sgnings - 1): k = CECKey() k.set_secretbytes(b"x" * (i + 1)) keys.append(k) script_list.extend( [k.get_pubkey(), OP_CHECKSIGVERIFY, OP_CODESEPARATOR]) k = CECKey() k.set_secretbytes(b"x" * n_sgnings) keys.append(k) script_list.extend([k.get_pubkey(), OP_CHECKSIG]) amount = tx_to_spend.vout[0].nValue - 2000 tx.vout.append(CTxOut(amount, CScript(script_list))) sighash = SignatureHashForkId(tx_to_spend.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, tx_to_spend.vout[0].nValue) tx.vin[0].scriptSig = CScript([ key_for_tx_to_spend.sign(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) ]) tx.rehash() return tx, keys
def create_tx(utxos, n_outputs, fee_delta=0): total_input = 0 tx = CTransaction() for utxo in utxos: tx.vin.append( CTxIn(COutPoint(utxo.tx.sha256, utxo.ndx), b"", 0xffffffff)) total_input += utxo.tx.vout[utxo.ndx].nValue amount_per_output = total_input // n_outputs - len( utxos) * 300 - n_outputs * 200 - 100 - fee_delta new_utxos = [] for i in range(n_outputs): k = new_key() new_utxos.append(UTXO(tx, i, k)) tx.vout.append( CTxOut(amount_per_output, CScript([k.get_pubkey(), OP_CHECKSIG]))) for input_ndx, (utxo, input) in enumerate(zip(utxos, tx.vin)): sighash = SignatureHashForkId(utxo.tx.vout[utxo.ndx].scriptPubKey, tx, input_ndx, SIGHASH_ALL | SIGHASH_FORKID, utxo.tx.vout[utxo.ndx].nValue) input.scriptSig = CScript([ utxo.key.sign(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) ]) tx.rehash() return tx, new_utxos
def make_unlock_script(tx, tx_to_spend): sighash = SignatureHashForkId(tx_to_spend.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, tx_to_spend.vout[0].nValue) sig = MaxMultiSigTest.THE_KEY.sign(sighash) + bytes( bytearray([SIGHASH_ALL | SIGHASH_FORKID])) return CScript([OP_0, sig])
def create_parent_tx(tx_to_spend, key_for_tx_to_spend, n_outputs, invalidity=None): tx = CTransaction() tx.vin.append(CTxIn(COutPoint(tx_to_spend.sha256, 0), b"", 0xffffffff)) keys = [] if invalidity == "low_fee": amount_per_output = tx_to_spend.vout[0].nValue // n_outputs - 10 else: amount_per_output = tx_to_spend.vout[0].nValue // n_outputs - 2000 for i in range(n_outputs): k = CECKey() keys.append(k) k.set_secretbytes(b"x" * (i + 1)) tx.vout.append( CTxOut(amount_per_output, CScript([k.get_pubkey(), OP_CHECKSIG]))) if invalidity == "bad_signature": sighash = b"\xff" * 32 else: sighash = SignatureHashForkId(tx_to_spend.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, tx_to_spend.vout[0].nValue) tx.vin[0].scriptSig = CScript([ key_for_tx_to_spend.sign(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) ]) tx.rehash() return tx, keys
def create_fund_and_spend_tx(spend, multi=False, sig='schnorr'): if multi: script = CScript([OP_1, public_key, OP_1, OP_CHECKMULTISIG]) else: script = CScript([public_key, OP_CHECKSIG]) # Fund transaction txfund = create_transaction(spend.tx, spend.n, b'', 50 * COIN, script) txfund.rehash() # Spend transaction txspend = CTransaction() txspend.vout.append(CTxOut(50 * COIN - 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, 50 * COIN) if sig == 'schnorr': txsig = schnorr.sign(privkeybytes, sighash) + hashbyte elif sig == 'ecdsa': txsig = private_key.sign(sighash) + hashbyte elif isinstance(sig, bytes): txsig = sig + hashbyte if multi: txspend.vin[0].scriptSig = CScript([b'', txsig]) else: txspend.vin[0].scriptSig = CScript([txsig]) txspend.rehash() return txfund, txspend
def new_transaction(utxokey, utxo, target_tx_size): ndx, tx_to_spend = utxo padding_size = target_tx_size while True: tx = CTransaction() tx.vin.append(CTxIn(COutPoint(tx_to_spend.sha256, ndx), b'')) tx.vin[0].scriptSig = b'' tx.vout.append( CTxOut(tx_to_spend.vout[0].nValue - 2 * target_tx_size, SIMPLE_OUTPUT_SCRIPT)) tx.vout.append( CTxOut(1, CScript([OP_FALSE, OP_RETURN] + [bytes(1) * padding_size]))) sighash = SignatureHashForkId(tx_to_spend.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, tx_to_spend.vout[0].nValue) sig = utxokey.sign(sighash) + bytes( bytearray([SIGHASH_ALL | SIGHASH_FORKID])) tx.vin[0].scriptSig = CScript([sig]) tx.rehash() diff = target_tx_size - len(tx.serialize()) if diff == 0: return tx padding_size += diff
def make_spend_txn(txtype, fund_txn_hash, fund_txn_num_vouts, out_value): # Create txn spend_tx = CTransaction() for idx in range(fund_txn_num_vouts): spend_tx.vin.append(CTxIn(COutPoint(fund_txn_hash, idx), b'')) sighash = SignatureHashForkId( redeem_script, spend_tx, idx, SIGHASH_ANYONECANPAY | SIGHASH_FORKID | SIGHASH_NONE, out_value) sig = key.sign(sighash) + bytes( bytearray([ SIGHASH_ANYONECANPAY | SIGHASH_FORKID | SIGHASH_NONE ])) spend_tx.vin[idx].scriptSig = CScript( [OP_0, sig, redeem_script]) # Standard transaction if TxType.standard == txtype: spend_tx.vout.append( CTxOut(out_value - 1000, CScript([OP_RETURN]))) # Non-standard transaction elif TxType.nonstandard == txtype: spend_tx.vout.append( CTxOut(out_value - 1000, CScript([OP_TRUE]))) spend_tx.rehash() return spend_tx
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_tx_with_script(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_ecdsa(sighash) + hashbyte txspend.vin[0].scriptSig = CScript([dummy, txsig]) txspend.rehash() return txspend
def prepare_for_test(self, height, label, coinbases, connections): transactions = [] n_generated_utxos = 0 while n_generated_utxos < self._NUMBER_OF_UTXOS_PER_HEIGHT: tx_to_spend = coinbases() tx = CTransaction() transactions.append(tx) tx.vin.append(CTxIn(COutPoint(tx_to_spend.sha256, 0), b'')) locking_key = self._UTXO_KEY cscript = CScript([locking_key.get_pubkey(), OP_CHECKSIG]) if locking_key else CScript([OP_TRUE]) for x in range(24): coinbaseoutput = CTxOut(2 * COIN, cscript) tx.vout.append(coinbaseoutput) n_generated_utxos += 1 if self.COINBASE_KEY: tx.rehash() sighash = SignatureHashForkId(tx_to_spend.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, tx_to_spend.vout[0].nValue) sig = self._coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) tx.vin[0].scriptSig = CScript([sig]) else: tx.vin[0].scriptSig = CScript([OP_TRUE]) tx.rehash() self.log.info(f"Created UTXO Tx {loghash(tx.hash)} with {n_generated_utxos} outputs") return transactions, None
def create_fund_and_spend_tx(spend, forkvalue=0): # Fund transaction script = CScript([public_key, OP_CHECKSIG]) txfund = create_tx_with_script(spend.tx, spend.n, b'', amount=50 * COIN - 1000, script_pub_key=script) txfund.rehash() # Spend transaction txspend = CTransaction() txspend.vout.append(CTxOut(50 * COIN - 2000, CScript([OP_TRUE]))) txspend.vin.append(CTxIn(COutPoint(txfund.sha256, 0), b'')) # Sign the transaction sighashtype = (forkvalue << 8) | SIGHASH_ALL | SIGHASH_FORKID sighash = SignatureHashForkId(script, txspend, 0, sighashtype, 50 * COIN - 1000) sig = private_key.sign_ecdsa(sighash) + \ bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) txspend.vin[0].scriptSig = CScript([sig]) txspend.rehash() return [txfund, txspend]
def sign_tx(self, tx, spend_tx, n): sighash = SignatureHashForkId(spend_tx.vout[n].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, spend_tx.vout[n].nValue) tx.vin[0].scriptSig = CScript([ self.prvkey.sign(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])), self.pubkey ])
def next_block(self, number, spend=None, additional_coinbase_value=0, script=None): if self.tip == None: base_block_hash = self.genesis_hash else: base_block_hash = self.tip.sha256 # First create the coinbase height = self.block_heights[base_block_hash] + 1 coinbase = create_coinbase(absoluteHeight=height, pubkey=self.coinbase_pubkey) coinbase.vout[0].nValue += additional_coinbase_value if (spend != None): coinbase.vout[0].nValue += spend.tx.vout[ spend.n].nValue - 1 # all but one satoshi to fees coinbase.rehash() block = create_block(base_block_hash, coinbase, self.block_time) if (spend != None): tx = CTransaction() tx.vin.append( CTxIn(COutPoint(spend.tx.sha256, spend.n), "", 0xffffffff)) # no signature yet # This copies the java comparison tool testing behavior: the first # txout has a garbage scriptPubKey, "to make sure we're not # pre-verifying too much" (?) tx.vout.append( CTxOut(0, CScript([random.randint(0, 255), height & 255]))) if script == None: tx.vout.append(CTxOut(1, CScript([OP_TRUE]))) else: tx.vout.append(CTxOut(1, script)) # Now sign it if necessary scriptSig = "" scriptPubKey = bytearray(spend.tx.vout[spend.n].scriptPubKey) if (scriptPubKey[0] == OP_TRUE): # looks like an anyone-can-spend scriptSig = CScript([OP_TRUE]) else: # We have to actually sign it sighash = SignatureHashForkId( spend.tx.vout[spend.n].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, spend.tx.vout[spend.n].nValue) scriptSig = CScript([ self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) ]) tx.vin[0].scriptSig = scriptSig # Now add the transaction to the block block = self.add_transactions_to_block(block, [tx]) block.solve() self.tip = block self.block_heights[block.sha256] = height self.block_time += 1 assert number not in self.blocks self.blocks[number] = block return block
def sign_tx(self, tx, spend_tx): scriptPubKey = bytearray(spend_tx.vout[0].scriptPubKey) if (scriptPubKey[0] == OP_TRUE): # an anyone-can-spend tx.vin[0].scriptSig = CScript() return sighash = SignatureHashForkId( spend_tx.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, spend_tx.vout[0].nValue) tx.vin[0].scriptSig = CScript( [self.coinbase_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID]))])
def sign_tx(self, tx, spend_tx, n, *, key): scriptPubKey = bytearray(spend_tx.vout[n].scriptPubKey) sighash = SignatureHashForkId(spend_tx.vout[n].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, spend_tx.vout[n].nValue) tx.vin[0].scriptSig = CScript([ key.sign(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) ])
def create_children_txs(parent_tx1, keys1, parent_tx2, keys2, invalidity=None): ret = [] for n, (txout1, key1, txout2, key2) in enumerate( zip(parent_tx1.vout, keys1, parent_tx2.vout, keys2)): amount1 = txout1.nValue if invalidity == "low_fee" else int( 0.99 * txout1.nValue) amount2 = txout2.nValue if invalidity == "low_fee" else int( 0.99 * txout2.nValue) amount = amount1 + amount2 tx = CTransaction() tx.vin.append(CTxIn(COutPoint(parent_tx1.sha256, n), b"", 0xffffffff)) tx.vin.append(CTxIn(COutPoint(parent_tx2.sha256, n), b"", 0xffffffff)) k = CECKey() k.set_secretbytes(b"x" * (n + 1)) tx.vout.append(CTxOut(amount, CScript([k.get_pubkey(), OP_CHECKSIG]))) tx.calc_sha256() sighash1 = SignatureHashForkId(parent_tx1.vout[n].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, parent_tx1.vout[n].nValue) tx.vin[0].scriptSig = CScript([ key1.sign(sighash1) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) ]) if invalidity == "bad_signature": sighash2 = b"\xff" * 32 else: sighash2 = SignatureHashForkId(parent_tx2.vout[n].scriptPubKey, tx, 1, SIGHASH_ALL | SIGHASH_FORKID, parent_tx2.vout[n].nValue) tx.vin[1].scriptSig = CScript([ key2.sign(sighash2) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) ]) tx.rehash() ret.append(tx) return ret
def spend_p2sh_tx(output_script=CScript([OP_TRUE])): # Create the transaction spent_p2sh_tx = CTransaction() spent_p2sh_tx.vin.append(CTxIn(COutPoint(p2sh_tx.sha256, 0), b'')) spent_p2sh_tx.vout.append(CTxOut(1, output_script)) # Sign the transaction using the redeem script sighash = SignatureHashForkId( redeem_script, spent_p2sh_tx, 0, SIGHASH_ALL | SIGHASH_FORKID, p2sh_tx.vout[0].nValue) sig = private_key.sign(sighash) + \ bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) spent_p2sh_tx.vin[0].scriptSig = CScript([sig, redeem_script]) spent_p2sh_tx.rehash() return spent_p2sh_tx
def make_unlock_modified00(tx, tx_to_spend): sighash = SignatureHashForkId(tx_to_spend.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, tx_to_spend.vout[0].nValue) sig = HandleTxsModified00Node.THE_KEY.sign(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) return CScript([sig]) class HandleTxsDefaultNode(GenesisHeightBasedSimpleTestsCase): ARGS = GenesisHeightBasedSimpleTestsCase.ARGS + ['-banscore=1000000', '-whitelist=127.0.0.1', '-acceptnonstdtxn=0', '-acceptnonstdoutputs=1'] NAME = "Reject nonstandard transactions and accept p2sh transactions before Genesis. Accept nonstandard and reject p2sh transactions after Genesis" THE_KEY = make_key() P2PK_LOCKING_SCRIPT = CScript([THE_KEY.get_pubkey(), OP_CHECKSIG]) TEST_PRE_GENESIS_STANDARD_TX = [ SimpleTestDefinition("PRE-GENESIS", P2PK_LOCKING_SCRIPT, "PRE-GENESIS", make_unlock_default) ] TEST_PRE_GENESIS_NONSTANDARD_TX = [ SimpleTestDefinition("PRE-GENESIS", P2PK_LOCKING_SCRIPT, "PRE-GENESIS", make_unlock_default, test_tx_locking_script=CScript([OP_TRUE]), p2p_reject_reason = b'scriptpubkey') ] TEST_PRE_GENESIS_P2SH_TX = [ SimpleTestDefinition("PRE-GENESIS", P2PK_LOCKING_SCRIPT, "PRE-GENESIS", make_unlock_default, test_tx_locking_script=CScript([OP_HASH160, hash160(CScript([OP_TRUE])), OP_EQUAL])) ] TEST_GENESIS_STANDARD_TX = [ SimpleTestDefinition("GENESIS", P2PK_LOCKING_SCRIPT, "GENESIS", make_unlock_default) ] TEST_GENESIS_NONSTANDARD_TX = [ SimpleTestDefinition("GENESIS", P2PK_LOCKING_SCRIPT, "GENESIS", make_unlock_default, test_tx_locking_script=CScript([OP_TRUE])) ] # P2SH transaction will be rejected from p2p, but not rejected as part of the block TEST_GENESIS_P2SH_TX = [ SimpleTestDefinition("GENESIS", P2PK_LOCKING_SCRIPT, "GENESIS", make_unlock_default, test_tx_locking_script=CScript([OP_HASH160, hash160(CScript([OP_TRUE])), OP_EQUAL]), p2p_reject_reason=b'bad-txns-vout-p2sh') ] TESTS = TEST_PRE_GENESIS_STANDARD_TX + TEST_PRE_GENESIS_NONSTANDARD_TX + TEST_PRE_GENESIS_P2SH_TX + TEST_GENESIS_STANDARD_TX + TEST_GENESIS_NONSTANDARD_TX + TEST_GENESIS_P2SH_TX
def spend_p2sh_tx(p2sh_tx_to_spend, output_script=CScript([OP_TRUE])): # Create the transaction spent_p2sh_tx = CTransaction() spent_p2sh_tx.vin.append( CTxIn(COutPoint(p2sh_tx_to_spend.sha256, 0), b'')) spent_p2sh_tx.vout.append(CTxOut(1000, output_script)) # Sign the transaction using the redeem script sighash = SignatureHashForkId(redeem_script, spent_p2sh_tx, 0, SIGHASH_ALL | SIGHASH_FORKID, p2sh_tx_to_spend.vout[0].nValue) sig = self.coinbase_key.sign(sighash) + bytes( bytearray([SIGHASH_ALL | SIGHASH_FORKID])) spent_p2sh_tx.vin[0].scriptSig = CScript([sig, redeem_script]) assert len(spent_p2sh_tx.vin[0].scriptSig ) >= 198, "needs to pass input sigchecks limit" spent_p2sh_tx.rehash() return spent_p2sh_tx
def spend_tx_to_data(tx_to_spend, key_for_tx_to_spend): "Create and send block with coinbase, returns conbase (tx, key) tuple" tx = CTransaction() tx.vin.append(CTxIn(COutPoint(tx_to_spend.sha256, 0), b"", 0xffffffff)) amount = tx_to_spend.vout[0].nValue - 2000 tx.vout.append(CTxOut(amount, OP_TRUE_OP_RETURN_SCRIPT)) sighash = SignatureHashForkId(tx_to_spend.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, tx_to_spend.vout[0].nValue) tx.vin[0].scriptSig = CScript([ key_for_tx_to_spend.sign(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) ]) tx.rehash() return tx
def create_fund_and_spend_tx(multi=False, sig='schnorr'): spendfrom = spendable_outputs.pop() vout = 1 if multi: script = CScript([OP_1, public_key, OP_1, OP_CHECKMULTISIG]) else: script = CScript([public_key, OP_CHECKSIG]) value = spendfrom.vout[vout].nValue # Fund transaction txfund = create_tx_with_script(spendfrom, vout, b'', amount=value, script_pub_key=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.txid, 0), b'')) # Sign the transaction sighashtype = SIGHASH_ALL | SIGHASH_FORKID hashbyte = bytes([sighashtype & 0xff]) sighash = SignatureHashForkId(script, txspend, 0, sighashtype, value) if sig == 'schnorr': txsig = private_key.sign_schnorr(sighash) + hashbyte elif sig == 'ecdsa': txsig = private_key.sign_ecdsa(sighash) + hashbyte elif isinstance(sig, bytes): txsig = sig + hashbyte if multi: txspend.vin[0].scriptSig = CScript([b'', txsig]) else: txspend.vin[0].scriptSig = CScript([txsig]) txspend.rehash() return txspend
def create_fund_and_activation_specific_spending_tx(spend, pre_fork_only): # Creates 2 transactions: # 1) txfund: create outputs to be used by txspend. Must be valid pre-fork. # 2) txspend: spending transaction that is specific to the activation # being used and can be pre-fork-only or post-fork-only, depending on the # function parameter. # This specific implementation uses the replay protection mechanism to # create transactions that are only valid before or after the fork. # Generate a key pair to test private_key = ECKey() private_key.generate() public_key = private_key.get_pubkey().get_bytes() # Fund transaction script = CScript([public_key, OP_CHECKSIG]) txfund = create_tx_with_script(spend.tx, spend.n, b'', amount=int(SUBSIDY * COIN), script_pub_key=script) txfund.rehash() # Activation specific spending tx txspend = CTransaction() txspend.vout.append(CTxOut(int(SUBSIDY * COIN) - 1000, CScript([OP_TRUE]))) txspend.vin.append(CTxIn(COutPoint(txfund.txid, 0), b'')) # Sign the transaction # Use forkvalues that create pre-fork-only or post-fork-only # transactions. forkvalue = 0 if pre_fork_only else 0xffdead sighashtype = (forkvalue << 8) | SIGHASH_ALL | SIGHASH_FORKID sighash = SignatureHashForkId(script, txspend, 0, sighashtype, int(SUBSIDY * COIN)) sig = private_key.sign_ecdsa(sighash) + \ bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) txspend.vin[0].scriptSig = CScript([sig]) txspend.rehash() return txfund, txspend
def create_tx(self, outpoints, noutput, feerate, make_long_eval_script=False): """creates p2pk transaction always using the same key (created in constructor), if make_long_eval_script is set we are prepending long evaluating script to the locking script """ pre_script = MemepoolAcceptingTransactionsDuringReorg.long_eval_script if make_long_eval_script else [] tx = CTransaction() total_input = 0 for parent_tx, n in outpoints: tx.vin.append( CTxIn(COutPoint(parent_tx.sha256, n), CScript([b"0" * 72]), 0xffffffff)) total_input += parent_tx.vout[n].nValue for _ in range(noutput): tx.vout.append( CTxOut(total_input // noutput, CScript(pre_script + [self.public_key, OP_CHECKSIG]))) tx.rehash() tx_size = len(tx.serialize()) fee_per_output = int(tx_size * feerate // noutput) for output in tx.vout: output.nValue -= fee_per_output for input, (parent_tx, n) in zip(tx.vin, outpoints): sighash = SignatureHashForkId(parent_tx.vout[n].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, parent_tx.vout[n].nValue) input.scriptSig = CScript([ self.private_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) ]) tx.rehash() return tx
def create_fund_and_spend_tx(node, spend, multi=False): privkeybytes = b"Schnorr!" * 4 private_key = CECKey() private_key.set_secretbytes(privkeybytes) # get uncompressed public key serialization public_key = private_key.get_pubkey() if multi: script = CScript([OP_1, public_key, OP_1, OP_CHECKMULTISIG]) else: script = CScript([public_key, OP_CHECKSIG]) # Fund transaction prevtx = FromHex(CTransaction(), node.getrawtransaction(spend['txid'])) prevtx.rehash() fee = 500 fund_amount = 50 * COIN - fee txfund = create_transaction(prevtx, spend['vout'], b'', fund_amount, script) txfund = FromHex(CTransaction(), node.signrawtransactionwithwallet(ToHex(txfund))["hex"]) txfund.rehash() # Spend transaction txspend = CTransaction() txspend.vout.append(CTxOut(fund_amount - 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, fund_amount) txsig = schnorr.sign(privkeybytes, sighash) + hashbyte if multi: txspend.vin[0].scriptSig = CScript([b'', txsig]) else: txspend.vin[0].scriptSig = CScript([txsig]) txspend.rehash() return txfund, txspend
def create_fund_and_spend_tx(scriptsigextra, redeemextra) -> Tuple[CTransaction, CTransaction]: spendfrom = spendable_txns.pop() redeem_script = CScript(redeemextra + [OP_1, public_key, OP_1, OP_CHECKMULTISIG]) script_pubkey = CScript([OP_HASH160, hash160(redeem_script), OP_EQUAL]) value = spendfrom.vout[0].nValue value1 = value - 500 # Fund transaction txfund = create_tx_with_script(spendfrom, 0, b'', value1, script_pubkey) txfund.rehash() p2sh = script_to_p2sh(redeem_script) self.log.info(f"scriptPubKey {script_pubkey!r}") self.log.info(f"redeemScript {redeem_script!r} -> p2sh address {p2sh}") # Spend transaction value2 = value1 - 500 txspend = CTransaction() txspend.vout.append( CTxOut(value2, 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( redeem_script, txspend, 0, sighashtype, value1) txsig = schnorr.sign(privkeybytes, sighash) + hashbyte dummy = OP_1 # Required for 1-of-1 schnorr sig txspend.vin[0].scriptSig = ss = CScript([dummy, txsig] + scriptsigextra + [redeem_script]) self.log.info(f"scriptSig: {ss!r}") txspend.rehash() return txfund, txspend
def create_fund_and_spend_tx(node, spendfrom, dummy): privkeybytes = b"Schnorr!" * 4 private_key = CECKey() private_key.set_secretbytes(privkeybytes) # get uncompressed public key serialization public_key = private_key.get_pubkey() script = CScript([OP_1, public_key, OP_1, OP_CHECKMULTISIG]) value = spendfrom.vout[0].nValue value -= 1000 # Fund transaction txfund = create_transaction(spendfrom, 0, b'', value, script) txfund = FromHex(CTransaction(), node.signrawtransactionwithwallet(ToHex(txfund))["hex"]) 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) txsig = schnorr.sign(privkeybytes, sighash) + hashbyte txspend.vin[0].scriptSig = CScript([dummy, txsig]) txspend.rehash() return txfund, txspend
def spend_separator_tx(tx_sep_tx, keys_for_sep_tx): """spends Transaction with scriptPubKey in form of: <pk1> OP_CHECKSIGVERIFY OP_CODESEPARATOR <pk2> OP_CHECKSIGVERIFY OP_CODESEPARATOR ... <pk N_signings> OP_CHECKSIG """ tx = CTransaction() tx.vin.append(CTxIn(COutPoint(tx_sep_tx.sha256, 0), b"", 0xffffffff)) k = CECKey() k.set_secretbytes(b"horsebattery") amount = tx_sep_tx.vout[0].nValue - 2000 script_lists = [[]] for item in list(tx_sep_tx.vout[0].scriptPubKey): for l in script_lists: l.append(item) if item == OP_CODESEPARATOR: script_lists.append([]) tx.vout.append(CTxOut(amount, CScript([k.get_pubkey(), OP_CHECKSIG]))) flags = bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) sign_list = [] for sc, key in zip(script_lists, keys_for_sep_tx): sighash = SignatureHashForkId(CScript(sc), tx, 0, SIGHASH_ALL | SIGHASH_FORKID, tx_sep_tx.vout[0].nValue) sign_list.append(key.sign(sighash) + flags) tx.vin[0].scriptSig = CScript(reversed(sign_list)) tx.rehash() return tx, k
def run_test(self): node = self.nodes[0] node.add_p2p_connection(P2PDataStore()) # Allocate as many UTXOs as are needed num_utxos = sum( len(tx_case['inputs']) for tx_case in TX_CASES if isinstance(tx_case, dict)) value = int(SUBSIDY * 1_000_000) fee = 10_000 pubkey_bytes = bytes.fromhex( '020000000000000000000000000000000000000000000000000000000000000001' ) pubkey = ECPubKey() pubkey.set(pubkey_bytes) max_utxo_value = (value - fee) // num_utxos spendable_outputs = [] utxo_idx = 0 # Prepare UTXOs for the tests below for tx_case in TX_CASES: if tx_case == 'ENABLE_REPLAY_PROTECTION': continue for tree, leaf_idx, _ in tx_case['inputs']: utxo_value = max_utxo_value - utxo_idx * 100 # deduct 100*i coins for unique amounts tree_result = taproot_tree_helper(tree) merkle_root = tree_result['hash'] tweak_hash = TaggedHash("TapTweak", pubkey_bytes + merkle_root) commitment = pubkey.add(tweak_hash) ops = [OP_SCRIPTTYPE, OP_1, commitment.get_bytes()] script_case = tree_result['items'][leaf_idx]['leaf'][ 'script_case'] if script_case.get('state', False): ops.append(script_case['state']) utxo_script = CScript(ops) 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 for tx_case in TX_CASES: if tx_case == 'ENABLE_REPLAY_PROTECTION': node.setmocktime(ACTIVATION_TIME) node.generatetoaddress(11, burn_address) continue num_inputs = len(tx_case['inputs']) num_outputs = tx_case['outputs'] # Build tx for this test, will broadcast later tx = CTransaction() 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) // num_outputs for i in range(num_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 range(num_inputs): tx.vin.append( CTxIn(COutPoint(tx_fan_out.txid, utxo_idx), CScript())) utxo_idx += 1 for input_idx, input_case in enumerate(tx_case['inputs']): tree, leaf_idx, sig_hash_types = input_case tree_result = taproot_tree_helper(tree) result_item = tree_result['items'][leaf_idx] leaf = result_item['leaf'] script_case = leaf['script_case'] exec_script = CScript(script_case['script']) keys = script_case.get('keys', []) assert len(sig_hash_types) == len(keys) sigs = [] for sig_hash_type, key in zip(sig_hash_types, keys): if sig_hash_type & SIGHASH_LOTUS == SIGHASH_LOTUS: sighash = SignatureHashLotus( tx_to=tx, spent_utxos=spent_outputs, sig_hash_type=sig_hash_type, input_index=input_idx, executed_script_hash=leaf['tapleaf_hash'], codeseparator_pos=script_case.get( 'codesep', 0xffff_ffff), ) elif sig_hash_type & SIGHASH_FORKID: sighash = SignatureHashForkId( exec_script, tx, input_idx, sig_hash_type, spent_outputs[input_idx].nValue, ) else: raise NotImplemented private_key = ECKey() private_key.set(key, True) if script_case.get('schnorr', False): signature = private_key.sign_schnorr(sighash) else: signature = private_key.sign_ecdsa(sighash) signature += bytes( [tx_case.get('suffix', sig_hash_type & 0xff)]) sigs.append(signature) control_block = bytearray(pubkey_bytes) control_block[0] = 0xc0 control_block[0] |= int(pubkey_bytes[0] == 0x03) control_block += result_item['path'] tx.vin[input_idx].scriptSig = CScript( script_case['script_inputs'] + sigs + [exec_script, control_block]) # Broadcast transaction and check success/failure tx.rehash() if 'error' not in tx_case: node.p2p.send_txs_and_test([tx], node) else: node.p2p.send_txs_and_test([tx], node, success=False, reject_reason=tx_case['error'])
def make_transactions(self, txtype, num_txns, stxn_vin_size, create_double_spends=False): key = CECKey() key.set_secretbytes(b"horsebattery") key.set_compressed(True) # Each coin being spent will always result in at least 14 expensive ECDSA checks. # 0x7f03 33 OP_NUM2BIN creates a valid non-zero compressed pubkey. redeem_script = CScript([ OP_1, key.get_pubkey(), 0x7f03, 33, OP_NUM2BIN, OP_DUP, OP_2DUP, OP_2DUP, OP_2DUP, OP_3DUP, OP_3DUP, OP_15, OP_CHECKMULTISIG ]) # Calculate how many found txns are needed to create a required spend money txns (num_txns) # - a fund txns are of type 1 - N (N=vouts_size_per_fund_txn) # - a spend money txns are of type M-1 (M inputs & 1 output) def estimate_fund_txns_number(num_txns, vouts_size_per_fund_txn): fund_txns_num = 1 if num_txns >= vouts_size_per_fund_txn: if num_txns % vouts_size_per_fund_txn == 0: fund_txns_num = num_txns // vouts_size_per_fund_txn else: fund_txns_num = num_txns // vouts_size_per_fund_txn + 1 return fund_txns_num * vouts_size_per_fund_txn # Create funding transactions that will provide funds for other transcations def make_fund_txn(node, out_value, num_vout_txns): # Create fund txn ftx = CTransaction() for i in range(num_vout_txns): ftx.vout.append( CTxOut( out_value, CScript([OP_HASH160, hash160(redeem_script), OP_EQUAL]))) # fund the transcation: ftxHex = node.fundrawtransaction( ToHex(ftx), {'changePosition': len(ftx.vout)})['hex'] ftxHex = node.signrawtransaction(ftxHex)['hex'] ftx = FromHex(CTransaction(), ftxHex) ftx.rehash() return ftx, ftxHex # Create a spend txn def make_spend_txn(txtype, fund_txn_hash, fund_txn_num_vouts, out_value): # Create txn spend_tx = CTransaction() for idx in range(fund_txn_num_vouts): spend_tx.vin.append(CTxIn(COutPoint(fund_txn_hash, idx), b'')) sighash = SignatureHashForkId( redeem_script, spend_tx, idx, SIGHASH_ANYONECANPAY | SIGHASH_FORKID | SIGHASH_NONE, out_value) sig = key.sign(sighash) + bytes( bytearray([ SIGHASH_ANYONECANPAY | SIGHASH_FORKID | SIGHASH_NONE ])) spend_tx.vin[idx].scriptSig = CScript( [OP_0, sig, redeem_script]) # Standard transaction if TxType.standard == txtype: spend_tx.vout.append( CTxOut(out_value - 1000, CScript([OP_RETURN]))) # Non-standard transaction elif TxType.nonstandard == txtype: spend_tx.vout.append( CTxOut(out_value - 1000, CScript([OP_TRUE]))) spend_tx.rehash() return spend_tx # # Generate some blocks to have enough spendable coins # node = self.nodes[0] node.generate(101) # # Estimate a number of required fund txns # out_value = 2000 # Number of outputs in each fund txn fund_txn_num_vouts = stxn_vin_size fund_txns_num = estimate_fund_txns_number(num_txns, fund_txn_num_vouts) # # Create and send fund txns to the mempool # fund_txns = [] for i in range(fund_txns_num): ftx, ftxHex = make_fund_txn(node, out_value, fund_txn_num_vouts) node.sendrawtransaction(ftxHex) fund_txns.append(ftx) # Ensure that mempool is empty to avoid 'too-long-mempool-chain' errors in next test node.generate(1) # # Create spend transactions. # txtype_to_create = txtype spend_txs = [] for i in range(len(fund_txns)): # If standard and non-standard txns are required then create equal (in size) sets. if TxType.std_and_nonstd == txtype: if i % 2: txtype_to_create = TxType.standard else: txtype_to_create = TxType.nonstandard # Create a spend money txn with fund_txn_num_vouts number of inputs. spend_tx = make_spend_txn(txtype_to_create, fund_txns[i].sha256, fund_txn_num_vouts, out_value) # Create double spend txns if required if create_double_spends and len(spend_txs) < num_txns // 2: # The first half of the array are double spend txns spend_tx.vin.append( CTxIn( COutPoint(fund_txns[len(fund_txns) - i - 1].sha256, 0), b'')) sighash = SignatureHashForkId( redeem_script, spend_tx, stxn_vin_size, SIGHASH_ANYONECANPAY | SIGHASH_FORKID | SIGHASH_NONE, out_value) sig = key.sign(sighash) + bytes( bytearray([ SIGHASH_ANYONECANPAY | SIGHASH_FORKID | SIGHASH_NONE ])) spend_tx.vin[stxn_vin_size].scriptSig = CScript( [OP_0, sig, redeem_script]) spend_tx.rehash() spend_txs.append(spend_tx) return spend_txs
def make_unlock_modified10(tx, tx_to_spend): sighash = SignatureHashForkId(tx_to_spend.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, tx_to_spend.vout[0].nValue) sig = HandleTxsModified10Node.THE_KEY.sign(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) return CScript([sig])
def run_test(self): self.bootstrap_p2p() # Add one p2p connection to the node self.block_heights = {} self.coinbase_key = CECKey() self.coinbase_key.set_secretbytes(b"horsebattery") self.coinbase_pubkey = self.coinbase_key.get_pubkey() self.tip = None self.blocks = {} self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 self.spendable_outputs = [] # Create a new block b0 = self.next_block(0) self.save_spendable_output() self.sync_blocks([b0]) # Allow the block to mature blocks = [] for i in range(129): blocks.append(self.next_block(5000 + i)) self.save_spendable_output() self.sync_blocks(blocks) # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(33): out.append(self.get_spendable_output()) # Start by building a block on top. # setup -> b13 (0) b13 = self.next_block(13, spend=out[0]) self.save_spendable_output() self.sync_blocks([b13]) # Add a block with MAX_BLOCK_SIGOPS_PER_MB and one with one more sigop # setup -> b13 (0) -> b15 (5) -> b16 (6) self.log.info("Accept a block with lots of checksigs") lots_of_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB - 1)) self.move_tip(13) b15 = self.next_block(15, spend=out[5], script=lots_of_checksigs) self.save_spendable_output() self.sync_blocks([b15], True) self.log.info("Reject a block with too many checksigs") too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB)) b16 = self.next_block(16, spend=out[6], script=too_many_checksigs) self.sync_blocks([b16], success=False, reject_reason='bad-blk-sigops', reconnect=True) self.move_tip(15) # ... skipped feature_block tests ... # b31 - b35 - check sigops of OP_CHECKMULTISIG / OP_CHECKMULTISIGVERIFY / OP_CHECKSIGVERIFY # # setup -> ... b15 (5) -> b31 (8) -> b33 (9) -> b35 (10) # \-> b36 (11) # \-> b34 (10) # \-> b32 (9) # # MULTISIG: each op code counts as 20 sigops. To create the edge case, # pack another 19 sigops at the end. self.log.info( "Accept a block with the max number of OP_CHECKMULTISIG sigops") lots_of_multisigs = CScript([OP_CHECKMULTISIG] * ((MAX_BLOCK_SIGOPS_PER_MB - 1) // 20) + [OP_CHECKSIG] * 19) b31 = self.next_block(31, spend=out[8], script=lots_of_multisigs) assert_equal(get_legacy_sigopcount_block(b31), MAX_BLOCK_SIGOPS_PER_MB) self.sync_blocks([b31], True) self.save_spendable_output() # this goes over the limit because the coinbase has one sigop self.log.info("Reject a block with too many OP_CHECKMULTISIG sigops") too_many_multisigs = CScript([OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS_PER_MB // 20)) b32 = self.next_block(32, spend=out[9], script=too_many_multisigs) assert_equal(get_legacy_sigopcount_block(b32), MAX_BLOCK_SIGOPS_PER_MB + 1) self.sync_blocks([b32], success=False, reject_reason='bad-blk-sigops', reconnect=True) # CHECKMULTISIGVERIFY self.log.info( "Accept a block with the max number of OP_CHECKMULTISIGVERIFY sigops" ) self.move_tip(31) lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS_PER_MB - 1) // 20) + [OP_CHECKSIG] * 19) b33 = self.next_block(33, spend=out[9], script=lots_of_multisigs) self.sync_blocks([b33], True) self.save_spendable_output() self.log.info( "Reject a block with too many OP_CHECKMULTISIGVERIFY sigops") too_many_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB // 20)) b34 = self.next_block(34, spend=out[10], script=too_many_multisigs) self.sync_blocks([b34], success=False, reject_reason='bad-blk-sigops', reconnect=True) # CHECKSIGVERIFY self.log.info( "Accept a block with the max number of OP_CHECKSIGVERIFY sigops") self.move_tip(33) lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB - 1)) b35 = self.next_block(35, spend=out[10], script=lots_of_checksigs) self.sync_blocks([b35], True) self.save_spendable_output() self.log.info("Reject a block with too many OP_CHECKSIGVERIFY sigops") too_many_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB)) b36 = self.next_block(36, spend=out[11], script=too_many_checksigs) self.sync_blocks([b36], success=False, reject_reason='bad-blk-sigops', reconnect=True) # ... skipped feature_block tests ... # Check P2SH SigOp counting # # # ... -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b41 (12) # \-> b40 (12) # # b39 - create some P2SH outputs that will require 6 sigops to spend: # # redeem_script = COINBASE_PUBKEY, (OP_2DUP+OP_CHECKSIGVERIFY) * 5, OP_CHECKSIG # p2sh_script = OP_HASH160, ripemd160(sha256(script)), OP_EQUAL # self.log.info("Check P2SH SIGOPS are correctly counted") self.move_tip(35) b39 = self.next_block(39) b39_outputs = 0 b39_sigops_per_output = 6 # Build the redeem script, hash it, use hash to create the p2sh script redeem_script = CScript([self.coinbase_pubkey] + [OP_2DUP, OP_CHECKSIGVERIFY] * 5 + [OP_CHECKSIG]) redeem_script_hash = hash160(redeem_script) p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL]) # Create a transaction that spends one satoshi to the p2sh_script, the rest to OP_TRUE # This must be signed because it is spending a coinbase spend = out[11] tx = self.create_tx(spend, 0, 1, p2sh_script) tx.vout.append(CTxOut(spend.vout[0].nValue - 1, CScript([OP_TRUE]))) self.sign_tx(tx, spend) tx.rehash() b39 = self.update_block(39, [tx]) b39_outputs += 1 # Until block is full, add tx's with 1 satoshi to p2sh_script, the rest # to OP_TRUE tx_new = None tx_last = tx tx_last_n = len(tx.vout) - 1 total_size = len(b39.serialize()) while (total_size < LEGACY_MAX_BLOCK_SIZE): tx_new = self.create_tx(tx_last, tx_last_n, 1, p2sh_script) tx_new.vout.append( CTxOut(tx_last.vout[tx_last_n].nValue - 1, CScript([OP_TRUE]))) tx_new.rehash() total_size += len(tx_new.serialize()) if total_size >= LEGACY_MAX_BLOCK_SIZE: break b39.vtx.append(tx_new) # add tx to block tx_last = tx_new tx_last_n = len(tx_new.vout) - 1 b39_outputs += 1 b39 = self.update_block(39, []) self.sync_blocks([b39], True) self.save_spendable_output() # Test sigops in P2SH redeem scripts # # b40 creates 3333 tx's spending the 6-sigop P2SH outputs from b39 for a total of 19998 sigops. # The first tx has one sigop and then at the end we add 2 more to put us just over the max. # # b41 does the same, less one, so it has the maximum sigops permitted. # self.log.info("Reject a block with too many P2SH sigops") self.move_tip(39) b40 = self.next_block(40, spend=out[12]) sigops = get_legacy_sigopcount_block(b40) numTxs = (MAX_BLOCK_SIGOPS_PER_MB - sigops) // b39_sigops_per_output assert_equal(numTxs <= b39_outputs, True) lastOutpoint = COutPoint(b40.vtx[1].sha256, 0) lastAmount = b40.vtx[1].vout[0].nValue new_txs = [] for i in range(1, numTxs + 1): tx = CTransaction() tx.vout.append(CTxOut(1, CScript([OP_TRUE]))) tx.vin.append(CTxIn(lastOutpoint, b'')) # second input is corresponding P2SH output from b39 tx.vin.append(CTxIn(COutPoint(b39.vtx[i].sha256, 0), b'')) # Note: must pass the redeem_script (not p2sh_script) to the # signature hash function sighash = SignatureHashForkId(redeem_script, tx, 1, SIGHASH_ALL | SIGHASH_FORKID, lastAmount) sig = self.coinbase_key.sign(sighash) + bytes( bytearray([SIGHASH_ALL | SIGHASH_FORKID])) scriptSig = CScript([sig, redeem_script]) tx.vin[1].scriptSig = scriptSig pad_tx(tx) tx.rehash() new_txs.append(tx) lastOutpoint = COutPoint(tx.sha256, 0) lastAmount = tx.vout[0].nValue b40_sigops_to_fill = MAX_BLOCK_SIGOPS_PER_MB - \ (numTxs * b39_sigops_per_output + sigops) + 1 tx = CTransaction() tx.vin.append(CTxIn(lastOutpoint, b'')) tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b40_sigops_to_fill))) pad_tx(tx) tx.rehash() new_txs.append(tx) self.update_block(40, new_txs) self.sync_blocks([b40], success=False, reject_reason='bad-blk-sigops', reconnect=True) # same as b40, but one less sigop self.log.info("Accept a block with the max number of P2SH sigops") self.move_tip(39) b41 = self.next_block(41, spend=None) self.update_block(41, [b40tx for b40tx in b40.vtx[1:] if b40tx != tx]) b41_sigops_to_fill = b40_sigops_to_fill - 1 tx = CTransaction() tx.vin.append(CTxIn(lastOutpoint, b'')) tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b41_sigops_to_fill))) pad_tx(tx) self.update_block(41, [tx]) self.sync_blocks([b41], True) # ... skipped feature_block tests ... b72 = self.next_block(72) self.save_spendable_output() self.sync_blocks([b72]) # Test some invalid scripts and MAX_BLOCK_SIGOPS_PER_MB # # ..... -> b72 # \-> b** (22) # # b73 - tx with excessive sigops that are placed after an excessively large script element. # The purpose of the test is to make sure those sigops are counted. # # script is a bytearray of size 20,526 # # bytearray[0-19,998] : OP_CHECKSIG # bytearray[19,999] : OP_PUSHDATA4 # bytearray[20,000-20,003]: 521 (max_script_element_size+1, in little-endian format) # bytearray[20,004-20,525]: unread data (script_element) # bytearray[20,526] : OP_CHECKSIG (this puts us over the limit) self.log.info( "Reject a block containing too many sigops after a large script element" ) self.move_tip(72) b73 = self.next_block(73) size = MAX_BLOCK_SIGOPS_PER_MB - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 + 1 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS_PER_MB - 1] = int("4e", 16) # OP_PUSHDATA4 element_size = MAX_SCRIPT_ELEMENT_SIZE + 1 a[MAX_BLOCK_SIGOPS_PER_MB] = element_size % 256 a[MAX_BLOCK_SIGOPS_PER_MB + 1] = element_size // 256 a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0 a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0 tx = self.create_and_sign_transaction(out[22], 1, CScript(a)) b73 = self.update_block(73, [tx]) assert_equal(get_legacy_sigopcount_block(b73), MAX_BLOCK_SIGOPS_PER_MB + 1) self.sync_blocks([b73], success=False, reject_reason='bad-blk-sigops', reconnect=True) # b74/75 - if we push an invalid script element, all prevous sigops are counted, # but sigops after the element are not counted. # # The invalid script element is that the push_data indicates that # there will be a large amount of data (0xffffff bytes), but we only # provide a much smaller number. These bytes are CHECKSIGS so they would # cause b75 to fail for excessive sigops, if those bytes were counted. # # b74 fails because we put MAX_BLOCK_SIGOPS_PER_MB+1 before the element # b75 succeeds because we put MAX_BLOCK_SIGOPS_PER_MB before the # element self.log.info( "Check sigops are counted correctly after an invalid script element" ) self.move_tip(72) b74 = self.next_block(74) size = MAX_BLOCK_SIGOPS_PER_MB - 1 + \ MAX_SCRIPT_ELEMENT_SIZE + 42 # total = 20,561 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS_PER_MB] = 0x4e a[MAX_BLOCK_SIGOPS_PER_MB + 1] = 0xfe a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0xff a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0xff a[MAX_BLOCK_SIGOPS_PER_MB + 4] = 0xff tx = self.create_and_sign_transaction(out[22], 1, CScript(a)) b74 = self.update_block(74, [tx]) self.sync_blocks([b74], success=False, reject_reason='bad-blk-sigops', reconnect=True) self.move_tip(72) b75 = self.next_block(75) size = MAX_BLOCK_SIGOPS_PER_MB - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS_PER_MB - 1] = 0x4e a[MAX_BLOCK_SIGOPS_PER_MB] = 0xff a[MAX_BLOCK_SIGOPS_PER_MB + 1] = 0xff a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0xff a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0xff tx = self.create_and_sign_transaction(out[22], 1, CScript(a)) b75 = self.update_block(75, [tx]) self.sync_blocks([b75], True) self.save_spendable_output() # Check that if we push an element filled with CHECKSIGs, they are not # counted self.move_tip(75) b76 = self.next_block(76) size = MAX_BLOCK_SIGOPS_PER_MB - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 a = bytearray([OP_CHECKSIG] * size) # PUSHDATA4, but leave the following bytes as just checksigs a[MAX_BLOCK_SIGOPS_PER_MB - 1] = 0x4e tx = self.create_and_sign_transaction(out[23], 1, CScript(a)) b76 = self.update_block(76, [tx]) self.sync_blocks([b76], True) self.save_spendable_output()