git / moonbite-core / test/functional/mweb_mining.py · raw

#!/usr/bin/env python3# Copyright (c) 2014-2020 The Bitcoin Core developers# Distributed under the MIT software license, see the accompanying# file COPYING or http://www.opensource.org/licenses/mit-license.php."""Test mining RPCs for MWEB blocks"""from decimal import Decimalfrom test_framework.blocktools import (create_coinbase, NORMAL_GBT_REQUEST_PARAMS)from test_framework.messages import (CBlock, MWEBBlock)from test_framework.test_framework import BitcoinTestFrameworkfrom test_framework.util import assert_equal, assert_raises_rpc_errorfrom test_framework.ltc_util import (    FIRST_MWEB_HEIGHT,    create_hogex,    get_hogex_tx,    get_mweb_header,)class MWEBMiningTest(BitcoinTestFramework):    def set_test_params(self):        self.num_nodes = 3        self.setup_clean_chain = True        self.supports_cli = False    def skip_test_if_missing_module(self):        self.skip_if_no_wallet()    def run_test(self):        node = self.nodes[0]        self.log.info("Mine first MWEB block")        node.generate(FIRST_MWEB_HEIGHT - 1)        first_pegin_txid = node.sendtoaddress(node.getnewaddress(address_type='mweb'), 1)        self.log.info("getblocktemplate: mweb rule is required for MWEB templates")        assert_raises_rpc_error(            -8,            "getblocktemplate must be called with the segwit & mweb rule sets",            node.getblocktemplate,            {'rules': ['segwit']},        )        first_template = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)        assert "mweb" in first_template        assert "mweb" in first_template["rules"]        first_mweb_block = node.generate(1)[0]        first_hogex = get_hogex_tx(node, first_mweb_block)        assert_equal(len(first_hogex.vin), 1)        assert_equal(first_hogex.vin[0].prevout.hash, int(first_pegin_txid, 16))        self.sync_all()        # Call getblocktemplate        node.generatetoaddress(1, node.get_deterministic_priv_key().address)        gbt = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)        next_height = int(gbt["height"])        # Build MWEB block        mweb_header = get_mweb_header(node)        mweb_header.height = next_height        mweb_header.rehash()        mweb_block = MWEBBlock(mweb_header)        # Build coinbase and HogEx txs        coinbase_tx = create_coinbase(height=next_height)        hogex_tx = create_hogex(node, mweb_header.hash)        vtx = [coinbase_tx, hogex_tx]        # Build block proposal        block = CBlock()        block.nVersion = gbt["version"]        block.hashPrevBlock = int(gbt["previousblockhash"], 16)        block.nTime = gbt["curtime"]        block.nBits = int(gbt["bits"], 16)        block.nNonce = 0        block.vtx = vtx        block.mweb_block = mweb_block        block.hashMerkleRoot = block.calc_merkle_root()        # Call getblocktemplate with the block proposal        self.log.info("getblocktemplate: Test valid block")        rsp = node.getblocktemplate(template_request={            'data': block.serialize().hex(),            'mode': 'proposal',            'rules': ['mweb', 'segwit'],        })        assert_equal(rsp, None)        self.log.info("Mine many pegins in one block")        self.mine_many_pegins()        self.log.info("Mine many pegouts in one block")        self.mine_many_pegouts()        self.log.info("Mine after MWEB mempool spend becomes stale across a reorg")        self.mine_after_stale_mweb_spend_reorg()    def mine_many_pegins(self):        miner = self.nodes[1]        funder = self.nodes[0]        pegin_wallets = []        pegin_funds = {}        for i in range(8):            miner.createwallet(wallet_name=f"pegin_batch_{i}")            pegin_wallet = miner.get_wallet_rpc(f"pegin_batch_{i}")            pegin_wallets.append(pegin_wallet)            pegin_funds[pegin_wallet.getnewaddress()] = Decimal("0.2")        funder.sendmany("", pegin_funds)        funder.generate(1)        self.sync_all()        pegin_txids = []        mweb_addr = funder.getnewaddress(address_type='mweb')        for pegin_wallet in pegin_wallets:            pegin_utxos = [utxo for utxo in pegin_wallet.listunspent(minconf=1) if "vout" in utxo]            assert_equal(len(pegin_utxos), 1)            pegin_txids.append(pegin_wallet.sendtoaddress(mweb_addr, Decimal("0.1")))        assert_equal(set(pegin_txids).issubset(set(miner.getrawmempool())), True)        template = miner.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)        assert template["transactions"][-1]["fee"] > 0        block_hash = miner.generate(1)[0]        block = miner.getblock(block_hash)        hogex = get_hogex_tx(miner, block_hash)        assert_equal(len(hogex.vin), len(pegin_txids) + 1)        for txid in pegin_txids:            assert txid in block["tx"]        self.sync_all()    def mine_many_pegouts(self):        funder = self.nodes[0]        spender = self.nodes[2]        funder.sendtoaddress(spender.getnewaddress(address_type='mweb'), 1)        funder.generate(1)        self.sync_all()        pegout_txids = []        for _ in range(6):            pegout_txids.append(spender.sendtoaddress(funder.getnewaddress(), Decimal("0.05")))        assert_equal(set(pegout_txids).issubset(set(spender.getrawmempool())), True)        block_hash = spender.generate(1)[0]        block = spender.getblock(block_hash)        hogex = get_hogex_tx(spender, block_hash)        assert_equal(block["tx"][-1], hogex.hash)        assert_equal(len(hogex.vout), len(pegout_txids) + 1)        for txid in pegout_txids:            assert txid not in block["tx"]        self.sync_all()    def mine_after_stale_mweb_spend_reorg(self):        miner = self.nodes[0]        fork_miner = self.nodes[1]        miner_wallet = miner.get_wallet_rpc(self.default_wallet_name)        miner.createwallet(wallet_name="reorg_funding")        miner.createwallet(wallet_name="reorg_stale")        funding = miner.get_wallet_rpc("reorg_funding")        stale_wallet = miner.get_wallet_rpc("reorg_stale")        miner_wallet.sendtoaddress(funding.getnewaddress(), Decimal("5"))        miner.generate(1)        self.sync_all()        funding_utxos = funding.listunspent(minconf=1)        assert_equal(len(funding_utxos), 1)        funding_utxo = funding_utxos[0]        conflict_amount = funding_utxo["amount"] - Decimal("0.001")        raw_conflict = miner.createrawtransaction(            [{"txid": funding_utxo["txid"], "vout": funding_utxo["vout"]}],            {funding.getnewaddress(): conflict_amount},        )        signed_conflict = funding.signrawtransactionwithwallet(raw_conflict)["hex"]        conflict_txid = miner.decoderawtransaction(signed_conflict)["txid"]        self.disconnect_nodes(0, 1)        pegin_txid = funding.sendtoaddress(stale_wallet.getnewaddress(address_type='mweb'), Decimal("1"))        pegin_tx = miner.getrawtransaction(pegin_txid, True)        assert {"txid": funding_utxo["txid"], "vout": funding_utxo["vout"]} in [            {"txid": vin["txid"], "vout": vin["vout"]} for vin in pegin_tx["vin"]        ]        pegin_block = miner.generate(1)[0]        assert pegin_txid in miner.getblock(pegin_block)["tx"]        stale_txid = stale_wallet.sendtoaddress(stale_wallet.getnewaddress(address_type='mweb'), Decimal("0.25"))        assert stale_txid in miner.getrawmempool()        fork_miner.sendrawtransaction(signed_conflict)        fork_blocks = fork_miner.generate(2)        assert conflict_txid in fork_miner.getblock(fork_blocks[0])["tx"]        self.connect_nodes(0, 1)        self.sync_blocks()        assert_equal(miner.getbestblockhash(), fork_blocks[-1])        stale_remained = stale_txid in miner.getrawmempool()        if stale_remained:            with miner.assert_debug_log(expected_msgs=["Failed to add MWEB transaction"], timeout=10):                miner.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)        else:            miner.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)        mined_hash = miner.generate(1)[0]        mined_block = miner.getblock(mined_hash)        hogex = get_hogex_tx(miner, mined_hash)        assert_equal(mined_block["tx"][-1], hogex.hash)if __name__ == '__main__':    MWEBMiningTest().main()