def run_test(self):
        n = self.nodes[0]
        self.bootstrap_p2p()

        coinbases = self.mine_blocks(n, 5)

        self.client = create_electrum_connection()

        # Test raw
        for tx in coinbases:
            assert_equal(ToHex(tx), self.client.call(TX_GET, tx.hash))

        # Test verbose.
        # The spec is unclear. It states:
        #
        # "whatever the coin daemon returns when asked for a
        #  verbose form of the raw transaction"
        #
        # Just check the basics.
        for tx in coinbases:
            electrum = self.client.call(TX_GET, tx.hash, True)
            bitcoind = n.getrawtransaction(tx.hash, True)
            assert_equal(bitcoind['txid'], electrum['txid'])
            assert_equal(bitcoind['locktime'], electrum['locktime'])
            assert_equal(bitcoind['size'], electrum['size'])
            assert_equal(bitcoind['hex'], electrum['hex'])
            assert_equal(len(bitcoind['vin']), len(bitcoind['vin']))
            assert_equal(len(bitcoind['vout']), len(bitcoind['vout']))
Exemplo n.º 2
0
    def test_invalid_args(self, electrum_client):
        from test_framework.connectrum.exc import ElectrumErrorResponse
        error_code = "-32602"

        hash_param_methods = ("blockchain.scripthash.get_balance",
                              "blockchain.scripthash.get_history",
                              "blockchain.scripthash.listunspent")

        for method in hash_param_methods:
            assert_raises(ElectrumErrorResponse, electrum_client.call, method,
                          "invalidhash")

            try:
                electrum_client.call(method, "invalidhash")
            except Exception as e:
                print("ERROR:" + str(e))
                assert error_code in str(e)

        # invalid tx
        try:
            tx = CTransaction()
            tx.calc_sha256()
            tx.vin = [CTxIn(COutPoint(0xbeef, 1))]
            electrum_client.call("blockchain.transaction.broadcast", ToHex(tx))
        except Exception as e:
            print("ERROR: " + str(e))
            assert error_code in str(e)
    def create_cashaccount_tx(self, n, spend, name, address):
        fee = 500
        tx = create_transaction(
            FromHex(CTransaction(), n.getrawtransaction(spend['txid'])),
            spend['vout'], b"", spend['satoshi'] - fee)

        _, _, keyhash = decode_addr(address)

        name = bytes(name, 'ascii')
        keyhash = DATATYPE_KEYHASH + keyhash

        cashaccount_script = CScript(
            [OP_RETURN, CASHACCONT_PREFIX, name, keyhash])

        tx.vout.append(CTxOut(0, cashaccount_script))
        tx.rehash()
        return n.signrawtransaction(ToHex(tx))['hex']
Exemplo n.º 4
0
        async def async_tests():
            cli = ElectrumConnection()
            await cli.connect()

            # Test raw
            for tx in coinbases:
                assert_equal(ToHex(tx), await cli.call(TX_GET, tx.hash))

            # Test verbose.
            # The spec is unclear. It states:
            #
            # "whatever the coin daemon returns when asked for a
            #  verbose form of the raw transaction"
            #
            # Just check the basics.
            for tx in coinbases:
                electrum = await cli.call(TX_GET, tx.hash, True)
                bitcoind = n.getrawtransaction(tx.hash, True)
                assert_equal(bitcoind['txid'], electrum['txid'])
                assert_equal(bitcoind['locktime'], electrum['locktime'])
                assert_equal(bitcoind['size'], electrum['size'])
                assert_equal(bitcoind['hex'], electrum['hex'])
                assert_equal(len(bitcoind['vin']), len(bitcoind['vin']))
                assert_equal(len(bitcoind['vout']), len(bitcoind['vout']))
Exemplo n.º 5
0
    def run_test(self):
        logging.info("Initializing test directory " + self.options.tmpdir)
        node = self.nodes[0]

        self.bootstrap_p2p()

        tip = self.getbestblock(node)

        logging.info("Create some blocks with OP_1 coinbase for spending.")
        blocks = []
        for _ in range(10):
            tip = self.build_block(tip)
            blocks.append(tip)
        self.p2p.send_blocks_and_test(blocks, node, success=True)
        spendable_outputs = [block.vtx[0] for block in blocks]

        logging.info("Mature the blocks and get out of IBD.")
        node.generate(100)

        tip = self.getbestblock(node)

        logging.info("Setting up spends to test and mining the fundings.")
        fundings = []

        # Generate a key pair
        privkeybytes = b"Schnorr!" * 4
        private_key = CECKey()
        private_key.set_secretbytes(privkeybytes)
        # get uncompressed public key serialization
        public_key = private_key.get_pubkey()

        def create_fund_and_spend_tx(dummy=OP_0, sigtype='ecdsa'):
            spendfrom = spendable_outputs.pop()

            script = CScript([OP_1, public_key, OP_1, OP_CHECKMULTISIG])

            value = spendfrom.vout[0].nValue

            # Fund transaction
            txfund = create_transaction(spendfrom, 0, b'', value, script)
            txfund.rehash()
            fundings.append(txfund)

            # Spend transaction
            txspend = CTransaction()
            txspend.vout.append(CTxOut(value - 1000, CScript([OP_TRUE])))
            txspend.vin.append(CTxIn(COutPoint(txfund.sha256, 0), b''))

            # Sign the transaction
            sighashtype = SIGHASH_ALL | SIGHASH_FORKID
            hashbyte = bytes([sighashtype & 0xff])
            sighash = SignatureHashForkId(script, txspend, 0, sighashtype,
                                          value)
            if sigtype == 'schnorr':
                txsig = schnorr.sign(privkeybytes, sighash) + hashbyte
            elif sigtype == 'ecdsa':
                txsig = private_key.sign(sighash) + hashbyte
            txspend.vin[0].scriptSig = CScript([dummy, txsig])
            txspend.rehash()

            return txspend

        # This is valid.
        ecdsa0tx = create_fund_and_spend_tx(OP_0, 'ecdsa')

        # This is invalid.
        ecdsa1tx = create_fund_and_spend_tx(OP_1, 'ecdsa')

        # This is invalid.
        schnorr0tx = create_fund_and_spend_tx(OP_0, 'schnorr')

        # This is valid.
        schnorr1tx = create_fund_and_spend_tx(OP_1, 'schnorr')

        tip = self.build_block(tip, fundings)
        self.p2p.send_blocks_and_test([tip], node)

        logging.info("Send a legacy ECDSA multisig into mempool.")
        self.p2p.send_txs_and_test([ecdsa0tx], node)
        waitFor(10, lambda: node.getrawmempool() == [ecdsa0tx.hash])

        logging.info("Trying to mine a non-null-dummy ECDSA.")
        self.check_for_ban_on_rejected_block(self.build_block(tip, [ecdsa1tx]),
                                             BADINPUTS_ERROR)
        logging.info(
            "If we try to submit it by mempool or RPC, it is rejected and we are banned"
        )
        assert_raises_rpc_error(-26, ECDSA_NULLDUMMY_ERROR,
                                node.sendrawtransaction, ToHex(ecdsa1tx))
        self.check_for_ban_on_rejected_tx(ecdsa1tx, ECDSA_NULLDUMMY_ERROR)

        logging.info(
            "Submitting a Schnorr-multisig via net, and mining it in a block")
        self.p2p.send_txs_and_test([schnorr1tx], node)
        waitFor(
            10, lambda: set(node.getrawmempool()) ==
            {ecdsa0tx.hash, schnorr1tx.hash})
        tip = self.build_block(tip, [schnorr1tx])
        self.p2p.send_blocks_and_test([tip], node)

        logging.info(
            "That legacy ECDSA multisig is still in mempool, let's mine it")
        waitFor(10, lambda: node.getrawmempool() == [ecdsa0tx.hash])
        tip = self.build_block(tip, [ecdsa0tx])
        self.p2p.send_blocks_and_test([tip], node)
        waitFor(10, lambda: node.getrawmempool() == [])

        logging.info(
            "Trying Schnorr in legacy multisig is invalid and banworthy.")
        self.check_for_ban_on_rejected_tx(schnorr0tx,
                                          SCHNORR_LEGACY_MULTISIG_ERROR)
        self.check_for_ban_on_rejected_block(
            self.build_block(tip, [schnorr0tx]), BADINPUTS_ERROR)
Exemplo n.º 6
0
    def run_test(self):
        self.bootstrap_p2p()
        self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
        self.block_heights[self.genesis_hash] = 0
        spendable_outputs = []

        # shorthand
        block = self.next_block
        node_nonstd = self.nodes[0]
        node_std = self.nodes[1]

        # save the current tip so it can be spent by a later block
        def save_spendable_output():
            spendable_outputs.append(self.tip)

        # get an output that we previously marked as spendable
        def get_spendable_output():
            return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0)

        # submit current tip and check it was accepted
        def accepted(self, node):
            self.p2p.send_blocks_and_test([self.tip], node)

        # move the tip back to a previous block
        def tip(number):
            self.tip = self.blocks[number]

        # adds transactions to the block and updates state
        def update_block(block_number, new_transactions):
            block = self.blocks[block_number]
            block.vtx.extend(new_transactions)
            old_sha256 = block.sha256
            make_conform_to_ctor(block)
            block.hashMerkleRoot = block.calc_merkle_root()
            block.solve()
            # Update the internal state just like in next_block
            self.tip = block
            if block.sha256 != old_sha256:
                self.block_heights[
                    block.sha256] = self.block_heights[old_sha256]
                del self.block_heights[old_sha256]
            self.blocks[block_number] = block
            return block

        # checks the mempool has exactly the same txns as in the provided list
        def check_mempool_equal(node, txns):
            assert set(node.getrawmempool()) == set(tx.hash for tx in txns)

        # Returns 2 transactions:
        # 1) txfund: create outputs in segwit addresses
        # 2) txspend: spends outputs from segwit addresses
        def create_segwit_fund_and_spend_tx(spend, case0=False):
            if not case0:
                # Spending from a P2SH-P2WPKH coin,
                #   txhash:a45698363249312f8d3d93676aa714be59b0bd758e62fa054fb1ea6218480691
                redeem_script0 = bytearray.fromhex(
                    '0014fcf9969ce1c98a135ed293719721fb69f0b686cb')
                # Spending from a P2SH-P2WSH coin,
                #   txhash:6b536caf727ccd02c395a1d00b752098ec96e8ec46c96bee8582be6b5060fa2f
                redeem_script1 = bytearray.fromhex(
                    '0020fc8b08ed636cb23afcb425ff260b3abd03380a2333b54cfa5d51ac52d803baf4')
            else:
                redeem_script0 = bytearray.fromhex('51020000')
                redeem_script1 = bytearray.fromhex('53020080')
            redeem_scripts = [redeem_script0, redeem_script1]

            # Fund transaction to segwit addresses
            txfund = CTransaction()
            txfund.vin = [CTxIn(COutPoint(spend.tx.sha256, spend.n))]
            amount = (50 * COIN - 1000) // len(redeem_scripts)
            for redeem_script in redeem_scripts:
                txfund.vout.append(
                    CTxOut(amount, CScript([OP_HASH160, hash160(redeem_script), OP_EQUAL])))
            txfund.rehash()

            # Segwit spending transaction
            # We'll test if a node that checks for standardness accepts this
            # txn. It should fail exclusively because of the restriction in
            # the scriptSig (non clean stack..), so all other characteristcs
            # must pass standardness checks. For this reason, we create
            # standard P2SH outputs.
            txspend = CTransaction()
            for i in range(len(redeem_scripts)):
                txspend.vin.append(
                    CTxIn(COutPoint(txfund.sha256, i), CScript([redeem_scripts[i]])))
            txspend.vout = [CTxOut(50 * COIN - 2000,
                                   CScript([OP_HASH160, hash160(CScript([OP_TRUE])), OP_EQUAL]))]
            txspend.rehash()

            return txfund, txspend

        # Check we are not banned when sending a txn that is rejected.
        def check_for_no_ban_on_rejected_tx(self, node, tx, reject_reason):
            self.p2p.send_txs_and_test(
                [tx], node, success=False, reject_reason=reject_reason)
        def check_for_accepted_tx(self, node, tx):
            self.p2p.send_txs_and_test([tx], node, success=True)

        # Create a new block
        block(0)
        save_spendable_output()
        accepted(self, node_nonstd)

        # Now we need that block to mature so we can spend the coinbase.
        matureblocks = []
        for i in range(199):
            block(5000 + i)
            matureblocks.append(self.tip)
            save_spendable_output()
        self.p2p.send_blocks_and_test(matureblocks, node_nonstd)

        # collect spendable outputs now to avoid cluttering the code later on
        out = []
        for i in range(100):
            out.append(get_spendable_output())

        # Create segwit funding and spending transactions
        txfund, txspend = create_segwit_fund_and_spend_tx(out[0])
        txfund_case0, txspend_case0 = create_segwit_fund_and_spend_tx(
            out[1], True)

        # Mine txfund, as it can't go into node_std mempool because it's
        # nonstandard.
        block(5555)
        update_block(5555, [txfund, txfund_case0])
        # check that current tip is accepted by node0 (nonstd)
        accepted(self, node_nonstd)

        # Check both nodes are synchronized before continuing.
        sync_blocks(self.nodes)

        # Check that upgraded nodes checking for standardness are not banning
        # nodes sending segwit spending txns.
        check_for_no_ban_on_rejected_tx(
            self, node_std, txspend, CLEANSTACK_ERROR)
        check_for_no_ban_on_rejected_tx(
            self, node_std, txspend_case0, EVAL_FALSE_ERROR)

        txspend_id = node_nonstd.decoderawtransaction(ToHex(txspend))["txid"]
        txspend_case0_id = node_nonstd.decoderawtransaction(ToHex(txspend_case0))["txid"]

        # Segwit recovery txns are accept from node that accept not standard txs
        assert_equal(node_nonstd.sendrawtransaction(ToHex(txspend)), txspend_id)
        assert_equal(node_nonstd.sendrawtransaction(ToHex(txspend_case0)), txspend_case0_id)

        # Segwit recovery txs are reject if node does not accept stansard txs
        assert_raises_rpc_error(-26, CLEANSTACK_ERROR,
                                node_std.sendrawtransaction, ToHex(txspend))
        assert_raises_rpc_error(-26, EVAL_FALSE_ERROR,
                                node_std.sendrawtransaction, ToHex(txspend_case0))

        # Blocks containing segwit spending txns are accepted in both nodes.
        self.next_block(5)
        update_block(5, [txspend, txspend_case0])
        accepted(self, node_nonstd)
        sync_blocks(self.nodes)
    def run_test(self):
        (node, ) = self.nodes
        self.pynode = P2PDataStore()
        self.connection = NodeConn('127.0.0.1', p2p_port(0), node, self.pynode)
        self.pynode.add_connection(self.connection)
        NetworkThread().start()
        self.pynode.wait_for_verack()
        # Get out of IBD
        node.generate(1)

        tip = self.getbestblock(node)

        logging.info("Create some blocks with OP_1 coinbase for spending.")
        blocks = []
        for _ in range(20):
            tip = self.build_block(tip)
            blocks.append(tip)
        self.pynode.send_blocks_and_test(blocks, node, timeout=10)
        self.spendable_outputs = deque(block.vtx[0] for block in blocks)

        logging.info("Mature the blocks.")
        node.generate(100)

        tip = self.getbestblock(node)

        # To make compact and fast-to-verify transactions, we'll use
        # CHECKDATASIG over and over with the same data.
        # (Using the same stuff over and over again means we get to hit the
        # node's signature cache and don't need to make new signatures every
        # time.)
        cds_message = b''
        # r=1 and s=1 ecdsa, the minimum values.
        cds_signature = bytes.fromhex('3006020101020101')
        # Recovered pubkey
        cds_pubkey = bytes.fromhex(
            '03089b476b570d66fad5a20ae6188ebbaf793a4c2a228c65f3d79ee8111d56c932'
        )

        fundings = []

        def make_spend(scriptpubkey, scriptsig):
            # Add a funding tx to fundings, and return a tx spending that using
            # scriptsig.
            logging.debug(
                "Gen tx with locking script {} unlocking script {} .".format(
                    scriptpubkey.hex(), scriptsig.hex()))

            # get funds locked with OP_1
            sourcetx = self.spendable_outputs.popleft()
            # make funding that forwards to scriptpubkey
            fundtx = create_transaction(sourcetx, scriptpubkey)
            fundings.append(fundtx)

            # make the spending
            tx = CTransaction()
            tx.vin.append(CTxIn(COutPoint(fundtx.sha256, 1), scriptsig))
            tx.vout.append(CTxOut(0, CScript([OP_RETURN])))
            pad_tx(tx)
            tx.rehash()
            return tx

        logging.info("Generating txes used in this test")

        # "Good" txns that pass our rule:

        goodtxes = [
            # most dense allowed input -- 2 sigchecks with a 26-byte scriptsig.
            make_spend(
                CScript([
                    cds_message, cds_pubkey, OP_3DUP, OP_CHECKDATASIGVERIFY,
                    OP_CHECKDATASIGVERIFY
                ]), CScript([b'x' * 16, cds_signature])),

            # 4 sigchecks with a 112-byte scriptsig, just at the limit for this
            # sigchecks count.
            make_spend(
                CScript([
                    cds_message, cds_pubkey, OP_3DUP, OP_CHECKDATASIGVERIFY,
                    OP_3DUP, OP_CHECKDATASIGVERIFY, OP_3DUP,
                    OP_CHECKDATASIGVERIFY, OP_CHECKDATASIGVERIFY
                ]), CScript([b'x' * 101, cds_signature])),

            # "nice" transaction - 1 sigcheck with 9-byte scriptsig.
            make_spend(CScript([cds_message, cds_pubkey, OP_CHECKDATASIG]),
                       CScript([cds_signature])),

            # 1 sigcheck with 0-byte scriptsig.
            make_spend(
                CScript(
                    [cds_signature, cds_message, cds_pubkey, OP_CHECKDATASIG]),
                CScript([])),
        ]

        badtxes = [
            # "Bad" txns:
            # 2 sigchecks with a 25-byte scriptsig, just 1 byte too short.
            make_spend(
                CScript([
                    cds_message, cds_pubkey, OP_3DUP, OP_CHECKDATASIGVERIFY,
                    OP_CHECKDATASIGVERIFY
                ]), CScript([b'x' * 15, cds_signature])),

            # 4 sigchecks with a 111-byte scriptsig, just 1 byte too short.
            make_spend(
                CScript([
                    cds_message, cds_pubkey, OP_3DUP, OP_CHECKDATASIGVERIFY,
                    OP_3DUP, OP_CHECKDATASIGVERIFY, OP_3DUP,
                    OP_CHECKDATASIGVERIFY, OP_CHECKDATASIGVERIFY
                ]), CScript([b'x' * 100, cds_signature])),
        ]

        goodtxids = set(t.hash for t in goodtxes)
        badtxids = set(t.hash for t in badtxes)

        logging.info("Funding the txes")
        tip = self.build_block(tip, fundings)
        self.pynode.send_blocks_and_test([tip], node, timeout=10)

        # Activation tests

        logging.info("Approach to just before upgrade activation")
        # Move our clock to the uprade time so we will accept such
        # future-timestamped blocks.
        node.setmocktime(MAY2020_START_TIME + 10)
        # Mine six blocks with timestamp starting at
        # SIGCHECKS_ACTIVATION_TIME-1
        blocks = []
        for i in range(-1, 5):
            tip = self.build_block(tip, nTime=MAY2020_START_TIME + i)
            blocks.append(tip)
        self.pynode.send_blocks_and_test(blocks, node, timeout=10)
        assert_equal(node.getblockchaininfo()['mediantime'],
                     MAY2020_START_TIME - 1)

        logging.info(
            "The next block will activate, but the activation block itself must follow old rules"
        )

        logging.info("Send all the transactions just before upgrade")

        self.pynode.send_txs_and_test(goodtxes, node)
        self.pynode.send_txs_and_test(badtxes, node)

        assert_equal(set(node.getrawmempool()), goodtxids | badtxids)

        # ask the node to mine a block, it should include the bad txes.
        [blockhash] = node.generate(1)
        assert_equal(set(node.getblock(blockhash, 1)['tx'][1:]),
                     goodtxids | badtxids)
        assert_equal(node.getrawmempool(), [])

        # discard that block
        node.invalidateblock(blockhash)
        waitFor(30, lambda: set(node.getrawmempool()) == goodtxids | badtxids)

        logging.info("Mine the activation block itself")
        tip = self.build_block(tip)
        self.pynode.send_blocks_and_test([tip], node, timeout=10)

        logging.info("We have activated!")
        assert_equal(node.getblockchaininfo()['mediantime'],
                     MAY2020_START_TIME)

        logging.info(
            "The high-sigchecks transactions got evicted but the good ones are still around"
        )
        waitFor(
            20, lambda: True if set(node.getrawmempool()) == goodtxids else
            logging.info(node.getrawmempool()))

        logging.info(
            "Now the high-sigchecks transactions are rejected from mempool.")
        # try sending some of the bad txes again after the upgrade
        for tx in badtxes:
            self.check_for_no_ban_on_rejected_tx(
                node, tx,
                None)  # No reject reason because we don't log on rejection
            assert_raises_rpc_error(-26, TX_INPUT_SIGCHECKS_ERROR,
                                    node.sendrawtransaction, ToHex(tx))

        logging.info("But they can still be mined!")

        # Now make a block with all the txes, they still are accepted in blocks!
        tip = self.build_block(tip, goodtxes + badtxes)
        self.pynode.send_blocks_and_test([tip], node, timeout=10)

        assert_equal(node.getbestblockhash(), tip.hash)
Exemplo n.º 8
0
    def run_test(self):
        logging.info("Initializing test directory " + self.options.tmpdir)
        node = self.nodes[0]

        self.bootstrap_p2p()

        tip = self.get_best_block(node)

        logging.info("Create some blocks with OP_1 coinbase for spending.")
        blocks = []
        for _ in range(10):
            tip = self.build_block(tip)
            blocks.append(tip)
        self.p2p.send_blocks_and_test(blocks, node, success=True)
        spendable_outputs = [block.vtx[0] for block in blocks]

        logging.info("Mature the blocks and get out of IBD.")
        node.generate(100)

        tip = self.get_best_block(node)

        logging.info(
            "Set up spending transactions to test and mine the funding transactions."
        )

        # Generate a key pair
        privkeybytes = b"xyzxyzhh" * 4
        private_key = CECKey()
        private_key.set_secretbytes(privkeybytes)
        # get uncompressed public key serialization
        public_key = private_key.get_pubkey()

        def create_fund_and_spend_tx():
            spend_from = spendable_outputs.pop()
            value = spend_from.vout[0].nValue

            # Reversed data
            data = bytes.fromhex('0123456789abcdef')
            rev_data = bytes(reversed(data))

            # Lockscript: provide a bytestring that reverses to X
            script = CScript([OP_REVERSEBYTES, rev_data, OP_EQUAL])

            # Fund transaction: REVERSEBYTES <reversed(x)> EQUAL
            tx_fund = create_tx_with_script(spend_from, 0, b'', value, script)
            tx_fund.rehash()

            # Spend transaction: <x>
            tx_spend = CTransaction()
            tx_spend.vout.append(
                CTxOut(value - 1000, CScript([b'x' * 100, OP_RETURN])))
            tx_spend.vin.append(CTxIn(COutPoint(tx_fund.sha256, 0), b''))
            tx_spend.vin[0].scriptSig = CScript([data])
            tx_spend.rehash()

            return tx_spend, tx_fund

        # Create funding/spending transaction pair
        tx_reversebytes_spend, tx_reversebytes_fund = create_fund_and_spend_tx(
        )

        # Mine funding transaction into block. Pre-upgrade output scripts can have
        # OP_REVERSEBYTES and still be fully valid, but they cannot spend it.
        tip = self.build_block(tip, [tx_reversebytes_fund])
        self.p2p.send_blocks_and_test([tip], node)

        logging.info("Start pre-upgrade tests")
        assert node.getblockheader(
            node.getbestblockhash())['mediantime'] < MAY2020_START_TIME

        logging.info(
            "Sending rejected transaction (bad opcode) via RPC (doesn't ban)")
        assert_raises_rpc_error(-26, PRE_UPGRADE_BAD_OPCODE_ERROR,
                                node.sendrawtransaction,
                                ToHex(tx_reversebytes_spend))

        logging.info(
            "Sending rejected transaction (bad opcode) via net (no banning)")
        self.check_for_no_ban_on_rejected_tx(tx_reversebytes_spend,
                                             PRE_UPGRADE_BAD_OPCODE_ERROR)

        logging.info(
            "Sending invalid transactions in blocks (bad inputs, and get banned)"
        )
        self.check_for_ban_on_rejected_block(
            self.build_block(tip, [tx_reversebytes_spend]), BAD_INPUTS_ERROR)

        logging.info("Start activation tests")

        logging.info("Approach to just before upgrade activation")
        # Move our clock to the upgrade time so we will accept such
        # future-timestamped blocks.
        node.setmocktime(MAY2020_START_TIME)

        # Mine six blocks with timestamp starting at MAY2020_START_TIME-1
        blocks = []
        for i in range(-1, 5):
            tip = self.build_block(tip, n_time=MAY2020_START_TIME + i)
            blocks.append(tip)
        self.p2p.send_blocks_and_test(blocks, node)

        # Ensure our MTP is MAY2020_START_TIME-1, just before activation
        waitFor(10, lambda: node.getblockchaininfo()['mediantime'],
                MAY2020_START_TIME - 1)

        logging.info(
            "The next block will activate, but the activation block itself must follow old rules"
        )
        self.check_for_ban_on_rejected_block(
            self.build_block(tip, [tx_reversebytes_spend]), BAD_INPUTS_ERROR)

        # Save pre-upgrade block, we will reorg based on this block later
        pre_upgrade_block = tip

        logging.info("Mine the activation block itself")
        tip = self.build_block(tip, [])
        self.p2p.send_blocks_and_test([tip], node)

        logging.info("We have activated!")
        # Ensure our MTP is MAY2020_START_TIME, exactly at activation
        waitFor(
            10, lambda: node.getblockchaininfo()['mediantime'] ==
            MAY2020_START_TIME)
        # Ensure empty mempool
        waitFor(10, lambda: node.getrawmempool() == [])

        # Save upgrade block, will invalidate and reconsider this later
        upgrade_block = tip

        logging.info(
            "Submitting a new OP_REVERSEBYTES tx via net, and mining it in a block"
        )
        # Send OP_REVERSEBYTES tx
        self.p2p.send_txs_and_test([tx_reversebytes_spend], node)

        # Verify OP_REVERSEBYTES tx is in mempool
        waitFor(
            10,
            lambda: set(node.getrawmempool()) == {tx_reversebytes_spend.hash})

        # Mine OP_REVERSEBYTES tx into block
        tip = self.build_block(tip, [tx_reversebytes_spend])
        self.p2p.send_blocks_and_test([tip], node)

        # Save post-upgrade block, will invalidate and reconsider this later
        post_upgrade_block = tip

        logging.info("Start deactivation tests")

        logging.info(
            "Invalidating the post-upgrade blocks returns OP_REVERSEBYTES transaction to mempool"
        )
        node.invalidateblock(post_upgrade_block.hash)
        waitFor(5, lambda: node.getbestblockhash() == upgrade_block.hash)
        waitFor(5, lambda: len(node.getrawmempool()) > 0)
        assert_equal(set(node.getrawmempool()), {tx_reversebytes_spend.hash})

        logging.info(
            "Invalidating the upgrade block evicts the OP_REVERSEBYTES transaction"
        )
        node.invalidateblock(upgrade_block.hash)
        assert_equal(set(node.getrawmempool()), set())

        logging.info("Return to our tip")
        try:
            node.reconsiderblock(upgrade_block.hash)
            node.reconsiderblock(post_upgrade_block.hash)
        except Exception as e:
            # Workaround for reconsiderblock bug;
            # Even though the block reconsidered was valid, if another block
            # is also reconsidered and fails, the call will return failure.
            pass

        waitFor(10, lambda: node.getbestblockhash() == tip.hash)
        waitFor(10, lambda: node.getrawmempool() == [])

        logging.info("Create an empty-block reorg that forks from pre-upgrade")
        tip = pre_upgrade_block
        blocks = []
        for _ in range(10):
            tip = self.build_block(tip)
            blocks.append(tip)
        self.p2p.send_blocks_and_test(blocks, node)

        logging.info(
            "Transactions from orphaned blocks are sent into mempool ready to be mined again, "
            "including upgrade-dependent ones even though the fork deactivated and reactivated "
            "the upgrade.")
        waitFor(
            10,
            lambda: set(node.getrawmempool()) == {tx_reversebytes_spend.hash})
        node.generate(1)
        tip = self.get_best_block(node)
        assert (set(tx.rehash()
                    for tx in tip.vtx) >= {tx_reversebytes_spend.hash})
Exemplo n.º 9
0
 async def test_non_verbose(self, cli, coinbases, unconfirmed):
     for tx in coinbases + [unconfirmed]:
         assert_equal(ToHex(tx), await cli.call(TX_GET, tx.hash))
Exemplo n.º 10
0
    def run_test(self):
        n = self.nodes[0]
        self.bootstrap_p2p()

        coinbases = self.mine_blocks(n, 104)

        # non-coinbase transactions
        prevtx = coinbases[0]
        nonstandard_tx = create_transaction(prevtx=prevtx,
                                            value=prevtx.vout[0].nValue,
                                            n=0,
                                            sig=CScript([OP_TRUE]),
                                            out=CScript([OP_FALSE, OP_DROP]))

        prevtx = coinbases[1]
        p2sh_tx = create_transaction(prevtx=prevtx,
                                     value=prevtx.vout[0].nValue,
                                     n=0,
                                     sig=CScript([OP_TRUE]),
                                     out=CScript(
                                         [OP_HASH160, DUMMY_HASH, OP_EQUAL]))

        prevtx = coinbases[2]
        p2pkh_tx = create_transaction(prevtx=prevtx,
                                      value=prevtx.vout[0].nValue,
                                      n=0,
                                      sig=CScript([OP_TRUE]),
                                      out=CScript([
                                          OP_DUP, OP_HASH160, DUMMY_HASH,
                                          OP_EQUALVERIFY, OP_CHECKSIG
                                      ]))

        prevtx = coinbases[3]
        unconfirmed_tx = create_transaction(prevtx=prevtx,
                                            value=prevtx.vout[0].nValue,
                                            n=0,
                                            sig=CScript([OP_TRUE]),
                                            out=CScript([
                                                OP_DUP, OP_HASH160, DUMMY_HASH,
                                                OP_EQUALVERIFY, OP_CHECKSIG
                                            ]))

        for tx in [nonstandard_tx, p2sh_tx, p2pkh_tx, unconfirmed_tx]:
            pad_tx(tx)

        coinbases.extend(
            self.mine_blocks(n, 1, [nonstandard_tx, p2sh_tx, p2pkh_tx]))
        self.sync_height()
        n.sendrawtransaction(ToHex(unconfirmed_tx))
        self.wait_for_mempool_count(count=1)

        async def async_tests(loop):
            cli = ElectrumConnection(loop)
            await cli.connect()

            return await asyncio.gather(
                self.test_verbose(n, cli, nonstandard_tx.hash, p2sh_tx.hash,
                                  p2pkh_tx.hash, unconfirmed_tx.hash),
                self.test_non_verbose(cli, coinbases, unconfirmed_tx))

        loop = asyncio.get_event_loop()
        loop.run_until_complete(async_tests(loop))
    def run_test(self):
        node = self.nodes[0]
        node.generate(1)

        self.bootstrap_p2p()

        tip = self.getbestblock(node)

        logging.info("Create some blocks with OP_1 coinbase for spending.")
        blocks = []
        for _ in range(10):
            tip = self.build_block(tip)
            blocks.append(tip)
        self.p2p.send_blocks_and_test(blocks, node, success=True)
        spendable_outputs = [block.vtx[0] for block in blocks]

        logging.info("Mature the blocks and get out of IBD.")
        node.generate(100)

        tip = self.getbestblock(node)

        logging.info("Setting up spends to test and mining the fundings.")
        fundings = []

        def create_fund_and_spend_tx():
            spendfrom = spendable_outputs.pop()

            script = CScript([OP_ADD])

            value = spendfrom.vout[0].nValue

            # Fund transaction
            txfund = create_transaction(spendfrom, 0, b'', value, script)
            pad_tx(txfund)
            txfund.rehash()
            fundings.append(txfund)

            # Spend transaction
            txspend = CTransaction()
            txspend.vout.append(CTxOut(value - 1000, CScript([OP_TRUE])))
            txspend.vin.append(CTxIn(COutPoint(txfund.sha256, 0), b''))

            # Sign the transaction
            txspend.vin[0].scriptSig = CScript(
                b'\x01\x01\x51')  # PUSH1(0x01) OP_1
            pad_tx(txspend)
            txspend.rehash()

            return txspend

        # make a few of these, which are nonstandard before upgrade and invalid after.
        nonminimaltx = create_fund_and_spend_tx()
        nonminimaltx_2 = create_fund_and_spend_tx()
        nonminimaltx_3 = create_fund_and_spend_tx()

        tip = self.build_block(tip, fundings)
        self.p2p.send_blocks_and_test([tip], node)

        logging.info("Start preupgrade tests")

        logging.info("Sending rejected transactions via RPC")
        assert_raises_rpc_error(-26, MINIMALPUSH_ERROR,
                                node.sendrawtransaction, ToHex(nonminimaltx))
        assert_raises_rpc_error(-26, MINIMALPUSH_ERROR,
                                node.sendrawtransaction, ToHex(nonminimaltx_2))
        assert_raises_rpc_error(-26, MINIMALPUSH_ERROR,
                                node.sendrawtransaction, ToHex(nonminimaltx_3))

        logging.info("Sending rejected transactions via net (no banning)")
        self.check_for_no_ban_on_rejected_tx(nonminimaltx, MINIMALPUSH_ERROR)
        self.check_for_no_ban_on_rejected_tx(nonminimaltx_2, MINIMALPUSH_ERROR)
        self.check_for_no_ban_on_rejected_tx(nonminimaltx_3, MINIMALPUSH_ERROR)

        assert_equal(node.getrawmempool(), [])

        logging.info("Successfully mine nonstandard transaction")
        tip = self.build_block(tip, [nonminimaltx])
        self.p2p.send_blocks_and_test([tip], node)

        # Activation tests

        logging.info("Approach to just before upgrade activation")
        # Move our clock to the uprade time so we will accept such future-timestamped blocks.
        node.setmocktime(NOV2019_START_TIME)
        # Mine six blocks with timestamp starting at NOV2019_START_TIME-1
        blocks = []
        for i in range(-1, 5):
            tip = self.build_block(tip, nTime=NOV2019_START_TIME + i)
            blocks.append(tip)
        self.p2p.send_blocks_and_test(blocks, node)
        assert_equal(node.getblockchaininfo()['mediantime'],
                     NOV2019_START_TIME - 1)

        # save this tip for later
        preupgrade_block = tip

        logging.info(
            "Mine the activation block itself, including a minimaldata violation at the last possible moment"
        )
        tip = self.build_block(tip, [nonminimaltx_2])
        self.p2p.send_blocks_and_test([tip], node)

        logging.info("We have activated!")
        assert_equal(node.getblockchaininfo()['mediantime'],
                     NOV2019_START_TIME)

        # save this tip for later
        upgrade_block = tip

        logging.info(
            "Trying to mine a minimaldata violation, but we are just barely too late"
        )
        self.check_for_ban_on_rejected_block(
            self.build_block(tip, [nonminimaltx_3]), BADSIGNATURE_ERROR)
        return
        logging.info(
            "If we try to submit it by mempool or RPC we still aren't banned")
        assert_raises_rpc_error(-26, rpc_error(MINIMALPUSH_ERROR),
                                node.sendrawtransaction, ToHex(nonminimaltx_3))
        self.check_for_no_ban_on_rejected_tx(nonminimaltx_3, MINIMALPUSH_ERROR)

        logging.info("Mine a normal block")
        tip = self.build_block(tip)
        self.p2p.send_blocks_and_test([tip], node)
    def run_test(self):
        node = self.nodes[0]

        self.bootstrap_p2p()

        tip = self.getbestblock(node)

        logging.info("Create some blocks with OP_1 coinbase for spending.")
        blocks = []
        for _ in range(10):
            tip = self.build_block(tip)
            blocks.append(tip)
        self.p2p.send_blocks_and_test(blocks, node, success=True)
        spendable_outputs = [block.vtx[0] for block in blocks]

        logging.info("Mature the blocks and get out of IBD.")
        node.generate(100)

        tip = self.getbestblock(node)

        logging.info("Setting up spends to test and mining the fundings.")
        fundings = []

        # Generate a key pair
        privkeybytes = b"Schnorr!" * 4
        private_key = CECKey()
        private_key.set_secretbytes(privkeybytes)
        # get uncompressed public key serialization
        public_key = private_key.get_pubkey()

        def create_fund_and_spend_tx(dummy=OP_0, sigtype='ecdsa'):
            spendfrom = spendable_outputs.pop()

            script = CScript([OP_1, public_key, OP_1, OP_CHECKMULTISIG])

            value = spendfrom.vout[0].nValue

            # Fund transaction
            txfund = create_transaction(spendfrom, 0, b'', value, script)
            txfund.rehash()
            fundings.append(txfund)

            # Spend transaction
            txspend = CTransaction()
            txspend.vout.append(
                CTxOut(value-1000, CScript([OP_TRUE])))
            txspend.vin.append(
                CTxIn(COutPoint(txfund.sha256, 0), b''))

            # Sign the transaction
            sighashtype = SIGHASH_ALL | SIGHASH_FORKID
            hashbyte = bytes([sighashtype & 0xff])
            sighash = SignatureHashForkId(
                script, txspend, 0, sighashtype, value)
            if sigtype == 'schnorr':
                txsig = schnorr.sign(privkeybytes, sighash) + hashbyte
            elif sigtype == 'ecdsa':
                txsig = private_key.sign(sighash) + hashbyte
            txspend.vin[0].scriptSig = CScript([dummy, txsig])
            txspend.rehash()

            return txspend

        # two of these transactions, which are valid both before and after upgrade.
        ecdsa0tx = create_fund_and_spend_tx(OP_0, 'ecdsa')
        ecdsa0tx_2 = create_fund_and_spend_tx(OP_0, 'ecdsa')

        # two of these, which are nonstandard before upgrade and invalid after.
        ecdsa1tx = create_fund_and_spend_tx(OP_1, 'ecdsa')
        ecdsa1tx_2 = create_fund_and_spend_tx(OP_1, 'ecdsa')

        # this one is always invalid.
        schnorr0tx = create_fund_and_spend_tx(OP_0, 'schnorr')

        # this one is only going to be valid after the upgrade.
        schnorr1tx = create_fund_and_spend_tx(OP_1, 'schnorr')

        tip = self.build_block(tip, fundings)
        self.p2p.send_blocks_and_test([tip], node)

        logging.info("Start preupgrade tests")
        assert node.getblockheader(node.getbestblockhash())['mediantime'] < NOV2019_START_TIME

        logging.info("Sending rejected transactions via RPC")
        assert_raises_rpc_error(-26, PREUPGRADE_ECDSA_NULLDUMMY_ERROR,
                                node.sendrawtransaction, ToHex(ecdsa1tx))
        assert_raises_rpc_error(-26, SCHNORR_LEGACY_MULTISIG_ERROR,
                                node.sendrawtransaction, ToHex(schnorr0tx))
        assert_raises_rpc_error(-26, PREUPGRADE_SCHNORR_MULTISIG_ERROR,
                                node.sendrawtransaction, ToHex(schnorr1tx))

        logging.info(
            "Sending rejected transactions via net (banning depending on situation)")
        self.check_for_no_ban_on_rejected_tx(
            ecdsa1tx, PREUPGRADE_ECDSA_NULLDUMMY_ERROR)
        self.check_for_ban_on_rejected_tx(
            schnorr0tx, SCHNORR_LEGACY_MULTISIG_ERROR)
        self.check_for_no_ban_on_rejected_tx(
            schnorr1tx, PREUPGRADE_SCHNORR_MULTISIG_ERROR)

        logging.info(
            "Sending invalid transactions in blocks (and get banned!)")
        self.check_for_ban_on_rejected_block(
            self.build_block(tip, [schnorr0tx]), BADSIG_ERROR)
        self.check_for_ban_on_rejected_block(
            self.build_block(tip, [schnorr1tx]), BADSIG_ERROR)

        logging.info("Sending valid transaction via net, then mining it")
        self.p2p.send_txs_and_test([ecdsa0tx], node)
        waitFor(10, lambda: node.getrawmempool() == [ecdsa0tx.hash])
        tip = self.build_block(tip, [ecdsa0tx])
        self.p2p.send_blocks_and_test([tip], node)
        waitFor(10, lambda: node.getrawmempool() == [])

        # Activation tests

        logging.info("Approach to just before upgrade activation")
        # Move our clock to the uprade time so we will accept such future-timestamped blocks.
        node.setmocktime(NOV2019_START_TIME)
        # Mine six blocks with timestamp starting at NOV2019_START_TIME-1
        blocks = []
        for i in range(-1, 5):
            tip = self.build_block(tip, nTime=NOV2019_START_TIME + i)
            blocks.append(tip)
        self.p2p.send_blocks_and_test(blocks, node)
        waitFor(10, lambda: node.getblockchaininfo()[
                     'mediantime'] == NOV2019_START_TIME - 1)
        logging.info(
            "The next block will activate, but the activation block itself must follow old rules")
        self.check_for_ban_on_rejected_block(
            self.build_block(tip, [schnorr0tx]), BADSIG_ERROR)

        logging.info(
            "Send a lecacy ECDSA multisig into mempool, we will check after upgrade to make sure it didn't get cleaned out unnecessarily.")
        self.p2p.send_txs_and_test([ecdsa0tx_2], node)
        waitFor(10, lambda: node.getrawmempool() == [ecdsa0tx_2.hash])

        # save this tip for later
        preupgrade_block = tip

        logging.info(
            "Mine the activation block itself, including a legacy nulldummy violation at the last possible moment")
        tip = self.build_block(tip, [ecdsa1tx])
        self.p2p.send_blocks_and_test([tip], node)

        logging.info("We have activated!")
        waitFor(10, lambda: node.getblockchaininfo()[
                     'mediantime'] == NOV2019_START_TIME)
        waitFor(10, lambda: node.getrawmempool() == [ecdsa0tx_2.hash])

        # save this tip for later
        upgrade_block = tip

        logging.info(
            "Trying to mine a legacy nulldummy violation, but we are just barely too late")
        self.check_for_ban_on_rejected_block(
            self.build_block(tip, [ecdsa1tx_2]), BADSIG_ERROR)
        logging.info(
            "If we try to submit it by mempool or RPC, the error code has changed but we still aren't banned")
        assert_raises_rpc_error(-26, POSTUPGRADE_ECDSA_NULLDUMMY_ERROR,
                                node.sendrawtransaction, ToHex(ecdsa1tx_2))
        self.check_for_no_ban_on_rejected_tx(
            ecdsa1tx_2, POSTUPGRADE_ECDSA_NULLDUMMY_ERROR)

        logging.info(
            "Submitting a new Schnorr-multisig via net, and mining it in a block")
        self.p2p.send_txs_and_test([schnorr1tx], node)
        waitFor(10, lambda: set(node.getrawmempool()) == {ecdsa0tx_2.hash, schnorr1tx.hash})
        tip = self.build_block(tip, [schnorr1tx])
        self.p2p.send_blocks_and_test([tip], node)

        # save this tip for later
        postupgrade_block = tip

        logging.info(
            "That legacy ECDSA multisig is still in mempool, let's mine it")
        waitFor(10, lambda: node.getrawmempool() == [ecdsa0tx_2.hash])
        tip = self.build_block(tip, [ecdsa0tx_2])
        self.p2p.send_blocks_and_test([tip], node)
        waitFor(10, lambda: node.getrawmempool() == [])

        logging.info(
            "Trying Schnorr in legacy multisig remains invalid and banworthy as ever")
        self.check_for_ban_on_rejected_tx(
            schnorr0tx, SCHNORR_LEGACY_MULTISIG_ERROR)
        self.check_for_ban_on_rejected_block(
            self.build_block(tip, [schnorr0tx]), BADSIG_ERROR)

        # Deactivation tests

        logging.info(
            "Invalidating the post-upgrade blocks returns the transactions to mempool")
        node.invalidateblock(postupgrade_block.hash)
        waitFor(10, lambda: set(node.getrawmempool()) == {ecdsa0tx_2.hash, schnorr1tx.hash})
        logging.info(
            "Invalidating the upgrade block evicts the transactions valid only after upgrade")
        node.invalidateblock(upgrade_block.hash)
        waitFor(10, lambda: set(node.getrawmempool()) == {ecdsa0tx_2.hash})

        logging.info("Return to our tip")
        try:
            node.reconsiderblock(upgrade_block.hash)
            node.reconsiderblock(postupgrade_block.hash)
        except Exception as e:
            # Workaround for reconsiderblock bug;
            # Even though the block reconsidered was valid, if another block
            # is also reconsidered and fails, the call will return failure.
            pass
        waitFor(10, lambda: node.getbestblockhash() == tip.hash)
        waitFor(10, lambda: node.getrawmempool() == [])

        logging.info(
            "Create an empty-block reorg that forks from pre-upgrade")
        tip = preupgrade_block
        blocks = []
        for _ in range(10):
            tip = self.build_block(tip)
            blocks.append(tip)
        self.p2p.send_blocks_and_test(blocks, node)

        logging.info("Transactions from orphaned blocks are sent into mempool ready to be mined again, including upgrade-dependent ones even though the fork deactivated and reactivated the upgrade.")
        waitFor(10, lambda: set(node.getrawmempool()) == {
                     ecdsa0tx_2.hash, schnorr1tx.hash})
        node.generate(1)
        tip = self.getbestblock(node)
        assert set(tx.rehash() for tx in tip.vtx).issuperset(
            {ecdsa0tx_2.hash, schnorr1tx.hash})
Exemplo n.º 13
0
    def run_test(self):
        node = self.nodes[0]
        node.generate(1)

        self.bootstrap_p2p()

        tip = self.getbestblock(node)

        logging.info("Create some blocks with OP_1 coinbase for spending.")
        blocks = []
        for _ in range(10):
            tip = self.build_block(tip)
            blocks.append(tip)
        self.p2p.send_blocks_and_test(blocks, node, success=True)
        spendable_outputs = [block.vtx[0] for block in blocks]

        logging.info("Mature the blocks and get out of IBD.")
        node.generate(100)

        tip = self.getbestblock(node)

        logging.info("Setting up spends to test and mining the fundings.")
        fundings = []

        def create_fund_and_spend_tx():
            spendfrom = spendable_outputs.pop()

            script = CScript([OP_ADD])

            value = spendfrom.vout[0].nValue

            # Fund transaction
            txfund = create_transaction(spendfrom, 0, b'', value, script)
            pad_tx(txfund)
            txfund.rehash()
            fundings.append(txfund)

            # Spend transaction
            txspend = CTransaction()
            txspend.vout.append(
                CTxOut(value-1000, CScript([OP_TRUE])))
            txspend.vin.append(
                CTxIn(COutPoint(txfund.sha256, 0), b''))

            # Sign the transaction
            txspend.vin[0].scriptSig = CScript(
                b'\x01\x01\x51')  # PUSH1(0x01) OP_1
            pad_tx(txspend)
            txspend.rehash()

            return txspend

        # no minimal tx are invalid
        nonminimaltx = create_fund_and_spend_tx()

        tip = self.build_block(tip, fundings)
        self.p2p.send_blocks_and_test([tip], node)

        logging.info("Trying to mine a minimaldata violation")
        self.check_for_ban_on_rejected_block(
            self.build_block(tip, [nonminimaltx]), BADSIGNATURE_ERROR)
        logging.info("If we try to submit it by mempool or RPC we are banned")
        assert_raises_rpc_error(-26, MINIMALPUSH_ERROR, node.sendrawtransaction, ToHex(nonminimaltx))
        self.check_for_ban_on_rejected_tx(nonminimaltx, MINIMALPUSH_ERROR)

        logging.info("Mine a normal block")
        tip = self.build_block(tip)
        self.p2p.send_blocks_and_test([tip], node)