Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
    def commits_test(self, node):
        def check_headers(number):
            wait_until(lambda: node.getblockchaininfo()['headers'] == number, timeout=5)

        def check_reject(err, block):
            wait_until(lambda: node.p2p.has_reject(err, block), timeout=5)

        def getbestblockhash():
            return int(node.getbestblockhash(), 16)

        def make_block(prev=None, secret=None):
            if secret is None:
                secret = "default"
            coinbase_key = CECKey()
            coinbase_key.set_secretbytes(bytes(secret, "utf-8"))
            coinbase_pubkey = coinbase_key.get_pubkey()
            if prev is None:
                block_base_hash = getbestblockhash()
                block_time = int(time.time()) + 1
            else:
                block_base_hash = prev.sha256
                block_time = prev.nTime + 1
            height = prev.height + 1 if prev else 1
            snapshot_hash = 0
            stake = self.nodes[0].listunspent()[0]
            coinbase = create_coinbase(height, stake, snapshot_hash, coinbase_pubkey)
            coinbase.rehash()
            b = create_block(block_base_hash, coinbase, block_time)
            b.solve()
            b.height = height
            return b

        def make_commits_msg(blocks):
            msg = msg_commits(0)
            for b in blocks:
                hc = HeaderAndCommits()
                hc.header = CBlockHeader(b)
                msg.data += [hc]
            return msg

        def send_commits(blocks):
            node.p2p.reset_messages()
            node.p2p.send_message(make_commits_msg(blocks))

        chain = []

        def generate(n):
            tip = chain[-1] if len(chain) > 0 else None
            for i in range(0, n):
                tip = make_block(tip)
                chain.append(tip)

        check_headers(0) # initial state of the node

        # generate 10 blocks and send commits
        generate(10)
        send_commits(chain)
        check_headers(10) # node accepted 10 headers

        # send same commits again
        send_commits(chain)
        check_headers(10)

        # send last 5 commits
        send_commits(chain[-5:])
        check_headers(10)

        # generate next 10 blocks, try to send commits starting from 2nd block
        generate(10)
        send_commits(chain[11:])
        check_reject(b'prev-blk-not-found', 0)  # node rejected orphan headers
        check_headers(10) # must keep old amount of headers

        # send correct commits
        send_commits(chain[10:])
        check_headers(20) # node accepted headers

        # generate next 10 blocks, send whole chain
        generate(10)
        send_commits(chain)
        check_headers(30) # node accepted headers

        # generate next 10 blocks, fool commit in one of them, send them
        generate(10)
        msg = make_commits_msg(chain[-10:])
        malicious_block = copy.deepcopy(chain[-1])
        msg.data[-1].commits = malicious_block.vtx # fool commits with coinbase tx
        tx = malicious_block.vtx[0]
        tx.calc_sha256()
        hashes = [ser_uint256(tx.sha256)]
        malicious_block.hash_finalizer_commits_merkle_root = CBlock.get_merkle_root(hashes)
        malicious_block.rehash()
        msg.data[-1].header.hash_finalizer_commits_merkle_root = malicious_block.hash_finalizer_commits_merkle_root
        node.p2p.send_message(msg)
        check_reject(b'bad-non-commit', malicious_block.sha256) # node rejected commits because of non-commit transaction
        check_headers(30) # must keep old amount of headers

        # send commits with bad merkle root
        msg = make_commits_msg(chain[-10:])
        malicious_block = copy.deepcopy(chain[-2])
        malicious_block.hash_finalizer_commits_merkle_root = 42
        malicious_block.rehash()
        msg.data[-2].header.hash_finalizer_commits_merkle_root = malicious_block.hash_finalizer_commits_merkle_root
        node.p2p.send_message(msg)
        check_reject(b'bad-finalizer-commits-merkle-root', malicious_block.sha256) # node rejected commits because of bad commits merkle root
        check_headers(30) # must keep old amount of headers
Exemplo n.º 4
0
    def run_test(self):
        node = self.nodes[0]
        self.wallet = MiniWallet(node)
        self.mine_chain()

        def assert_submitblock(block, result_str_1, result_str_2=None):
            block.solve()
            result_str_2 = result_str_2 or 'duplicate-invalid'
            assert_equal(result_str_1,
                         node.submitblock(hexdata=block.serialize().hex()))
            assert_equal(result_str_2,
                         node.submitblock(hexdata=block.serialize().hex()))

        self.log.info('getmininginfo')
        mining_info = node.getmininginfo()
        assert_equal(mining_info['blocks'], 200)
        assert_equal(mining_info['chain'], self.chain)
        assert 'currentblocktx' not in mining_info
        assert 'currentblockweight' not in mining_info
        assert 'currentblocksize' not in mining_info
        assert_equal(mining_info['difficulty'],
                     Decimal('4.656542373906925E-10'))
        assert_equal(mining_info['networkhashps'],
                     Decimal('0.003333333333333334'))
        assert_equal(mining_info['pooledtx'], 0)

        self.log.info("getblocktemplate: Test default witness commitment")
        txid = int(self.wallet.send_self_transfer(from_node=node)['wtxid'], 16)
        tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)

        # Check that default_witness_commitment is present.
        assert 'default_witness_commitment' in tmpl
        witness_commitment = tmpl['default_witness_commitment']

        # Check that default_witness_commitment is correct.
        witness_root = CBlock.get_merkle_root(
            [ser_uint256(0), ser_uint256(txid)])
        script = get_witness_script(witness_root, 0)
        assert_equal(witness_commitment, script.hex())

        # Mine a block to leave initial block download and clear the mempool
        self.generatetoaddress(node, 1,
                               node.get_deterministic_priv_key().address)
        tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
        self.log.info("getblocktemplate: Test capability advertised")
        assert 'proposal' in tmpl['capabilities']
        assert 'coinbasetxn' not in tmpl

        next_height = int(tmpl["height"])
        coinbase_tx = create_coinbase(height=next_height)
        # sequence numbers must not be max for nLockTime to have effect
        coinbase_tx.vin[0].nSequence = 2**32 - 2
        coinbase_tx.rehash()

        block = CBlock()
        block.nVersion = tmpl["version"]
        block.hashPrevBlock = int(tmpl["previousblockhash"], 16)
        block.nTime = tmpl["curtime"]
        block.nBits = int(tmpl["bits"], 16)
        block.nNonce = 0
        block.vtx = [coinbase_tx]

        self.log.info("getblocktemplate: segwit rule must be set")
        assert_raises_rpc_error(
            -8, "getblocktemplate must be called with the segwit rule set",
            node.getblocktemplate)

        self.log.info("getblocktemplate: Test valid block")
        assert_template(node, block, None)

        self.log.info("submitblock: Test block decode failure")
        assert_raises_rpc_error(-22, "Block decode failed", node.submitblock,
                                block.serialize()[:-15].hex())

        self.log.info(
            "getblocktemplate: Test bad input hash for coinbase transaction")
        bad_block = copy.deepcopy(block)
        bad_block.vtx[0].vin[0].prevout.hash += 1
        bad_block.vtx[0].rehash()
        assert_template(node, bad_block, 'bad-cb-missing')

        self.log.info("submitblock: Test invalid coinbase transaction")
        assert_raises_rpc_error(-22, "Block does not start with a coinbase",
                                node.submitblock,
                                bad_block.serialize().hex())

        self.log.info("getblocktemplate: Test truncated final transaction")
        assert_raises_rpc_error(
            -22, "Block decode failed", node.getblocktemplate, {
                'data': block.serialize()[:-1].hex(),
                'mode': 'proposal',
                'rules': ['segwit'],
            })

        self.log.info("getblocktemplate: Test duplicate transaction")
        bad_block = copy.deepcopy(block)
        bad_block.vtx.append(bad_block.vtx[0])
        assert_template(node, bad_block, 'bad-txns-duplicate')
        assert_submitblock(bad_block, 'bad-txns-duplicate',
                           'bad-txns-duplicate')

        self.log.info("getblocktemplate: Test invalid transaction")
        bad_block = copy.deepcopy(block)
        bad_tx = copy.deepcopy(bad_block.vtx[0])
        bad_tx.vin[0].prevout.hash = 255
        bad_tx.rehash()
        bad_block.vtx.append(bad_tx)
        assert_template(node, bad_block, 'bad-txns-inputs-missingorspent')
        assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent')

        self.log.info("getblocktemplate: Test nonfinal transaction")
        bad_block = copy.deepcopy(block)
        bad_block.vtx[0].nLockTime = 2**32 - 1
        bad_block.vtx[0].rehash()
        assert_template(node, bad_block, 'bad-txns-nonfinal')
        assert_submitblock(bad_block, 'bad-txns-nonfinal')

        self.log.info("getblocktemplate: Test bad tx count")
        # The tx count is immediately after the block header
        bad_block_sn = bytearray(block.serialize())
        assert_equal(bad_block_sn[BLOCK_HEADER_SIZE], 1)
        bad_block_sn[BLOCK_HEADER_SIZE] += 1
        assert_raises_rpc_error(-22, "Block decode failed",
                                node.getblocktemplate, {
                                    'data': bad_block_sn.hex(),
                                    'mode': 'proposal',
                                    'rules': ['segwit'],
                                })

        self.log.info("getblocktemplate: Test bad bits")
        bad_block = copy.deepcopy(block)
        bad_block.nBits = 469762303  # impossible in the real world
        assert_template(node, bad_block, 'bad-diffbits')

        self.log.info("getblocktemplate: Test bad merkle root")
        bad_block = copy.deepcopy(block)
        bad_block.hashMerkleRoot += 1
        assert_template(node, bad_block, 'bad-txnmrklroot', False)
        assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot')

        self.log.info("getblocktemplate: Test bad timestamps")
        bad_block = copy.deepcopy(block)
        bad_block.nTime = 2**31 - 1
        assert_template(node, bad_block, 'time-too-new')
        assert_submitblock(bad_block, 'time-too-new', 'time-too-new')
        bad_block.nTime = 0
        assert_template(node, bad_block, 'time-too-old')
        assert_submitblock(bad_block, 'time-too-old', 'time-too-old')

        self.log.info("getblocktemplate: Test not best block")
        bad_block = copy.deepcopy(block)
        bad_block.hashPrevBlock = 123
        assert_template(node, bad_block, 'inconclusive-not-best-prevblk')
        assert_submitblock(bad_block, 'prev-blk-not-found',
                           'prev-blk-not-found')

        self.log.info('submitheader tests')
        assert_raises_rpc_error(
            -22, 'Block header decode failed',
            lambda: node.submitheader(hexdata='xx' * BLOCK_HEADER_SIZE))
        assert_raises_rpc_error(
            -22, 'Block header decode failed',
            lambda: node.submitheader(hexdata='ff' * (BLOCK_HEADER_SIZE - 2)))
        assert_raises_rpc_error(
            -25, 'Must submit previous header', lambda: node.submitheader(
                hexdata=super(CBlock, bad_block).serialize().hex()))

        block.nTime += 1
        block.solve()

        def chain_tip(b_hash, *, status='headers-only', branchlen=1):
            return {
                'hash': b_hash,
                'height': 202,
                'branchlen': branchlen,
                'status': status
            }

        assert chain_tip(block.hash) not in node.getchaintips()
        node.submitheader(hexdata=block.serialize().hex())
        assert chain_tip(block.hash) in node.getchaintips()
        node.submitheader(
            hexdata=CBlockHeader(block).serialize().hex())  # Noop
        assert chain_tip(block.hash) in node.getchaintips()

        bad_block_root = copy.deepcopy(block)
        bad_block_root.hashMerkleRoot += 2
        bad_block_root.solve()
        assert chain_tip(bad_block_root.hash) not in node.getchaintips()
        node.submitheader(
            hexdata=CBlockHeader(bad_block_root).serialize().hex())
        assert chain_tip(bad_block_root.hash) in node.getchaintips()
        # Should still reject invalid blocks, even if we have the header:
        assert_equal(
            node.submitblock(hexdata=bad_block_root.serialize().hex()),
            'bad-txnmrklroot')
        assert_equal(
            node.submitblock(hexdata=bad_block_root.serialize().hex()),
            'bad-txnmrklroot')
        assert chain_tip(bad_block_root.hash) in node.getchaintips()
        # We know the header for this invalid block, so should just return early without error:
        node.submitheader(
            hexdata=CBlockHeader(bad_block_root).serialize().hex())
        assert chain_tip(bad_block_root.hash) in node.getchaintips()

        bad_block_lock = copy.deepcopy(block)
        bad_block_lock.vtx[0].nLockTime = 2**32 - 1
        bad_block_lock.vtx[0].rehash()
        bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root()
        bad_block_lock.solve()
        assert_equal(
            node.submitblock(hexdata=bad_block_lock.serialize().hex()),
            'bad-txns-nonfinal')
        assert_equal(
            node.submitblock(hexdata=bad_block_lock.serialize().hex()),
            'duplicate-invalid')
        # Build a "good" block on top of the submitted bad block
        bad_block2 = copy.deepcopy(block)
        bad_block2.hashPrevBlock = bad_block_lock.sha256
        bad_block2.solve()
        assert_raises_rpc_error(
            -25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(
                bad_block2).serialize().hex()))

        # Should reject invalid header right away
        bad_block_time = copy.deepcopy(block)
        bad_block_time.nTime = 1
        bad_block_time.solve()
        assert_raises_rpc_error(
            -25, 'time-too-old', lambda: node.submitheader(
                hexdata=CBlockHeader(bad_block_time).serialize().hex()))

        # Should ask for the block from a p2p node, if they announce the header as well:
        peer = node.add_p2p_connection(P2PDataStore())
        peer.wait_for_getheaders(timeout=5)  # Drop the first getheaders
        peer.send_blocks_and_test(blocks=[block], node=node)
        # Must be active now:
        assert chain_tip(block.hash, status='active',
                         branchlen=0) in node.getchaintips()

        # Building a few blocks should give the same results
        self.generatetoaddress(node, 10,
                               node.get_deterministic_priv_key().address)
        assert_raises_rpc_error(
            -25, 'time-too-old', lambda: node.submitheader(
                hexdata=CBlockHeader(bad_block_time).serialize().hex()))
        assert_raises_rpc_error(
            -25, 'bad-prevblk', lambda: node.submitheader(hexdata=CBlockHeader(
                bad_block2).serialize().hex()))
        node.submitheader(hexdata=CBlockHeader(block).serialize().hex())
        node.submitheader(
            hexdata=CBlockHeader(bad_block_root).serialize().hex())
        assert_equal(node.submitblock(hexdata=block.serialize().hex()),
                     'duplicate')  # valid