def create_and_sign_tx(self, node, in_count, out_count, in_size, out_size, min_confirmations, spam): utx = self.create_utxos_value100000(node, in_count, in_size, min_confirmations) sum_values_sats = 0 assert (len(utx.vout) == in_count) tx = CTransaction() for i in range(in_count): u = utx.vout[i] sum_values_sats = sum_values_sats + u.nValue tx.vin.append( CTxIn( COutPoint( uint256_from_str(hex_str_to_bytes(utx.hash)[::-1]), i), b'')) flags = bytes( bytearray( [SIGHASH_NONE | SIGHASH_ANYONECANPAY | SIGHASH_FORKID])) adjust = len( bytes_to_hex_str(flags)) + 1 # plus one for one OP_HOP while True: scriptSig = CScript( [bytes(bytearray([OP_NOP]) * (spam - adjust)) + flags]) tx.vin[-1].scriptSig = scriptSig if len(scriptSig) > spam: adjust = adjust + 1 continue elif len(scriptSig) < spam: adjust = adjust - 1 continue break assert (len(tx.vin) == in_count) x = out_size // out_count x_rest = out_size % out_count check_size = 0 for i in range(out_count): if i == out_count - 1: x = x_rest + x scriptPubKey = CScript([OP_NOP] * (x - 1) + [OP_TRUE]) check_size = check_size + len(scriptPubKey) amount = sum_values_sats // (out_count - i) tx.vout.append(CTxOut(amount, scriptPubKey)) sum_values_sats = sum_values_sats - amount assert (check_size == out_size) tx.rehash() return ToHex(tx)
def gbt_submitblock(self, nu5_active): node = self.node mempool_tx_list = node.getrawmempool() gbt = node.getblocktemplate() # make sure no transactions were left out (or added) assert_equal(len(mempool_tx_list), len(gbt['transactions'])) assert_equal(set(mempool_tx_list), set([tx['hash'] for tx in gbt['transactions']])) prevhash = int(gbt['previousblockhash'], 16) nTime = gbt['mintime'] nBits = int(gbt['bits'], 16) if nu5_active: blockcommitmentshash = int(gbt['defaultroots']['blockcommitmentshash'], 16) else: blockcommitmentshash = int(gbt['defaultroots']['chainhistoryroot'], 16) assert 'blockcommitmentshash' not in gbt['defaultroots'] # Confirm that the legacy fields match this default value. assert_equal(blockcommitmentshash, int(gbt['blockcommitmentshash'], 16)) assert_equal(blockcommitmentshash, int(gbt['lightclientroothash'], 16)) assert_equal(blockcommitmentshash, int(gbt['finalsaplingroothash'], 16)) f = BytesIO(hex_str_to_bytes(gbt['coinbasetxn']['data'])) coinbase = CTransaction() coinbase.deserialize(f) coinbase.calc_sha256() assert_equal(coinbase.hash, gbt['coinbasetxn']['hash']) assert_equal(coinbase.auth_digest_hex, gbt['coinbasetxn']['authdigest']) block = create_block(prevhash, coinbase, nTime, nBits, blockcommitmentshash) # copy the non-coinbase transactions from the block template to the block for gbt_tx in gbt['transactions']: f = BytesIO(hex_str_to_bytes(gbt_tx['data'])) tx = CTransaction() tx.deserialize(f) tx.calc_sha256() assert_equal(tx.auth_digest_hex, node.getrawtransaction(tx.hash, 1)['authdigest']) block.vtx.append(tx) block.hashMerkleRoot = int(gbt['defaultroots']['merkleroot'], 16) assert_equal(block.hashMerkleRoot, block.calc_merkle_root(), "merkleroot") assert_equal(len(block.vtx), len(gbt['transactions']) + 1, "number of transactions") assert_equal(block.hashPrevBlock, int(gbt['previousblockhash'], 16), "prevhash") if nu5_active: assert_equal(uint256_from_str(block.calc_auth_data_root()), int(gbt['defaultroots']['authdataroot'], 16)) else: assert 'authdataroot' not in gbt['defaultroots'] block.solve() block.calc_sha256() submitblock_reply = node.submitblock(codecs.encode(block.serialize(), 'hex_codec')) assert_equal(None, submitblock_reply) assert_equal(block.hash, node.getbestblockhash()) # Wait until the wallet has been notified of all blocks, so that it doesn't try to # double-spend transparent coins in subsequent test phases. self.sync_all()
def SignatureHashForkId(script, txTo, inIdx, hashtype, amount): hashPrevouts = 0 hashSequence = 0 hashOutputs = 0 if not (hashtype & SIGHASH_ANYONECANPAY): serialize_prevouts = bytes() for i in txTo.vin: serialize_prevouts += i.prevout.serialize() hashPrevouts = uint256_from_str(hash256(serialize_prevouts)) if (not (hashtype & SIGHASH_ANYONECANPAY) and (hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE): serialize_sequence = bytes() for i in txTo.vin: serialize_sequence += struct.pack("<I", i.nSequence) hashSequence = uint256_from_str(hash256(serialize_sequence)) if ((hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE): serialize_outputs = bytes() for o in txTo.vout: serialize_outputs += o.serialize() hashOutputs = uint256_from_str(hash256(serialize_outputs)) elif ((hashtype & 0x1f) == SIGHASH_SINGLE and inIdx < len(txTo.vout)): serialize_outputs = txTo.vout[inIdx].serialize() hashOutputs = uint256_from_str(hash256(serialize_outputs)) ss = bytes() ss += struct.pack("<i", txTo.nVersion) ss += ser_uint256(hashPrevouts) ss += ser_uint256(hashSequence) ss += txTo.vin[inIdx].prevout.serialize() ss += ser_string(script) ss += struct.pack("<q", amount) ss += struct.pack("<I", txTo.vin[inIdx].nSequence) ss += ser_uint256(hashOutputs) ss += struct.pack("<i", txTo.nLockTime) ss += struct.pack("<I", hashtype) return hash256(ss)
def run_test(self): # Set up test nodes. # - test_nodes[0] will only request v4 transactions # - test_nodes[1] will only request v5 transactions # - test_nodes[2] will test invalid v4 request using MSG_WTXID # - test_nodes[3] will test invalid v5 request using MSG_TX test_nodes = [] connections = [] for i in range(4): test_nodes.append(TestNode()) connections.append( NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i], protocol_version=NU5_PROTO_VERSION)) test_nodes[i].add_connection(connections[i]) NetworkThread().start() # Start up network handling in another thread [x.wait_for_verack() for x in test_nodes] net_version = self.nodes[0].getnetworkinfo()["protocolversion"] if net_version < NU5_PROTO_VERSION: # Sending a getdata message containing a MSG_WTX CInv message type # results in a reject message. self.verify_invalid_cinv( test_nodes[0], connections[0], 5, "Negotiated protocol version does not support CInv message type MSG_WTX", ) # Sending a getdata message containing an invalid CInv message type # results in a reject message. self.verify_invalid_cinv(test_nodes[1], connections[1], 0xffff, "Unknown CInv message type") print( "Node's block index is not NU5-aware, skipping remaining tests" ) return # Load funds into the Sprout address sproutzaddr = self.nodes[0].z_getnewaddress('sprout') result = self.nodes[2].z_shieldcoinbase("*", sproutzaddr, 0) wait_and_assert_operationid_status(self.nodes[2], result['opid']) self.sync_all() self.nodes[1].generate(1) self.sync_all() # Activate NU5. Block height after this is 210. self.nodes[0].generate(9) self.sync_all() # Add v4 transaction to the mempool. node1_taddr = self.nodes[1].getnewaddress() opid = self.nodes[0].z_sendmany(sproutzaddr, [{ 'address': node1_taddr, 'amount': 1, }]) v4_txid = uint256_from_str( hex_str_to_bytes( wait_and_assert_operationid_status(self.nodes[0], opid))[::-1]) # Add v5 transaction to the mempool. v5_txid = self.nodes[0].sendtoaddress(node1_taddr, 1, "", "", True) v5_tx = self.nodes[0].getrawtransaction(v5_txid, 1) assert_equal(v5_tx['version'], 5) v5_txid = uint256_from_str(hex_str_to_bytes(v5_txid)[::-1]) v5_auth_digest = uint256_from_str( hex_str_to_bytes(v5_tx['authdigest'])[::-1]) # Wait for the mempools to sync. self.sync_all() # # inv # # On a mempool request, nodes should return an inv message containing: # - the v4 tx, with type MSG_TX. # - the v5 tx, with type MSG_WTX. for testnode in test_nodes: self.verify_inv(testnode, [ self.cinv_for(v4_txid), self.cinv_for(v5_txid, v5_auth_digest), ]) # # getdata # # We can request a v4 transaction with MSG_TX. self.send_data_message(test_nodes[0], v4_txid) self.verify_last_tx(test_nodes[0], v4_txid) # We can request a v5 transaction with MSG_WTX. self.send_data_message(test_nodes[1], v5_txid, v5_auth_digest) self.verify_last_tx(test_nodes[1], v5_txid, v5_auth_digest) # Requesting with a different authDigest results in a notfound. self.send_data_message(test_nodes[1], v5_txid, 1) self.verify_last_notfound(test_nodes[1], v5_txid, 1) # Requesting a v4 transaction with MSG_WTX causes a disconnect. self.send_data_message(test_nodes[2], v4_txid, (1 << 256) - 1) self.verify_disconnected(test_nodes[2]) # Requesting a v5 transaction with MSG_TX causes a disconnect. self.send_data_message(test_nodes[3], v5_txid) self.verify_disconnected(test_nodes[3]) # Sending a getdata message containing an invalid CInv message type # results in a reject message. self.verify_invalid_cinv(test_nodes[0], connections[0], 0xffff, "Unknown CInv message type") [c.disconnect_node() for c in connections] self.log_stderr.close()
block.rehash() # Get the difficulty on the BStash chain hex_bits = aux['bits'] nBits = struct.unpack(">I", hex_str_to_bytes(hex_bits))[0] target = uint256_from_compact(nBits) # Construct BCash block block_ser = b"" block_ser += struct.pack("<i", block.nVersion) block_ser += ser_uint256(block.hashPrevBlock) block_ser += ser_uint256(block.hashMerkleRoot) block_ser += struct.pack("<I", block.nTime) block_ser += struct.pack("<I", block.nBits) block_ser_final = block_ser + struct.pack("<I", block.nNonce) hash_result = uint256_from_str(hash256(block_ser_final)) print("Starting mining, difficulty:{}".format(hex_bits)) i = 0 start_time = time.time() maxnonce = 2**32 block.nNonce = 0 nNonce = 0 while hash_result > target: nNonce += 1 if nNonce >= maxnonce: print("Finished mining:{} per sec".format(nNonce / (time.time() - start_time))) sys.exit() hash_result = uint256_from_str( hash256(block_ser + struct.pack("<I", nNonce)))
def create_utxos_value100000(self, node, utxo_count, utxo_size, min_confirmations): utxos = [] addr = node.getnewaddress() # create some confirmed UTXOs for i in range(utxo_count): txid = node.sendtoaddress(addr, self.utxo_test_bsvs) tx = FromHex(CTransaction(), node.getrawtransaction(txid)) tx.rehash() utxos.append(tx) node.generate(1) # Convert those utxos to new UTXO's in one single transaction that anyone can spend tx = None fee = 0 for _ in range(2): # firs iteration is to calculate the fee tx = CTransaction() amount = self.utxo_test_sats check_size = 0 for u in utxos: # Each UTXO will have two outputs, one for change and another one # amounting to roughly 10000 satoshis for i in range(len(u.vout)): uu = u.vout[i] if uu.nValue <= self.utxo_test_sats and uu.nValue > self.utxo_test_sats // 2: tx.vin.append( CTxIn( COutPoint( uint256_from_str( hex_str_to_bytes(u.hash)[::-1]), i), b'')) break adjust = 2 scriptPubKey = CScript([OP_DROP] + ([OP_NOP] * ((utxo_size // utxo_count) - adjust)) + [OP_TRUE]) if len(tx.vin) == utxo_count: amount = amount - fee while True: scriptPubKey = CScript([OP_DROP] + ([OP_NOP] * ( (utxo_size // utxo_count) - adjust)) + [OP_TRUE]) if check_size + len(scriptPubKey) > utxo_size: adjust = adjust + 1 continue elif check_size + len(scriptPubKey) < utxo_size: adjust = adjust - 1 continue break check_size = check_size + len(scriptPubKey) tx.vout.append(CTxOut(amount, scriptPubKey)) assert (len(tx.vout) == utxo_count) assert (check_size == utxo_size) # sign and send transaction txHex = node.signrawtransaction(ToHex(tx))['hex'] tx = FromHex(CTransaction(), txHex) tx.rehash() tx_size = len(ToHex(tx)) fee = int(self.blockmintxfee_sats * tx_size / 1000) node.sendrawtransaction(ToHex(tx)) if min_confirmations > 0: node.generate(min_confirmations) return tx