def test_getmnlistdiff_quorums(self, baseBlockHash, blockHash, baseQuorumList, expectedDeleted, expectedNew): d = self.test_getmnlistdiff_base(baseBlockHash, blockHash) assert_equal(set(d.deletedQuorums), set(expectedDeleted)) assert_equal( set([QuorumId(e.llmqType, e.quorumHash) for e in d.newQuorums]), set(expectedNew)) newQuorumList = baseQuorumList.copy() for e in d.deletedQuorums: newQuorumList.pop(e) for e in d.newQuorums: newQuorumList[QuorumId(e.llmqType, e.quorumHash)] = e hashes = [] for qc in newQuorumList.values(): hashes.append(hash256(qc.serialize())) hashes.sort() merkleRoot = CBlock.get_merkle_root(hashes) assert_equal(merkleRoot, d.merkleRootQuorums) return newQuorumList
def run_test(self): masternode = self.mninfo[0] p2p_masternode = masternode.node.add_p2p_connection(P2PInterface()) p2p_masternode.wait_for_verack() protx_hash = masternode.proTxHash public_key = masternode.pubKeyOperator # The peerinfo should not yet contain verified_proregtx_hash/verified_pubkey_hash assert ("verified_proregtx_hash" not in masternode.node.getpeerinfo()[-1]) assert ("verified_pubkey_hash" not in masternode.node.getpeerinfo()[-1]) # Fake-Authenticate the P2P connection to the masternode node_id = masternode.node.getpeerinfo()[-1]["id"] assert (masternode.node.mnauth(node_id, protx_hash, public_key)) # The peerinfo should now contain verified_proregtx_hash and verified_pubkey_hash peerinfo = masternode.node.getpeerinfo()[-1] assert ("verified_proregtx_hash" in peerinfo) assert ("verified_pubkey_hash" in peerinfo) assert_equal(peerinfo["verified_proregtx_hash"], protx_hash) assert_equal( peerinfo["verified_pubkey_hash"], bytes_to_hex_str(hash256(hex_str_to_bytes(public_key))[::-1])) # Test some error cases null_hash = "0000000000000000000000000000000000000000000000000000000000000000" assert_raises_rpc_error(-8, "proTxHash invalid", masternode.node.mnauth, node_id, null_hash, public_key) assert_raises_rpc_error(-8, "publicKey invalid", masternode.node.mnauth, node_id, protx_hash, null_hash) assert (not masternode.node.mnauth(-1, protx_hash, public_key))
def test_getmnlistdiff(self, baseBlockHash, blockHash, baseMNList, expectedDeleted, expectedUpdated): d = self.test_getmnlistdiff_base(baseBlockHash, blockHash) # Assert that the deletedMNs and mnList fields are what we expected assert_equal(set(d.deletedMNs), set([int(e, 16) for e in expectedDeleted])) assert_equal(set([e.proRegTxHash for e in d.mnList]), set(int(e, 16) for e in expectedUpdated)) # Build a new list based on the old list and the info from the diff newMNList = baseMNList.copy() for e in d.deletedMNs: newMNList.pop(format(e, '064x')) for e in d.mnList: newMNList[format(e.proRegTxHash, '064x')] = e # Verify that the merkle root matches what we locally calculate hashes = [] for mn in sorted(newMNList.values(), key=lambda mn: ser_uint256(mn.proRegTxHash)): hashes.append(hash256(mn.serialize())) merkleRoot = CBlock.get_merkle_root(hashes) assert_equal(merkleRoot, d.merkleRootMNList) return newMNList
def serialize_addrman( *, format=1, lowest_compatible=4, net_magic="regtest", bucket_key=1, len_new=None, len_tried=None, mock_checksum=None, ): new = [] tried = [] INCOMPATIBILITY_BASE = 32 r = MAGIC_BYTES[net_magic] r += struct.pack("B", format) r += struct.pack("B", INCOMPATIBILITY_BASE + lowest_compatible) r += ser_uint256(bucket_key) r += struct.pack("i", len_new or len(new)) r += struct.pack("i", len_tried or len(tried)) ADDRMAN_NEW_BUCKET_COUNT = 1 << 10 r += struct.pack("i", ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30)) for _ in range(ADDRMAN_NEW_BUCKET_COUNT): r += struct.pack("i", 0) checksum = hash256(r) r += mock_checksum or checksum return r
def get_request_id(self, tx_hex): tx = FromHex(CTransaction(), tx_hex) request_id_buf = ser_string(b"islock") + ser_compact_size(len(tx.vin)) for txin in tx.vin: request_id_buf += txin.prevout.serialize() return hash256(request_id_buf)[::-1].hex()
def run_test(self): self.node = self.nodes[0] privkey = byte_to_base58(hash256(struct.pack('<I', 0)), 239) self.node.importprivkey(privkey) self.node.generatetoaddress(100 + COINBASE_MATURITY, "qSrM9K6FMhZ29Vkp8Rdk8Jp66bbfpjFETq") """ pragma solidity ^0.4.11; contract Example { function timestamp() external returns(uint) { return now; } } """ bytecode = "60606040523415600b57fe5b5b60928061001a6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063b80777ea14603a575bfe5b3415604157fe5b6047605d565b6040518082815260200191505060405180910390f35b60004290505b905600a165627a7a7230582022b5728b8ca07de23857473e303660ad554d6344c64658ab692d741fa8753b380029" self.contract_address = self.node.createcontract(bytecode)['address'] self.node.generate(1) now = int(time.time()) expected_now = int( self.node.callcontract(self.contract_address, "b80777ea")['executionResult']['output'], 16) print(now, expected_now) assert (expected_now == now or expected_now == now + 1) activate_mpos(self.node) self.node.setmocktime(0) now = int(time.time()) expected_now = int( self.node.callcontract(self.contract_address, "b80777ea")['executionResult']['output'], 16) print(now, expected_now) assert (expected_now == now or expected_now == now + 1)
def run_test(self): node = self.nodes[0] node.generate(30) node.generate(100) fee = Decimal('10000') / COIN wallet_unspent = node.listunspent() for test_case in TESTCASES: num_inputs = len(test_case['sig_hash_types']) spent_outputs = wallet_unspent[:num_inputs] del wallet_unspent[:num_inputs] assert len(spent_outputs) == num_inputs total_input_amount = sum(output['amount'] for output in spent_outputs) max_output_amount = (total_input_amount - fee) / test_case['outputs'] tx = CTransaction() for i in range(test_case['outputs']): # Make sure each UTXO is unique output_amount = max_output_amount - i * Decimal('0.000047') output_script = CScript( [OP_HASH160, i.to_bytes(20, 'big'), OP_EQUAL]) tx.vout.append(CTxOut(int(output_amount * COIN), output_script)) # Build deserialized outputs so we can compute the sighash below spent_outputs_deser = [] for spent_output in spent_outputs: tx.vin.append( CTxIn( COutPoint(int(spent_output['txid'], 16), spent_output['vout']), CScript())) spent_outputs_deser.append( CTxOut( int(spent_output['amount'] * COIN), CScript(bytes.fromhex(spent_output['scriptPubKey'])))) unsigned_tx = tx.serialize().hex() for i, sig_hash_type in enumerate(test_case['sig_hash_types']): # Sign transaction using wallet raw_signed_tx = node.signrawtransactionwithwallet( unsigned_tx, None, sig_hash_type)['hex'] # Extract signature and pubkey from scriptSig signed_tx = CTransaction() signed_tx.deserialize(io.BytesIO(bytes.fromhex(raw_signed_tx))) stack_items = list(CScript(signed_tx.vin[i].scriptSig)) sig = stack_items[0] pubkey = ECPubKey() pubkey.set(stack_items[1]) sig_hash_type_int = self.parse_sig_hash_type(sig_hash_type) # Build expected sighash sighash = SignatureHashLotus( tx_to=tx, spent_utxos=spent_outputs_deser, sig_hash_type=sig_hash_type_int, input_index=i, executed_script_hash=hash256( spent_outputs_deser[i].scriptPubKey), ) # 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_int
def createblockproof(self, block, signblockprivkey): # create block proof with xField for all xfieldTypes r = b"" r += struct.pack("<i", block.nVersion) r += ser_uint256(block.hashPrevBlock) r += ser_uint256(block.hashMerkleRoot) r += ser_uint256(block.hashImMerkleRoot) r += struct.pack("<I", block.nTime) r += struct.pack("B", block.xfieldType) r += ser_string(block.xfield) sighash = hash256(r) block.proof = bytearray(self.signKey.sign(sighash)) block.rehash()
def SegwitVersion1SignatureHash_legacy(script, txTo, inIdx, hashtype, amount): """ This method is identical to the regular `SegwitVersion1SignatureHash` method, but without support for SIGHASH_RANGEPROOF. So basically it's the old version of the method from before the new sighash flag was added. """ hashPrevouts = 0 hashSequence = 0 hashIssuance = 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 not (hashtype & SIGHASH_ANYONECANPAY): serialize_issuance = bytes() # TODO actually serialize issuances for _ in txTo.vin: serialize_issuance += b'\x00' hashIssuance = uint256_from_str(hash256(serialize_issuance)) 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 += ser_uint256(hashIssuance) ss += txTo.vin[inIdx].prevout.serialize() ss += ser_string(script) ss += amount.serialize() 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 create_fake_clsig(self, height_offset): # create a fake block height_offset blocks ahead of the tip height = self.nodes[0].getblockcount() + height_offset fake_block = create_block(0xff, create_coinbase(height)) # create a fake clsig for that block quorum_hash = self.nodes[0].quorum_list(1)["llmq_test"][0] request_id_buf = ser_string(b"clsig") + struct.pack("<I", height) request_id_buf += bytes.fromhex(quorum_hash)[::-1] request_id = hash256(request_id_buf)[::-1].hex() quorum_hash = self.nodes[0].quorum_list(1)["llmq_test"][0] for mn in self.mninfo: mn.node.quorum_sign(100, request_id, fake_block.hash, quorum_hash) rec_sig = self.get_recovered_sig(request_id, fake_block.hash) fake_clsig = msg_clsig(height, fake_block.sha256, bytes.fromhex(rec_sig['sig']), [1, 0, 0, 0]) return fake_clsig, fake_block.hash
def serialize_addrman(*, format=1, lowest_compatible=3): new = [] tried = [] INCOMPATIBILITY_BASE = 32 r = MAGIC_BYTES["regtest"] r += struct.pack("B", format) r += struct.pack("B", INCOMPATIBILITY_BASE + lowest_compatible) r += ser_uint256(1) r += struct.pack("i", len(new)) r += struct.pack("i", len(tried)) ADDRMAN_NEW_BUCKET_COUNT = 1 << 10 r += struct.pack("i", ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30)) for _ in range(ADDRMAN_NEW_BUCKET_COUNT): r += struct.pack("i", 0) checksum = hash256(r) r += checksum return r
def test_recovered_signature_publishers(self): def validate_recovered_sig(request_id, msg_hash): # Make sure the recovered sig exists by RPC rpc_recovered_sig = self.get_recovered_sig(request_id, msg_hash) # Validate hashrecoveredsig zmq_recovered_sig_hash = bytes_to_hex_str( self.receive(ZMQPublisher.hash_recovered_sig).read(32)) assert_equal(zmq_recovered_sig_hash, msg_hash) # Validate rawrecoveredsig zmq_recovered_sig_raw = CRecoveredSig() zmq_recovered_sig_raw.deserialize( self.receive(ZMQPublisher.raw_recovered_sig)) assert_equal(zmq_recovered_sig_raw.llmqType, rpc_recovered_sig['llmqType']) assert_equal(uint256_to_string(zmq_recovered_sig_raw.quorumHash), rpc_recovered_sig['quorumHash']) assert_equal(uint256_to_string(zmq_recovered_sig_raw.id), rpc_recovered_sig['id']) assert_equal(uint256_to_string(zmq_recovered_sig_raw.msgHash), rpc_recovered_sig['msgHash']) assert_equal(bytes_to_hex_str(zmq_recovered_sig_raw.sig), rpc_recovered_sig['sig']) recovered_sig_publishers = [ ZMQPublisher.hash_recovered_sig, ZMQPublisher.raw_recovered_sig ] self.log.info("Testing %d recovered signature publishers" % len(recovered_sig_publishers)) # Subscribe to recovered signature messages self.subscribe(recovered_sig_publishers) # Generate a ChainLock and make sure this leads to valid recovered sig ZMQ messages rpc_last_block_hash = self.nodes[0].generate(1)[0] self.wait_for_chainlocked_block_all_nodes(rpc_last_block_hash) height = self.nodes[0].getblockcount() rpc_request_id = hash256( ser_string(b"clsig") + struct.pack("<I", height))[::-1].hex() validate_recovered_sig(rpc_request_id, rpc_last_block_hash) # Sign an arbitrary and make sure this leads to valid recovered sig ZMQ messages sign_id = uint256_to_string(random.getrandbits(256)) sign_msg_hash = uint256_to_string(random.getrandbits(256)) for mn in self.get_quorum_masternodes(self.quorum_hash): mn.node.quorum("sign", self.quorum_type, sign_id, sign_msg_hash) validate_recovered_sig(sign_id, sign_msg_hash) # Unsubscribe from recovered signature messages self.unsubscribe(recovered_sig_publishers)
def receive_thread_nevm(self, idx, subscriber): while True: try: self.log.info('receive_thread_nevm waiting to receive... idx {}'.format(idx)) data = subscriber.receive() if data[0] == b"nevmcomms": subscriber.send([b"nevmcomms", b"ack"]) elif data[0] == b"nevmblock": hashStr = hash256(str(random.randint(-0x80000000, 0x7fffffff)).encode()) hashTopic = uint256_from_str(hashStr) nevmBlock = CNEVMBlock(hashTopic, hashTopic, hashTopic, b"nevmblock") subscriber.send([b"nevmblock", nevmBlock.serialize()]) elif data[0] == b"nevmconnect": evmBlockConnect = CNEVMBlockConnect() evmBlockConnect.deserialize(BytesIO(data[1])) resBlock = subscriber.addBlock(evmBlockConnect) res = b"" if resBlock: res = b"connected" else: res = b"not connected" # stay paused during delay test while subscriber.artificialDelay == True: sleep(0.1) subscriber.send([b"nevmconnect", res]) elif data[0] == b"nevmdisconnect": evmBlockDisconnect = CNEVMBlockDisconnect() evmBlockDisconnect.deserialize(BytesIO(data[1])) resBlock = subscriber.deleteBlock(evmBlockDisconnect) res = b"" if resBlock: res = b"disconnected" else: res = b"not disconnected" subscriber.send([b"nevmdisconnect", res]) else: self.log.info("Unknown topic in REQ {}".format(data)) except zmq.ContextTerminated: sleep(1) break except zmq.ZMQError: self.log.warning('zmq error, socket closed unexpectedly.') sleep(1) break
def SignatureHash_legacy(script, txTo, inIdx, hashtype): """ This method is identical to the regular `SignatureHash` method, but without support for SIGHASH_RANGEPROOF. So basically it's the old version of the method from before the new sighash flag was added. """ HASH_ONE = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' if inIdx >= len(txTo.vin): return (HASH_ONE, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin))) txtmp = CTransaction(txTo) for txin in txtmp.vin: txin.scriptSig = b'' txtmp.vin[inIdx].scriptSig = FindAndDelete(script, CScript([OP_CODESEPARATOR])) if (hashtype & 0x1f) == SIGHASH_NONE: txtmp.vout = [] for i in range(len(txtmp.vin)): if i != inIdx: txtmp.vin[i].nSequence = 0 elif (hashtype & 0x1f) == SIGHASH_SINGLE: outIdx = inIdx if outIdx >= len(txtmp.vout): return (HASH_ONE, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout))) tmp = txtmp.vout[outIdx] txtmp.vout = [] for i in range(outIdx): txtmp.vout.append(CTxOut(-1)) txtmp.vout.append(tmp) for i in range(len(txtmp.vin)): if i != inIdx: txtmp.vin[i].nSequence = 0 if hashtype & SIGHASH_ANYONECANPAY: tmp = txtmp.vin[inIdx] txtmp.vin = [] txtmp.vin.append(tmp) # sighash serialization is different from non-witness serialization # do manual sighash serialization: s = b"" s += struct.pack("<i", txtmp.nVersion) s += ser_vector(txtmp.vin) s += ser_vector(txtmp.vout) s += struct.pack("<I", txtmp.nLockTime) # add sighash type s += struct.pack(b"<I", hashtype) hash = hash256(s) return (hash, None)
def run_test(self): self.nodes[0].add_p2p_connection(P2PDataStore()) self.nodeaddress = self.nodes[0].getnewaddress() self.pubkey = self.nodes[0].getaddressinfo(self.nodeaddress)["pubkey"] self.log.info("Mining %d blocks", CHAIN_HEIGHT) self.coinbase_txids = [ self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate( CHAIN_HEIGHT, self.signblockprivkey_wif) ] ## P2PKH transaction ######################## self.log.info("Test using a P2PKH transaction") spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0], self.nodeaddress, amount=10) spendtx.rehash() copy_spendTx = CTransaction(spendtx) #cache hashes hash = spendtx.hash hashMalFix = spendtx.hashMalFix #malleate unDERify(spendtx) spendtx.rehash() # verify that hashMalFix remains the same even when signature is malleated and hash changes assert_not_equal(hash, spendtx.hash) assert_equal(hashMalFix, spendtx.hashMalFix) # verify that hash is spendtx.serialize() hash = encode(hash256(spendtx.serialize())[::-1], 'hex_codec').decode('ascii') assert_equal(hash, spendtx.hash) # verify that hashMalFix is spendtx.serialize(with_scriptsig=False) hashMalFix = encode( hash256(spendtx.serialize(with_scriptsig=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hashMalFix, spendtx.hashMalFix) assert_not_equal(hash, hashMalFix) #as this transaction does not have witness data the following is true assert_equal(spendtx.serialize(), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_not_equal( spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=False)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=True), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=False), spendtx.serialize_without_witness(with_scriptsig=False)) #Create block with only non-DER signature P2PKH transaction tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 1), block_time) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkey) # serialize with and without witness block remains the same assert_equal(block.serialize(with_witness=True), block.serialize()) assert_equal(block.serialize(with_witness=True), block.serialize(with_witness=False)) assert_equal(block.serialize(with_witness=True), block.serialize(with_witness=False, with_scriptsig=True)) self.log.info("Reject block with non-DER signature") self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), tip) wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_INVALID) assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed') self.log.info("Accept block with DER signature") #recreate block with DER sig transaction block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 1), block_time) block.vtx.append(copy_spendTx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkey) self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) ## P2SH transaction ######################## self.log.info("Test using P2SH transaction ") REDEEM_SCRIPT_1 = CScript([OP_1, OP_DROP]) P2SH_1 = CScript([OP_HASH160, hash160(REDEEM_SCRIPT_1), OP_EQUAL]) tx = CTransaction() tx.vin.append( CTxIn(COutPoint(int(self.coinbase_txids[1], 16), 0), b"", 0xffffffff)) tx.vout.append(CTxOut(10, P2SH_1)) tx.rehash() spendtx_raw = self.nodes[0].signrawtransactionwithwallet( ToHex(tx), [], "ALL", self.options.scheme)["hex"] spendtx = FromHex(spendtx, spendtx_raw) spendtx.rehash() copy_spendTx = CTransaction(spendtx) #cache hashes hash = spendtx.hash hashMalFix = spendtx.hashMalFix #malleate spendtxcopy = spendtx unDERify(spendtxcopy) spendtxcopy.rehash() # verify that hashMalFix remains the same even when signature is malleated and hash changes assert_not_equal(hash, spendtxcopy.hash) assert_equal(hashMalFix, spendtxcopy.hashMalFix) # verify that hash is spendtx.serialize() hash = encode( hash256(spendtx.serialize(with_witness=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hash, spendtx.hash) # verify that hashMalFix is spendtx.serialize(with_scriptsig=False) hashMalFix = encode( hash256(spendtx.serialize(with_witness=False, with_scriptsig=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hashMalFix, spendtx.hashMalFix) assert_not_equal(hash, hashMalFix) #as this transaction does not have witness data the following is true assert_equal(spendtx.serialize(), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_not_equal( spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=False)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=True), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=False), spendtx.serialize_without_witness(with_scriptsig=False)) #Create block with only non-DER signature P2SH transaction tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 2), block_time) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkey) # serialize with and without witness block remains the same assert_equal(block.serialize(with_witness=True), block.serialize()) assert_equal(block.serialize(with_witness=True), block.serialize(with_witness=False)) assert_equal(block.serialize(with_witness=True), block.serialize(with_witness=True, with_scriptsig=True)) self.log.info("Reject block with non-DER signature") self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), tip) wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_INVALID) assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed') self.log.info("Accept block with DER signature") #recreate block with DER sig transaction block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 2), block_time) block.vtx.append(copy_spendTx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkey) self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) ## redeem previous P2SH ######################### self.log.info("Test using P2SH redeem transaction ") tx = CTransaction() tx.vout.append(CTxOut(1, CScript([OP_TRUE]))) tx.vin.append(CTxIn(COutPoint(block.vtx[1].malfixsha256, 0), b'')) (sighash, err) = SignatureHash(REDEEM_SCRIPT_1, tx, 1, SIGHASH_ALL) signKey = CECKey() signKey.set_secretbytes(b"horsebattery") sig = signKey.sign(sighash) + bytes(bytearray([SIGHASH_ALL])) scriptSig = CScript([sig, REDEEM_SCRIPT_1]) tx.vin[0].scriptSig = scriptSig tx.rehash() spendtx_raw = self.nodes[0].signrawtransactionwithwallet( ToHex(tx), [], "ALL", self.options.scheme)["hex"] spendtx = FromHex(spendtx, spendtx_raw) spendtx.rehash() #cache hashes hash = spendtx.hash hashMalFix = spendtx.hashMalFix #malleate spendtxcopy = spendtx unDERify(spendtxcopy) spendtxcopy.rehash() # verify that hashMalFix remains the same even when signature is malleated and hash changes assert_not_equal(hash, spendtxcopy.hash) assert_equal(hashMalFix, spendtxcopy.hashMalFix) # verify that hash is spendtx.serialize() hash = encode( hash256(spendtx.serialize(with_witness=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hash, spendtx.hash) # verify that hashMalFix is spendtx.serialize(with_scriptsig=False) hashMalFix = encode( hash256(spendtx.serialize(with_witness=False, with_scriptsig=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hashMalFix, spendtx.hashMalFix) assert_not_equal(hash, hashMalFix) #as this transaction does not have witness data the following is true assert_equal(spendtx.serialize(), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_not_equal( spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=False)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=True), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=False), spendtx.serialize_without_witness(with_scriptsig=False)) #Create block with only non-DER signature P2SH redeem transaction tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 3), block_time) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkey) # serialize with and without witness block remains the same assert_equal(block.serialize(with_witness=True), block.serialize()) assert_equal(block.serialize(with_witness=True), block.serialize(with_witness=False)) assert_equal(block.serialize(with_witness=True), block.serialize(with_witness=True, with_scriptsig=True)) self.log.info("Accept block with P2SH redeem transaction") self.nodes[0].p2p.send_and_ping(msg_block(block)) assert_equal(self.nodes[0].getbestblockhash(), block.hash) ## p2sh_p2wpkh transaction ############################## self.log.info("Test using p2sh_p2wpkh transaction ") spendtxStr = create_witness_tx(self.nodes[0], True, getInput(self.coinbase_txids[4]), self.pubkey, amount=1.0) #get CTRansaction object from above hex spendtx = CTransaction() spendtx.deserialize(BytesIO(hex_str_to_bytes(spendtxStr))) spendtx.rehash() #cache hashes spendtx.rehash() hash = spendtx.hash hashMalFix = spendtx.hashMalFix withash = spendtx.calc_sha256(True) # malleate unDERify(spendtx) spendtx.rehash() withash2 = spendtx.calc_sha256(True) # verify that hashMalFix remains the same even when signature is malleated and hash changes assert_equal(withash, withash2) assert_equal(hash, spendtx.hash) assert_equal(hashMalFix, spendtx.hashMalFix) # verify that hash is spendtx.serialize() hash = encode(hash256(spendtx.serialize())[::-1], 'hex_codec').decode('ascii') assert_equal(hash, spendtx.hash) # verify that hashMalFix is spendtx.serialize(with_scriptsig=False) hashMalFix = encode( hash256(spendtx.serialize(with_scriptsig=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hashMalFix, spendtx.hashMalFix) assert_not_equal(hash, hashMalFix) #as this transaction does not have witness data the following is true assert_equal(spendtx.serialize(), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_not_equal( spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=False)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=True), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=False), spendtx.serialize_without_witness(with_scriptsig=False)) #Create block with only non-DER signature p2sh_p2wpkh transaction spendtxStr = self.nodes[0].signrawtransactionwithwallet( spendtxStr, [], "ALL", self.options.scheme) assert ("errors" not in spendtxStr or len(["errors"]) == 0) spendtxStr = spendtxStr["hex"] spendtx = CTransaction() spendtx.deserialize(BytesIO(hex_str_to_bytes(spendtxStr))) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 4), block_time) block.vtx.append(spendtx) add_witness_commitment(block) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkey) # serialize with and without witness assert_equal(block.serialize(with_witness=False), block.serialize()) assert_not_equal(block.serialize(with_witness=True), block.serialize(with_witness=False)) assert_not_equal( block.serialize(with_witness=True), block.serialize(with_witness=False, with_scriptsig=True)) self.log.info( "Reject block with p2sh_p2wpkh transaction and witness commitment") assert_raises_rpc_error( -22, "Block does not start with a coinbase", self.nodes[0].submitblock, bytes_to_hex_str(block.serialize(with_witness=True))) assert_equal(self.nodes[0].getbestblockhash(), tip) block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 4), block_time) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkey) self.log.info("Accept block with p2sh_p2wpkh transaction") self.nodes[0].submitblock( bytes_to_hex_str(block.serialize(with_witness=True))) assert_equal(self.nodes[0].getbestblockhash(), block.hash) ## p2sh_p2wsh transaction ############################## self.log.info("Test using p2sh_p2wsh transaction") spendtxStr = create_witness_tx(self.nodes[0], True, getInput(self.coinbase_txids[5]), self.pubkey, amount=1.0) #get CTRansaction object from above hex spendtx = CTransaction() spendtx.deserialize(BytesIO(hex_str_to_bytes(spendtxStr))) spendtx.rehash() #cache hashes spendtx.rehash() hash = spendtx.hash hashMalFix = spendtx.hashMalFix withash = spendtx.calc_sha256(True) # malleate unDERify(spendtx) spendtx.rehash() withash2 = spendtx.calc_sha256(True) # verify that hashMalFix remains the same even when signature is malleated and hash changes assert_equal(withash, withash2) assert_equal(hash, spendtx.hash) assert_equal(hashMalFix, spendtx.hashMalFix) # verify that hash is spendtx.serialize() hash = encode(hash256(spendtx.serialize())[::-1], 'hex_codec').decode('ascii') assert_equal(hash, spendtx.hash) # verify that hashMalFix is spendtx.serialize(with_scriptsig=False) hashMalFix = encode( hash256(spendtx.serialize(with_scriptsig=False))[::-1], 'hex_codec').decode('ascii') assert_equal(hashMalFix, spendtx.hashMalFix) assert_not_equal(hash, hashMalFix) #as this transaction does not have witness data the following is true assert_equal(spendtx.serialize(), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=True)) assert_not_equal( spendtx.serialize(with_witness=False), spendtx.serialize(with_witness=True, with_scriptsig=False)) assert_equal(spendtx.serialize(with_witness=False), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=True), spendtx.serialize_without_witness(with_scriptsig=True)) assert_equal(spendtx.serialize_with_witness(with_scriptsig=False), spendtx.serialize_without_witness(with_scriptsig=False)) #Create block with only non-DER signature p2sh_p2wsh transaction spendtxStr = self.nodes[0].signrawtransactionwithwallet( spendtxStr, [], "ALL", self.options.scheme) assert ("errors" not in spendtxStr or len(["errors"]) == 0) spendtxStr = spendtxStr["hex"] spendtx = CTransaction() spendtx.deserialize(BytesIO(hex_str_to_bytes(spendtxStr))) spendtx.rehash() tip = self.nodes[0].getbestblockhash() block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 5), block_time) block.vtx.append(spendtx) add_witness_commitment(block) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkey) # serialize with and without witness assert_equal(block.serialize(with_witness=False), block.serialize()) assert_not_equal(block.serialize(with_witness=True), block.serialize(with_witness=False)) assert_not_equal( block.serialize(with_witness=True), block.serialize(with_witness=False, with_scriptsig=True)) self.log.info( "Reject block with p2sh_p2wsh transaction and witness commitment") assert_raises_rpc_error( -22, "Block does not start with a coinbase", self.nodes[0].submitblock, bytes_to_hex_str(block.serialize(with_witness=True))) assert_equal(self.nodes[0].getbestblockhash(), tip) block = create_block(int(tip, 16), create_coinbase(CHAIN_HEIGHT + 5), block_time) block.vtx.append(spendtx) block.hashMerkleRoot = block.calc_merkle_root() block.hashImMerkleRoot = block.calc_immutable_merkle_root() block.rehash() block.solve(self.signblockprivkey) self.log.info("Accept block with p2sh_p2wsh transaction") self.nodes[0].submitblock( bytes_to_hex_str(block.serialize(with_witness=True))) assert_equal(self.nodes[0].getbestblockhash(), block.hash)
def run_test(self): # Node 0 supports COMPACT_FILTERS, node 1 does not. peer_0 = self.nodes[0].add_p2p_connection(FiltersClient()) peer_1 = self.nodes[1].add_p2p_connection(FiltersClient()) # Nodes 0 & 1 share the same first 999 blocks in the chain. self.generate(self.nodes[0], 999) # Stale blocks by disconnecting nodes 0 & 1, mining, then reconnecting self.disconnect_nodes(0, 1) stale_block_hash = self.generate(self.nodes[0], 1, sync_fun=self.no_op)[0] self.nodes[0].syncwithvalidationinterfacequeue() assert_equal(self.nodes[0].getblockcount(), 1000) self.generate(self.nodes[1], 1001, sync_fun=self.no_op) assert_equal(self.nodes[1].getblockcount(), 2000) # Check that nodes have signalled NODE_COMPACT_FILTERS correctly. assert peer_0.nServices & NODE_COMPACT_FILTERS != 0 assert peer_1.nServices & NODE_COMPACT_FILTERS == 0 # Check that the localservices is as expected. assert int(self.nodes[0].getnetworkinfo()['localservices'], 16) & NODE_COMPACT_FILTERS != 0 assert int(self.nodes[1].getnetworkinfo()['localservices'], 16) & NODE_COMPACT_FILTERS == 0 self.log.info("get cfcheckpt on chain to be re-orged out.") request = msg_getcfcheckpt( filter_type=FILTER_TYPE_BASIC, stop_hash=int(stale_block_hash, 16), ) peer_0.send_and_ping(message=request) response = peer_0.last_message['cfcheckpt'] assert_equal(response.filter_type, request.filter_type) assert_equal(response.stop_hash, request.stop_hash) assert_equal(len(response.headers), 1) self.log.info("Reorg node 0 to a new chain.") self.connect_nodes(0, 1) self.sync_blocks(timeout=600) self.nodes[0].syncwithvalidationinterfacequeue() main_block_hash = self.nodes[0].getblockhash(1000) assert main_block_hash != stale_block_hash, "node 0 chain did not reorganize" self.log.info("Check that peers can fetch cfcheckpt on active chain.") tip_hash = self.nodes[0].getbestblockhash() request = msg_getcfcheckpt( filter_type=FILTER_TYPE_BASIC, stop_hash=int(tip_hash, 16), ) peer_0.send_and_ping(request) response = peer_0.last_message['cfcheckpt'] assert_equal(response.filter_type, request.filter_type) assert_equal(response.stop_hash, request.stop_hash) main_cfcheckpt = self.nodes[0].getblockfilter(main_block_hash, 'basic')['header'] tip_cfcheckpt = self.nodes[0].getblockfilter(tip_hash, 'basic')['header'] assert_equal( response.headers, [int(header, 16) for header in (main_cfcheckpt, tip_cfcheckpt)], ) self.log.info("Check that peers can fetch cfcheckpt on stale chain.") request = msg_getcfcheckpt( filter_type=FILTER_TYPE_BASIC, stop_hash=int(stale_block_hash, 16), ) peer_0.send_and_ping(request) response = peer_0.last_message['cfcheckpt'] stale_cfcheckpt = self.nodes[0].getblockfilter(stale_block_hash, 'basic')['header'] assert_equal( response.headers, [int(header, 16) for header in (stale_cfcheckpt, )], ) self.log.info("Check that peers can fetch cfheaders on active chain.") request = msg_getcfheaders( filter_type=FILTER_TYPE_BASIC, start_height=1, stop_hash=int(main_block_hash, 16), ) peer_0.send_and_ping(request) response = peer_0.last_message['cfheaders'] main_cfhashes = response.hashes assert_equal(len(main_cfhashes), 1000) assert_equal( compute_last_header(response.prev_header, response.hashes), int(main_cfcheckpt, 16), ) self.log.info("Check that peers can fetch cfheaders on stale chain.") request = msg_getcfheaders( filter_type=FILTER_TYPE_BASIC, start_height=1, stop_hash=int(stale_block_hash, 16), ) peer_0.send_and_ping(request) response = peer_0.last_message['cfheaders'] stale_cfhashes = response.hashes assert_equal(len(stale_cfhashes), 1000) assert_equal( compute_last_header(response.prev_header, response.hashes), int(stale_cfcheckpt, 16), ) self.log.info("Check that peers can fetch cfilters.") stop_hash = self.nodes[0].getblockhash(10) request = msg_getcfilters( filter_type=FILTER_TYPE_BASIC, start_height=1, stop_hash=int(stop_hash, 16), ) peer_0.send_and_ping(request) response = peer_0.pop_cfilters() assert_equal(len(response), 10) self.log.info("Check that cfilter responses are correct.") for cfilter, cfhash, height in zip(response, main_cfhashes, range(1, 11)): block_hash = self.nodes[0].getblockhash(height) assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC) assert_equal(cfilter.block_hash, int(block_hash, 16)) computed_cfhash = uint256_from_str(hash256(cfilter.filter_data)) assert_equal(computed_cfhash, cfhash) self.log.info("Check that peers can fetch cfilters for stale blocks.") request = msg_getcfilters( filter_type=FILTER_TYPE_BASIC, start_height=1000, stop_hash=int(stale_block_hash, 16), ) peer_0.send_and_ping(request) response = peer_0.pop_cfilters() assert_equal(len(response), 1) cfilter = response[0] assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC) assert_equal(cfilter.block_hash, int(stale_block_hash, 16)) computed_cfhash = uint256_from_str(hash256(cfilter.filter_data)) assert_equal(computed_cfhash, stale_cfhashes[999]) self.log.info( "Requests to node 1 without NODE_COMPACT_FILTERS results in disconnection." ) requests = [ msg_getcfcheckpt( filter_type=FILTER_TYPE_BASIC, stop_hash=int(main_block_hash, 16), ), msg_getcfheaders( filter_type=FILTER_TYPE_BASIC, start_height=1000, stop_hash=int(main_block_hash, 16), ), msg_getcfilters( filter_type=FILTER_TYPE_BASIC, start_height=1000, stop_hash=int(main_block_hash, 16), ), ] for request in requests: peer_1 = self.nodes[1].add_p2p_connection(P2PInterface()) peer_1.send_message(request) peer_1.wait_for_disconnect() self.log.info("Check that invalid requests result in disconnection.") requests = [ # Requesting too many filters results in disconnection. msg_getcfilters( filter_type=FILTER_TYPE_BASIC, start_height=0, stop_hash=int(main_block_hash, 16), ), # Requesting too many filter headers results in disconnection. msg_getcfheaders( filter_type=FILTER_TYPE_BASIC, start_height=0, stop_hash=int(tip_hash, 16), ), # Requesting unknown filter type results in disconnection. msg_getcfcheckpt( filter_type=255, stop_hash=int(main_block_hash, 16), ), # Requesting unknown hash results in disconnection. msg_getcfcheckpt( filter_type=FILTER_TYPE_BASIC, stop_hash=123456789, ), ] for request in requests: peer_0 = self.nodes[0].add_p2p_connection(P2PInterface()) peer_0.send_message(request) peer_0.wait_for_disconnect() self.log.info( "Test -peerblockfilters without -blockfilterindex raises an error") self.stop_node(0) self.nodes[0].extra_args = ["-peerblockfilters"] msg = "Error: Cannot set -peerblockfilters without -blockfilterindex." self.nodes[0].assert_start_raises_init_error(expected_msg=msg) self.log.info( "Test -blockfilterindex with -reindex-chainstate raises an error") self.nodes[0].assert_start_raises_init_error( expected_msg= 'Error: -reindex-chainstate option is not compatible with -blockfilterindex. ' 'Please temporarily disable blockfilterindex while using -reindex-chainstate, or replace -reindex-chainstate with -reindex to fully rebuild all indexes.', extra_args=['-blockfilterindex', '-reindex-chainstate'], )
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'])
def run_test(self): # Node 0 supports COMPACT_FILTERS, node 1 does not. node0 = self.nodes[0].add_p2p_connection(CFiltersClient()) node1 = self.nodes[1].add_p2p_connection(CFiltersClient()) # Nodes 0 & 1 share the same first 999 blocks in the chain. self.nodes[0].generate(999) self.sync_blocks(timeout=600) # Stale blocks by disconnecting nodes 0 & 1, mining, then reconnecting disconnect_nodes(self.nodes[0], self.nodes[1]) self.nodes[0].generate(1) wait_until(lambda: self.nodes[0].getblockcount() == 1000) stale_block_hash = self.nodes[0].getblockhash(1000) self.nodes[1].generate(1001) wait_until(lambda: self.nodes[1].getblockcount() == 2000) # Check that nodes have signalled NODE_COMPACT_FILTERS correctly. assert node0.nServices & NODE_COMPACT_FILTERS != 0 assert node1.nServices & NODE_COMPACT_FILTERS == 0 # Check that the localservices is as expected. assert int(self.nodes[0].getnetworkinfo()['localservices'], 16) & NODE_COMPACT_FILTERS != 0 assert int(self.nodes[1].getnetworkinfo()['localservices'], 16) & NODE_COMPACT_FILTERS == 0 self.log.info("get cfcheckpt on chain to be re-orged out.") request = msg_getcfcheckpt(filter_type=FILTER_TYPE_BASIC, stop_hash=int(stale_block_hash, 16)) node0.send_and_ping(message=request) response = node0.last_message['cfcheckpt'] assert_equal(response.filter_type, request.filter_type) assert_equal(response.stop_hash, request.stop_hash) assert_equal(len(response.headers), 1) self.log.info("Reorg node 0 to a new chain.") connect_nodes(self.nodes[0], self.nodes[1]) self.sync_blocks(timeout=600) main_block_hash = self.nodes[0].getblockhash(1000) assert main_block_hash != stale_block_hash, "node 0 chain did not reorganize" self.log.info("Check that peers can fetch cfcheckpt on active chain.") tip_hash = self.nodes[0].getbestblockhash() request = msg_getcfcheckpt(filter_type=FILTER_TYPE_BASIC, stop_hash=int(tip_hash, 16)) node0.send_and_ping(request) response = node0.last_message['cfcheckpt'] assert_equal(response.filter_type, request.filter_type) assert_equal(response.stop_hash, request.stop_hash) main_cfcheckpt = self.nodes[0].getblockfilter(main_block_hash, 'basic')['header'] tip_cfcheckpt = self.nodes[0].getblockfilter(tip_hash, 'basic')['header'] assert_equal( response.headers, [int(header, 16) for header in (main_cfcheckpt, tip_cfcheckpt)]) self.log.info("Check that peers can fetch cfcheckpt on stale chain.") request = msg_getcfcheckpt(filter_type=FILTER_TYPE_BASIC, stop_hash=int(stale_block_hash, 16)) node0.send_and_ping(request) response = node0.last_message['cfcheckpt'] stale_cfcheckpt = self.nodes[0].getblockfilter(stale_block_hash, 'basic')['header'] assert_equal(response.headers, [int(header, 16) for header in (stale_cfcheckpt, )]) self.log.info("Check that peers can fetch cfheaders on active chain.") request = msg_getcfheaders(filter_type=FILTER_TYPE_BASIC, start_height=1, stop_hash=int(main_block_hash, 16)) node0.send_and_ping(request) response = node0.last_message['cfheaders'] main_cfhashes = response.hashes assert_equal(len(main_cfhashes), 1000) assert_equal( compute_last_header(response.prev_header, response.hashes), int(main_cfcheckpt, 16)) self.log.info("Check that peers can fetch cfheaders on stale chain.") request = msg_getcfheaders(filter_type=FILTER_TYPE_BASIC, start_height=1, stop_hash=int(stale_block_hash, 16)) node0.send_and_ping(request) response = node0.last_message['cfheaders'] stale_cfhashes = response.hashes assert_equal(len(stale_cfhashes), 1000) assert_equal( compute_last_header(response.prev_header, response.hashes), int(stale_cfcheckpt, 16)) self.log.info("Check that peers can fetch cfilters.") stop_hash = self.nodes[0].getblockhash(10) request = msg_getcfilters(filter_type=FILTER_TYPE_BASIC, start_height=1, stop_hash=int(stop_hash, 16)) node0.send_message(request) node0.sync_with_ping() response = node0.pop_cfilters() assert_equal(len(response), 10) self.log.info("Check that cfilter responses are correct.") for cfilter, cfhash, height in zip(response, main_cfhashes, range(1, 11)): block_hash = self.nodes[0].getblockhash(height) assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC) assert_equal(cfilter.block_hash, int(block_hash, 16)) computed_cfhash = uint256_from_str(hash256(cfilter.filter_data)) assert_equal(computed_cfhash, cfhash) self.log.info("Check that peers can fetch cfilters for stale blocks.") request = msg_getcfilters(filter_type=FILTER_TYPE_BASIC, start_height=1000, stop_hash=int(stale_block_hash, 16)) node0.send_message(request) node0.sync_with_ping() response = node0.pop_cfilters() assert_equal(len(response), 1) cfilter = response[0] assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC) assert_equal(cfilter.block_hash, int(stale_block_hash, 16)) computed_cfhash = uint256_from_str(hash256(cfilter.filter_data)) assert_equal(computed_cfhash, stale_cfhashes[999]) self.log.info( "Requests to node 1 without NODE_COMPACT_FILTERS results in disconnection." ) requests = [ msg_getcfcheckpt(filter_type=FILTER_TYPE_BASIC, stop_hash=int(main_block_hash, 16)), msg_getcfheaders(filter_type=FILTER_TYPE_BASIC, start_height=1000, stop_hash=int(main_block_hash, 16)), msg_getcfilters(filter_type=FILTER_TYPE_BASIC, start_height=1000, stop_hash=int(main_block_hash, 16)), ] for request in requests: node1 = self.nodes[1].add_p2p_connection(P2PInterface()) node1.send_message(request) node1.wait_for_disconnect() self.log.info("Check that invalid requests result in disconnection.") requests = [ # Requesting too many filters results in disconnection. msg_getcfilters(filter_type=FILTER_TYPE_BASIC, start_height=0, stop_hash=int(main_block_hash, 16)), # Requesting too many filter headers results in disconnection. msg_getcfheaders(filter_type=FILTER_TYPE_BASIC, start_height=0, stop_hash=int(tip_hash, 16)), # Requesting unknown filter type results in disconnection. msg_getcfcheckpt(filter_type=255, stop_hash=int(main_block_hash, 16)), # Requesting unknown hash results in disconnection. msg_getcfcheckpt( filter_type=FILTER_TYPE_BASIC, stop_hash=123456789, ), ] for request in requests: node0 = self.nodes[0].add_p2p_connection(P2PInterface()) node0.send_message(request) node0.wait_for_disconnect()
from test_framework.script import hash160 from test_framework.messages import sha256, hash256 from binascii import hexlify, unhexlify if __name__ == '__main__': # 生成script_test中的部分数据 print(hexlify(sha256(b""))) print(hexlify(sha256(b"a"))) print(hexlify(sha256(b"abcdefghijklmnopqrstuvwxyz"))) print(hexlify(hash160(b""))) print(hexlify(hash160(b"a"))) print(hexlify(hash160(b"abcdefghijklmnopqrstuvwxyz"))) print(hexlify(hash256(b""))) print(hexlify(hash256(b"a"))) print(hexlify(hash256(b"abcdefghijklmnopqrstuvwxyz"))) print(hexlify(hash160(b'\x51'))) print(hexlify(hash160(b'\xb9'))) print(hexlify(hash160(b'\x50'))) print(hexlify(hash160(b'\x62'))) print(hexlify(hash160(b'\x63\x51\x68'))) print(hexlify(hash160(b'\x64\x51\x68'))) print(hexlify(sha256(unhexlify("635168")))) print( hexlify( hash160( unhexlify( "0020db42be9d58213fee35c471713a4a1de4c1a8654f58008c14836e9a5274278f4d"
def hash256(x): x = to_bytes(x) return messages.hash256(x)
def send_clsig(self, clsig): clhash = uint256_from_str(hash256(clsig.serialize())) self.clsigs[clhash] = clsig inv = msg_inv([CInv(20, clhash)]) self.send_message(inv)
def run_test(self): node = self.nodes[0] addrkey0 = node.get_deterministic_priv_key() blockhashes = node.generatetoaddress(2, addrkey0.address) stakes = create_coinbase_stakes(node, [blockhashes[0]], addrkey0.key) privkey = ECKey() privkey.generate() proof_master = privkey.get_pubkey().get_bytes().hex() proof_sequence = 42 proof_expiration = 2000000000 proof = node.buildavalancheproof(proof_sequence, proof_expiration, proof_master, stakes) nodeid = add_interface_node(node) def check_addavalanchenode_error(error_code, error_message, nodeid=nodeid, proof=proof, pubkey=proof_master, delegation=None): assert_raises_rpc_error( error_code, error_message, node.addavalanchenode, nodeid, pubkey, proof, delegation, ) self.log.info("Invalid proof") check_addavalanchenode_error(-22, "Proof must be an hexadecimal string", proof="not a proof") check_addavalanchenode_error(-22, "Proof has invalid format", proof="f000") no_stake = node.buildavalancheproof(proof_sequence, proof_expiration, proof_master, []) check_addavalanchenode_error(-8, "The proof is invalid: no-stake", proof=no_stake) self.log.info("Node doesn't exist") check_addavalanchenode_error(-8, f"The node does not exist: {nodeid + 1}", nodeid=nodeid + 1) self.log.info("Invalid delegation") dg_privkey = ECKey() dg_privkey.generate() dg_pubkey = dg_privkey.get_pubkey().get_bytes() check_addavalanchenode_error( -22, "Delegation must be an hexadecimal string", pubkey=dg_pubkey.hex(), delegation="not a delegation") check_addavalanchenode_error(-22, "Delegation has invalid format", pubkey=dg_pubkey.hex(), delegation="f000") self.log.info("Delegation mismatch with the proof") delegation_wrong_proofid = AvalancheDelegation() check_addavalanchenode_error( -8, "The delegation does not match the proof", pubkey=dg_pubkey.hex(), delegation=delegation_wrong_proofid.serialize().hex()) proofobj = FromHex(AvalancheProof(), proof) delegation = AvalancheDelegation( limited_proofid=proofobj.limited_proofid, proof_master=proofobj.master, ) self.log.info("Delegation with bad signature") bad_level = AvalancheDelegationLevel(pubkey=dg_pubkey, ) delegation.levels.append(bad_level) check_addavalanchenode_error(-8, "The delegation is invalid", pubkey=dg_pubkey.hex(), delegation=delegation.serialize().hex()) delegation.levels = [] level = AvalancheDelegationLevel(pubkey=dg_pubkey, sig=privkey.sign_schnorr( hash256(delegation.getid() + ser_string(dg_pubkey)))) delegation.levels.append(level) self.log.info("Key mismatch with the proof") check_addavalanchenode_error( -5, "The public key does not match the proof", pubkey=dg_pubkey.hex(), ) self.log.info("Key mismatch with the delegation") random_privkey = ECKey() random_privkey.generate() random_pubkey = random_privkey.get_pubkey() check_addavalanchenode_error( -5, "The public key does not match the delegation", pubkey=random_pubkey.get_bytes().hex(), delegation=delegation.serialize().hex(), ) self.log.info("Happy path") assert node.addavalanchenode(nodeid, proof_master, proof) # Adding several times is OK assert node.addavalanchenode(nodeid, proof_master, proof) # Use an hardcoded proof. This will help detecting proof format changes. # Generated using: # stakes = create_coinbase_stakes(node, [blockhashes[1]], addrkey0.key) # hardcoded_proof = node.buildavalancheproof( # proof_sequence, proof_expiration, random_pubkey, stakes) hardcoded_pubkey = "037d20fcfe118296bb53f0a8f87c864e7b9831c4fcd7c6a0bb9a58e0e0f53d5cbc" hardcoded_proof = ( "2a00000000000000009435770000000021037d20fcfe118296bb53f0a8f87c864e" "7b9831c4fcd7c6a0bb9a58e0e0f53d5cbc01683ef49024cf25bb55775b327f5e68" "c79da3a7824dc03df5623c96f4a60158f90000000000f902950000000095010000" "210227d85ba011276cf25b51df6a188b75e604b38770a462b2d0e9fb2fc839ef5d" "3f612834ef0e2545d6359e9f34967c2bb69cb88fe246fed716d998f3f62eba1ef6" "6a547606a7ac14c1b5697f4acc20853b3f99954f4f7b6e9bf8a085616d3adfc7") assert node.addavalanchenode(nodeid, hardcoded_pubkey, hardcoded_proof) self.log.info("Add a node with a valid delegation") assert node.addavalanchenode( nodeid, dg_pubkey.hex(), proof, delegation.serialize().hex(), ) self.log.info("Several nodes can share a proof") nodeid2 = add_interface_node(node) assert node.addavalanchenode(nodeid2, proof_master, proof)
def send_islock(self, islock): hash = uint256_from_str(hash256(islock.serialize())) self.islocks[hash] = islock inv = msg_inv([CInv(30, hash)]) self.send_message(inv)
def send_tx(self, tx): hash = uint256_from_str(hash256(tx.serialize())) self.txes[hash] = tx inv = msg_inv([CInv(30, hash)]) self.send_message(inv)
def hash256_int(x): return int.from_bytes(hash256(x), 'little')
def compute_last_header(prev_header, hashes): """Compute the last filter header from a starting header and a sequence of filter hashes.""" header = ser_uint256(prev_header) for filter_hash in hashes: header = hash256(ser_uint256(filter_hash) + header) return uint256_from_str(header)
def decodescript_script_pub_key(self): public_key = '03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2' push_public_key = '21' + public_key public_key_hash = '5dd1d3a048119c27b28293056724d9522f26d945' push_public_key_hash = '14' + public_key_hash uncompressed_public_key = '04b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb25e01fc8fde47c96c98a4f3a8123e33a38a50cf9025cc8c4494a518f991792bb7' push_uncompressed_public_key = '41' + uncompressed_public_key p2wsh_p2pk_script_hash = 'd8590cf8ea0674cf3d49fd7ca249b85ef7485dea62c138468bddeb20cd6519f7' p2wpk_script_hash = '91659559d38e91cccab6f5685dc9df0814740bfc' # below are test cases for all of the standard transaction types # 1) P2PK scriptPubKey # <pubkey> OP_CHECKSIG rpc_result = self.nodes[0].decodescript(push_public_key + 'ac') assert_equal(public_key + ' OP_CHECKSIG', rpc_result['asm']) # P2PK is translated to P2WPK assert_equal('0 ' + p2wpk_script_hash, rpc_result['segwit']['asm']) # 2) P2PKH scriptPubKey # OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG rpc_result = self.nodes[0].decodescript('76a9' + push_public_key_hash + '88ac') assert_equal('OP_DUP OP_HASH160 ' + public_key_hash + ' OP_EQUALVERIFY OP_CHECKSIG', rpc_result['asm']) # P2PKH can't be automatically mapped to a segwit address assert('segwit' not in rpc_result) # 3) multisig scriptPubKey # <m> <A pubkey> <B pubkey> <C pubkey> <n> OP_CHECKMULTISIG # just imagine that the pub keys used below are different. # for our purposes here it does not matter that they are the same even though it is unrealistic. multisig_script = '52' + push_public_key + push_public_key + push_public_key + '53ae' rpc_result = self.nodes[0].decodescript(multisig_script) assert_equal('2 ' + public_key + ' ' + public_key + ' ' + public_key + ' 3 OP_CHECKMULTISIG', rpc_result['asm']) # multisig in P2WSH multisig_script_hash = bytes_to_hex_str(hash256(hex_str_to_bytes('00' + multisig_script))) assert_equal('0 ' + multisig_script_hash, rpc_result['segwit']['asm']) # 4) P2SH scriptPubKey # OP_HASH160 <Hash160(redeemScript)> OP_EQUAL. # push_public_key_hash here should actually be the hash of a redeem script. # but this works the same for purposes of this test. rpc_result = self.nodes[0].decodescript('a9' + push_public_key_hash + '87') assert_equal('OP_HASH160 ' + public_key_hash + ' OP_EQUAL', rpc_result['asm']) # P2SH does not work in segwit secripts. decodescript should not return a result for it. assert 'segwit' not in rpc_result # 5) null data scriptPubKey # use a signature look-alike here to make sure that we do not decode random data as a signature. # this matters if/when signature sighash decoding comes along. # would want to make sure that no such decoding takes place in this case. signature_imposter = '48304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c509001' # OP_RETURN <data> rpc_result = self.nodes[0].decodescript('6a' + signature_imposter) assert_equal('OP_RETURN ' + signature_imposter[2:], rpc_result['asm']) # 6) a CLTV redeem script. redeem scripts are in-effect scriptPubKey scripts, so adding a test here. # OP_NOP2 is also known as OP_CHECKLOCKTIMEVERIFY. # just imagine that the pub keys used below are different. # for our purposes here it does not matter that they are the same even though it is unrealistic. # # OP_IF # <receiver-pubkey> OP_CHECKSIGVERIFY # OP_ELSE # <lock-until> OP_CHECKLOCKTIMEVERIFY OP_DROP # OP_ENDIF # <sender-pubkey> OP_CHECKSIG # # lock until block 500,000 cltv_script = '63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac' rpc_result = self.nodes[0].decodescript(cltv_script) assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm']) # CLTV script in P2WSH cltv_script_hash = bytes_to_hex_str(hash256(hex_str_to_bytes('00' + cltv_script))) assert_equal('0 ' + cltv_script_hash, rpc_result['segwit']['asm']) # 7) P2PK scriptPubKey # <pubkey> OP_CHECKSIG rpc_result = self.nodes[0].decodescript(push_uncompressed_public_key + 'ac') assert_equal(uncompressed_public_key + ' OP_CHECKSIG', rpc_result['asm']) # uncompressed pubkeys are invalid for checksigs in segwit scripts. # decodescript should not return a P2WPKH equivalent. assert 'segwit' not in rpc_result # 8) multisig scriptPubKey with an uncompressed pubkey # <m> <A pubkey> <B pubkey> <n> OP_CHECKMULTISIG # just imagine that the pub keys used below are different. # the purpose of this test is to check that a segwit script is not returned for bare multisig scripts # with an uncompressed pubkey in them. rpc_result = self.nodes[0].decodescript('52' + push_public_key + push_uncompressed_public_key +'52ae') assert_equal('2 ' + public_key + ' ' + uncompressed_public_key + ' 2 OP_CHECKMULTISIG', rpc_result['asm']) # uncompressed pubkeys are invalid for checksigs in segwit scripts. # decodescript should not return a P2WPKH equivalent. assert 'segwit' not in rpc_result # 9) P2WPKH scriptpubkey # 0 <PubKeyHash> rpc_result = self.nodes[0].decodescript('00' + push_public_key_hash) assert_equal('0 ' + public_key_hash, rpc_result['asm']) # segwit scripts do not work nested into each other. # a nested segwit script should not be returned in the results. assert 'segwit' not in rpc_result # 10) P2WSH scriptpubkey # 0 <ScriptHash> # even though this hash is of a P2PK script which is better used as bare P2WPKH, it should not matter # for the purpose of this test. rpc_result = self.nodes[0].decodescript('0020' + p2wsh_p2pk_script_hash) assert_equal('0 ' + p2wsh_p2pk_script_hash, rpc_result['asm']) # segwit scripts do not work nested into each other. # a nested segwit script should not be returned in the results. assert 'segwit' not in rpc_result
def hash256_reversed(byte_str): return hash256(byte_str)[::-1]
def run_test(self): # Node 0 supports COMPACT_FILTERS, node 1 does not. node0 = self.nodes[0].add_p2p_connection(CFiltersClient()) node1 = self.nodes[1].add_p2p_connection(CFiltersClient()) # Nodes 0 & 1 share the same first 999 blocks in the chain. self.nodes[0].generate(999) sync_blocks(self.nodes) # Stale blocks by disconnecting nodes 0 & 1, mining, then reconnecting disconnect_nodes(self.nodes[0], 1) self.nodes[0].generate(1) wait_until(lambda: self.nodes[0].getblockcount() == 1000) stale_block_hash = self.nodes[0].getblockhash(1000) self.nodes[1].generate(1001) wait_until(lambda: self.nodes[1].getblockcount() == 2000) # Fetch cfcheckpt on node 0. Since the implementation caches the checkpoints on the active # chain in memory, this checks that the cache is updated correctly upon subsequent queries # after the reorg. request = msg_getcfcheckpt(filter_type=FILTER_TYPE_BASIC, stop_hash=int(stale_block_hash, 16)) node0.send_message(request) node0.sync_with_ping(timeout=5) response = node0.last_message['cfcheckpt'] assert_equal(response.filter_type, request.filter_type) assert_equal(response.stop_hash, request.stop_hash) assert_equal(len(response.headers), 1) # Reorg node 0 to a new chain connect_nodes(self.nodes[0], 1) sync_blocks(self.nodes) main_block_hash = self.nodes[0].getblockhash(1000) assert main_block_hash != stale_block_hash, "node 0 chain did not reorganize" default_services = node1.nServices # Check that nodes have signalled expected services. assert 0 == node1.nServices & NODE_COMPACT_FILTERS assert_equal(node0.nServices, default_services | NODE_COMPACT_FILTERS) # Check that the localservices is as expected. assert_equal(int(self.nodes[0].getnetworkinfo()['localservices'], 16), default_services | NODE_COMPACT_FILTERS) assert_equal(int(self.nodes[1].getnetworkinfo()['localservices'], 16), default_services) # Check that peers can fetch cfcheckpt on active chain. tip_hash = self.nodes[0].getbestblockhash() request = msg_getcfcheckpt(filter_type=FILTER_TYPE_BASIC, stop_hash=int(tip_hash, 16)) node0.send_message(request) node0.sync_with_ping() response = node0.last_message['cfcheckpt'] assert_equal(response.filter_type, request.filter_type) assert_equal(response.stop_hash, request.stop_hash) main_cfcheckpt = self.nodes[0].getblockfilter(main_block_hash, 'basic')['header'] tip_cfcheckpt = self.nodes[0].getblockfilter(tip_hash, 'basic')['header'] assert_equal( response.headers, [int(header, 16) for header in (main_cfcheckpt, tip_cfcheckpt)]) # Check that peers can fetch cfcheckpt on stale chain. request = msg_getcfcheckpt(filter_type=FILTER_TYPE_BASIC, stop_hash=int(stale_block_hash, 16)) node0.send_message(request) node0.sync_with_ping() response = node0.last_message['cfcheckpt'] stale_cfcheckpt = self.nodes[0].getblockfilter(stale_block_hash, 'basic')['header'] assert_equal(response.headers, [int(header, 16) for header in (stale_cfcheckpt, )]) # Check that peers can fetch cfheaders on active chain. request = msg_getcfheaders(filter_type=FILTER_TYPE_BASIC, start_height=1, stop_hash=int(main_block_hash, 16)) node0.send_message(request) node0.sync_with_ping() response = node0.last_message['cfheaders'] main_cfhashes = response.hashes assert_equal( compute_last_header(response.prev_header, response.hashes), int(main_cfcheckpt, 16)) # Check that peers can fetch cfheaders on stale chain. request = msg_getcfheaders(filter_type=FILTER_TYPE_BASIC, start_height=1, stop_hash=int(stale_block_hash, 16)) node0.send_message(request) node0.sync_with_ping() response = node0.last_message['cfheaders'] stale_cfhashes = response.hashes assert_equal( compute_last_header(response.prev_header, response.hashes), int(stale_cfcheckpt, 16)) # Check that peers can fetch cfilters. stop_hash = self.nodes[0].getblockhash(10) request = msg_getcfilters(filter_type=FILTER_TYPE_BASIC, start_height=1, stop_hash=int(stop_hash, 16)) node0.send_message(request) node0.sync_with_ping() response = node0.pop_cfilters() assert_equal(len(response), 10) # Check that cfilter responses are correct. for cfilter, cfhash, height in zip(response, main_cfhashes, range(1, 11)): block_hash = self.nodes[0].getblockhash(height) assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC) assert_equal(cfilter.block_hash, int(block_hash, 16)) computed_cfhash = uint256_from_str(hash256(cfilter.filter_data)) assert_equal(computed_cfhash, cfhash) # Check that peers can fetch cfilters for stale blocks. stop_hash = self.nodes[0].getblockhash(10) request = msg_getcfilters(filter_type=FILTER_TYPE_BASIC, start_height=1000, stop_hash=int(stale_block_hash, 16)) node0.send_message(request) node0.sync_with_ping() response = node0.pop_cfilters() assert_equal(len(response), 1) cfilter = response[0] assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC) assert_equal(cfilter.block_hash, int(stale_block_hash, 16)) computed_cfhash = uint256_from_str(hash256(cfilter.filter_data)) assert_equal(computed_cfhash, stale_cfhashes[999]) # Requests to node 1 without NODE_COMPACT_FILTERS results in disconnection. requests = [ msg_getcfcheckpt(filter_type=FILTER_TYPE_BASIC, stop_hash=int(main_block_hash, 16)), msg_getcfheaders(filter_type=FILTER_TYPE_BASIC, start_height=1000, stop_hash=int(main_block_hash, 16)), msg_getcfilters(filter_type=FILTER_TYPE_BASIC, start_height=1000, stop_hash=int(main_block_hash, 16)), ] node1.sync_with_ping( ) # ensure 'ping' has at least one message before we copy node1_check_message_count = dict(node1.message_count) node1_check_message_count['pong'] += 1 for request in requests: node1.send_message(request) node1.sync_with_ping() assert_equal(node1_check_message_count, dict(node1.message_count)) # Check that invalid requests result in disconnection. requests = [ # Requesting too many filters results in disconnection. msg_getcfilters(filter_type=FILTER_TYPE_BASIC, start_height=0, stop_hash=int(main_block_hash, 16)), # Requesting too many filter headers results in disconnection. msg_getcfheaders(filter_type=FILTER_TYPE_BASIC, start_height=0, stop_hash=int(tip_hash, 16)), # Requesting unknown filter type results in disconnection. msg_getcfcheckpt(filter_type=255, stop_hash=int(main_block_hash, 16)), # Requesting unknown hash results in disconnection. msg_getcfcheckpt( filter_type=FILTER_TYPE_BASIC, stop_hash=123456789, ), ] for request in requests: node0 = self.nodes[0].add_p2p_connection(CFiltersClient()) node0.send_message(request) node0.wait_for_disconnect()