def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])):
    """Create a txout with a given amount and scriptPubKey

    Mines coins as needed.

    confirmed - txouts created will be confirmed in the blockchain;
                unconfirmed otherwise.
    """
    fee = 1*COIN
    while node.getbalance()['bitcoin'] < satoshi_round((amount + fee)/COIN):
        node.generate(100)

    new_addr = node.getnewaddress()
    unblinded_addr = node.validateaddress(new_addr)["unconfidential"]
    txidstr = node.sendtoaddress(new_addr, satoshi_round((amount+fee)/COIN))
    tx1 = node.getrawtransaction(txidstr, 1)
    txid = int(txidstr, 16)
    i = None

    for i, txout in enumerate(tx1['vout']):
        if txout['scriptPubKey']['type'] == "fee":
            continue # skip fee outputs
        if txout['scriptPubKey']['addresses'] == [unblinded_addr]:
            break
    assert i is not None

    tx2 = CTransaction()
    tx2.vin = [CTxIn(COutPoint(txid, i))]
    tx1raw = CTransaction()
    tx1raw.deserialize(BytesIO(hex_str_to_bytes(node.getrawtransaction(txidstr))))
    feeout = CTxOut(CTxOutValue(tx1raw.vout[i].nValue.getAmount() - amount))
    tx2.vout = [CTxOut(amount, scriptPubKey), feeout]
    tx2.rehash()

    signed_tx = node.signrawtransactionwithwallet(txToHex(tx2))

    txid = node.sendrawtransaction(signed_tx['hex'], True)

    # If requested, ensure txouts are confirmed.
    if confirmed:
        mempool_size = len(node.getrawmempool())
        while mempool_size > 0:
            node.generate(1)
            new_size = len(node.getrawmempool())
            # Error out if we have something stuck in the mempool, as this
            # would likely be a bug.
            assert(new_size < mempool_size)
            mempool_size = new_size

    return COutPoint(int(txid, 16), 0)
def split_inputs(from_node, txins, txouts, initial_split=False):
    """Generate a lot of inputs so we can generate a ton of transactions.

    This function takes an input from txins, and creates and sends a transaction
    which splits the value into 2 outputs which are appended to txouts.
    Previously this was designed to be small inputs so they wouldn't have
    a high coin age when the notion of priority still existed."""

    prevtxout = txins.pop()
    tx = CTransaction()
    tx.vin.append(CTxIn(COutPoint(int(prevtxout["txid"], 16), prevtxout["vout"]), b""))

    half_change = satoshi_round(prevtxout["amount"] / 2)
    rem_change = prevtxout["amount"] - half_change - Decimal("0.00001000")
    tx.vout.append(CTxOut(int(half_change * COIN), P2SH_1))
    tx.vout.append(CTxOut(int(rem_change * COIN), P2SH_2))

    # If this is the initial split we actually need to sign the transaction
    # Otherwise we just need to insert the proper ScriptSig
    if (initial_split):
        completetx = from_node.signrawtransactionwithwallet(ToHex(tx))["hex"]
    else:
        tx.vin[0].scriptSig = SCRIPT_SIG[prevtxout["vout"]]
        completetx = ToHex(tx)
    txid = from_node.sendrawtransaction(completetx, True)
    txouts.append({"txid": txid, "vout": 0, "amount": half_change})
    txouts.append({"txid": txid, "vout": 1, "amount": rem_change})
Beispiel #3
0
def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])):
    """Create a txout with a given amount and scriptPubKey

    Mines coins as needed.

    confirmed - txouts created will be confirmed in the blockchain;
                unconfirmed otherwise.
    """
    fee = 1*COIN
    while node.getbalance() < satoshi_round((amount + fee)/COIN):
        node.generate(100)

    new_addr = node.getnewaddress()
    txid = node.sendtoaddress(new_addr, satoshi_round((amount+fee)/COIN))
    tx1 = node.getrawtransaction(txid, 1)
    txid = int(txid, 16)
    i = None

    for i, txout in enumerate(tx1['vout']):
        if txout['scriptPubKey']['addresses'] == [new_addr]:
            break
    assert i is not None

    tx2 = CTransaction()
    tx2.vin = [CTxIn(COutPoint(txid, i))]
    tx2.vout = [CTxOut(amount, scriptPubKey)]
    tx2.rehash()

    signed_tx = node.signrawtransactionwithwallet(txToHex(tx2))

    txid = node.sendrawtransaction(signed_tx['hex'], True)

    # If requested, ensure txouts are confirmed.
    if confirmed:
        mempool_size = len(node.getrawmempool())
        while mempool_size > 0:
            node.generate(1)
            new_size = len(node.getrawmempool())
            # Error out if we have something stuck in the mempool, as this
            # would likely be a bug.
            assert(new_size < mempool_size)
            mempool_size = new_size

    return COutPoint(int(txid, 16), 0)
Beispiel #4
0
 def create_null_data_tx(self, data_size):
     node = self.nodes[0]
     utxos = node.listunspent()
     assert(len(utxos) > 0)
     utxo = utxos[0]
     tx = CTransaction()
     value = int(satoshi_round(utxo["amount"] - self.relayfee) * COIN)
     tx.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]))]
     script = CScript([OP_RETURN, b'x' * data_size])
     tx.vout = [CTxOut(value, script)]
     tx_signed = node.signrawtransaction(ToHex(tx))["hex"]
     return tx_signed
 def chain_transaction(self, node, parent_txid, vout, value, fee, num_outputs):
     send_value = satoshi_round((value - fee)/num_outputs)
     inputs = [ {'txid' : parent_txid, 'vout' : vout} ]
     outputs = {}
     for i in range(num_outputs):
         outputs[node.getnewaddress()] = send_value
     rawtx = node.createrawtransaction(inputs, outputs)
     signedtx = node.signrawtransactionwithwallet(rawtx)
     txid = node.sendrawtransaction(signedtx['hex'])
     fulltx = node.getrawtransaction(txid, 1)
     assert(len(fulltx['vout']) == num_outputs) # make sure we didn't generate a change output
     return (txid, send_value)
 def create_and_tx(self, count):
     node = self.nodes[0]
     utxos = node.listunspent()
     assert(len(utxos) > 0)
     utxo = utxos[0]
     tx = CTransaction()
     value = int(satoshi_round(
         utxo["amount"] - self.relayfee) * COIN) // count
     tx.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]))]
     tx.vout = []
     for _ in range(count):
         tx.vout.append(CTxOut(value, CScript([OP_1, OP_1, OP_AND])))
     tx_signed = node.signrawtransaction(ToHex(tx))["hex"]
     return tx_signed
def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment):
    """Create and send a transaction with a random fee.

    The transaction pays to a trivial P2SH script, and assumes that its inputs
    are of the same form.
    The function takes a list of confirmed outputs and unconfirmed outputs
    and attempts to use the confirmed list first for its inputs.
    It adds the newly created outputs to the unconfirmed list.
    Returns (raw transaction, fee)."""

    # It's best to exponentially distribute our random fees
    # because the buckets are exponentially spaced.
    # Exponentially distributed from 1-128 * fee_increment
    rand_fee = float(fee_increment) * (1.1892 ** random.randint(0, 28))
    # Total fee ranges from min_fee to min_fee + 127*fee_increment
    fee = min_fee - fee_increment + satoshi_round(rand_fee)
    tx = CTransaction()
    total_in = Decimal("0.00000000")
    while total_in <= (amount + fee) and len(conflist) > 0:
        t = conflist.pop(0)
        total_in += t["amount"]
        tx.vin.append(CTxIn(COutPoint(int(t["txid"], 16), t["vout"]), b""))
    if total_in <= amount + fee:
        while total_in <= (amount + fee) and len(unconflist) > 0:
            t = unconflist.pop(0)
            total_in += t["amount"]
            tx.vin.append(CTxIn(COutPoint(int(t["txid"], 16), t["vout"]), b""))
        if total_in <= amount + fee:
            raise RuntimeError("Insufficient funds: need %d, have %d" % (amount + fee, total_in))
    tx.vout.append(CTxOut(int((total_in - amount - fee) * COIN), P2SH_1))
    tx.vout.append(CTxOut(int(amount * COIN), P2SH_2))
    tx.vout.append(CTxOut(int(fee*COIN))) # fee
    # These transactions don't need to be signed, but we still have to insert
    # the ScriptSig that will satisfy the ScriptPubKey.
    for inp in tx.vin:
        inp.scriptSig = SCRIPT_SIG[inp.prevout.n]
    txid = from_node.sendrawtransaction(ToHex(tx), True)
    unconflist.append({"txid": txid, "vout": 0, "amount": total_in - amount - fee})
    unconflist.append({"txid": txid, "vout": 1, "amount": amount})

    return (ToHex(tx), fee)
    def test_disable_flag(self):
        # Create some unconfirmed inputs
        new_addr = self.nodes[0].getnewaddress()
        self.nodes[0].sendtoaddress(new_addr, 2) # send 2 BTC

        utxos = self.nodes[0].listunspent(0, 0)
        assert len(utxos) > 0

        utxo = utxos[0]

        tx1 = CTransaction()
        value = int(satoshi_round(utxo["amount"] - self.relayfee)*COIN)

        # Check that the disable flag disables relative locktime.
        # If sequence locks were used, this would require 1 block for the
        # input to mature.
        sequence_value = SEQUENCE_LOCKTIME_DISABLE_FLAG | 1
        tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)]
        tx1.vout = [CTxOut(value, CScript([b'a']))]

        tx1_signed = self.nodes[0].signrawtransactionwithwallet(ToHex(tx1))["hex"]
        tx1_id = self.nodes[0].sendrawtransaction(tx1_signed)
        tx1_id = int(tx1_id, 16)

        # This transaction will enable sequence-locks, so this transaction should
        # fail
        tx2 = CTransaction()
        tx2.nVersion = 2
        sequence_value = sequence_value & 0x7fffffff
        tx2.vin = [CTxIn(COutPoint(tx1_id, 0), nSequence=sequence_value)]
        tx2.vout = [CTxOut(int(value - self.relayfee * COIN), CScript([b'a' * 35]))]
        tx2.rehash()

        assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2))

        # Setting the version back down to 1 should disable the sequence lock,
        # so this should be accepted.
        tx2.nVersion = 1

        self.nodes[0].sendrawtransaction(ToHex(tx2))
Beispiel #9
0
    def test_compactblock_construction(self, node, test_node, version,
                                       use_witness_address):
        # Generate a bunch of transactions.
        node.generate(101)
        num_transactions = 25
        address = node.getnewaddress()
        if use_witness_address:
            # Want at least one segwit spend, so move all funds to
            # a witness address.
            address = node.getnewaddress(address_type='bech32')
            value_to_send = node.getbalance()
            node.sendtoaddress(address,
                               satoshi_round(value_to_send - Decimal(0.1)))
            node.generate(1)

        segwit_tx_generated = False
        for i in range(num_transactions):
            txid = node.sendtoaddress(address, 0.1)
            hex_tx = node.gettransaction(txid)["hex"]
            tx = FromHex(CTransaction(), hex_tx)
            if not tx.wit.is_null():
                segwit_tx_generated = True

        if use_witness_address:
            assert segwit_tx_generated  # check that our test is not broken

        # Wait until we've seen the block announcement for the resulting tip
        tip = int(node.getbestblockhash(), 16)
        test_node.wait_for_block_announcement(tip)

        # Make sure we will receive a fast-announce compact block
        self.request_cb_announcements(test_node, node, version)

        # Now mine a block, and look at the resulting compact block.
        test_node.clear_block_announcement()
        block_hash = int(node.generate(1)[0], 16)

        # Store the raw block in our internal format.
        block = FromHex(CBlock(), node.getblock("%064x" % block_hash, False))
        for tx in block.vtx:
            tx.calc_sha256()
        block.rehash()

        # Wait until the block was announced (via compact blocks)
        wait_until(test_node.received_block_announcement,
                   timeout=30,
                   lock=mininode_lock)

        # Now fetch and check the compact block
        header_and_shortids = None
        with mininode_lock:
            assert ("cmpctblock" in test_node.last_message)
            # Convert the on-the-wire representation to absolute indexes
            header_and_shortids = HeaderAndShortIDs(
                test_node.last_message["cmpctblock"].header_and_shortids)
        self.check_compactblock_construction_from_block(
            version, header_and_shortids, block_hash, block)

        # Now fetch the compact block using a normal non-announce getdata
        with mininode_lock:
            test_node.clear_block_announcement()
            inv = CInv(4, block_hash)  # 4 == "CompactBlock"
            test_node.send_message(msg_getdata([inv]))

        wait_until(test_node.received_block_announcement,
                   timeout=30,
                   lock=mininode_lock)

        # Now fetch and check the compact block
        header_and_shortids = None
        with mininode_lock:
            assert ("cmpctblock" in test_node.last_message)
            # Convert the on-the-wire representation to absolute indexes
            header_and_shortids = HeaderAndShortIDs(
                test_node.last_message["cmpctblock"].header_and_shortids)
        self.check_compactblock_construction_from_block(
            version, header_and_shortids, block_hash, block)
Beispiel #10
0
    def run_test(self):
        # Mine some blocks and have them mature.
        peer_inv_store = self.nodes[0].add_p2p_connection(
            P2PTxInvStore())  # keep track of invs
        self.nodes[0].generate(COINBASE_MATURITY + 1)
        utxo = self.nodes[0].listunspent(10)
        txid = utxo[0]['txid']
        vout = utxo[0]['vout']
        value = utxo[0]['amount']

        fee = Decimal("0.0001")
        # MAX_ANCESTORS transactions off a confirmed tx should be fine
        chain = []
        witness_chain = []
        for _ in range(MAX_ANCESTORS):
            (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [0],
                                                   value, fee, 1)
            value = sent_value
            chain.append(txid)
            # We need the wtxids to check P2P announcements
            fulltx = self.nodes[0].getrawtransaction(txid)
            witnesstx = self.nodes[0].decoderawtransaction(fulltx, True)
            witness_chain.append(witnesstx['hash'])

        # Wait until mempool transactions have passed initial broadcast (sent inv and received getdata)
        # Otherwise, getrawmempool may be inconsistent with getmempoolentry if unbroadcast changes in between
        peer_inv_store.wait_for_broadcast(witness_chain)

        # Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor
        # count and fees should look correct
        mempool = self.nodes[0].getrawmempool(True)
        assert_equal(len(mempool), MAX_ANCESTORS)
        descendant_count = 1
        descendant_fees = 0
        descendant_vsize = 0

        ancestor_vsize = sum([mempool[tx]['vsize'] for tx in mempool])
        ancestor_count = MAX_ANCESTORS
        ancestor_fees = sum([mempool[tx]['fee'] for tx in mempool])

        descendants = []
        ancestors = list(chain)
        for x in reversed(chain):
            # Check that getmempoolentry is consistent with getrawmempool
            entry = self.nodes[0].getmempoolentry(x)
            assert_equal(entry, mempool[x])

            # Check that the descendant calculations are correct
            assert_equal(mempool[x]['descendantcount'], descendant_count)
            descendant_fees += mempool[x]['fee']
            assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee'])
            assert_equal(mempool[x]['fees']['base'], mempool[x]['fee'])
            assert_equal(mempool[x]['fees']['modified'],
                         mempool[x]['modifiedfee'])
            assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN)
            assert_equal(mempool[x]['fees']['descendant'], descendant_fees)
            descendant_vsize += mempool[x]['vsize']
            assert_equal(mempool[x]['descendantsize'], descendant_vsize)
            descendant_count += 1

            # Check that ancestor calculations are correct
            assert_equal(mempool[x]['ancestorcount'], ancestor_count)
            assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN)
            assert_equal(mempool[x]['ancestorsize'], ancestor_vsize)
            ancestor_vsize -= mempool[x]['vsize']
            ancestor_fees -= mempool[x]['fee']
            ancestor_count -= 1

            # Check that parent/child list is correct
            assert_equal(mempool[x]['spentby'], descendants[-1:])
            assert_equal(mempool[x]['depends'], ancestors[-2:-1])

            # Check that getmempooldescendants is correct
            assert_equal(sorted(descendants),
                         sorted(self.nodes[0].getmempooldescendants(x)))

            # Check getmempooldescendants verbose output is correct
            for descendant, dinfo in self.nodes[0].getmempooldescendants(
                    x, True).items():
                assert_equal(dinfo['depends'],
                             [chain[chain.index(descendant) - 1]])
                if dinfo['descendantcount'] > 1:
                    assert_equal(dinfo['spentby'],
                                 [chain[chain.index(descendant) + 1]])
                else:
                    assert_equal(dinfo['spentby'], [])
            descendants.append(x)

            # Check that getmempoolancestors is correct
            ancestors.remove(x)
            assert_equal(sorted(ancestors),
                         sorted(self.nodes[0].getmempoolancestors(x)))

            # Check that getmempoolancestors verbose output is correct
            for ancestor, ainfo in self.nodes[0].getmempoolancestors(
                    x, True).items():
                assert_equal(ainfo['spentby'],
                             [chain[chain.index(ancestor) + 1]])
                if ainfo['ancestorcount'] > 1:
                    assert_equal(ainfo['depends'],
                                 [chain[chain.index(ancestor) - 1]])
                else:
                    assert_equal(ainfo['depends'], [])

        # Check that getmempoolancestors/getmempooldescendants correctly handle verbose=true
        v_ancestors = self.nodes[0].getmempoolancestors(chain[-1], True)
        assert_equal(len(v_ancestors), len(chain) - 1)
        for x in v_ancestors.keys():
            assert_equal(mempool[x], v_ancestors[x])
        assert chain[-1] not in v_ancestors.keys()

        v_descendants = self.nodes[0].getmempooldescendants(chain[0], True)
        assert_equal(len(v_descendants), len(chain) - 1)
        for x in v_descendants.keys():
            assert_equal(mempool[x], v_descendants[x])
        assert chain[0] not in v_descendants.keys()

        # Check that ancestor modified fees includes fee deltas from
        # prioritisetransaction
        self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=1000)
        mempool = self.nodes[0].getrawmempool(True)
        ancestor_fees = 0
        for x in chain:
            ancestor_fees += mempool[x]['fee']
            assert_equal(mempool[x]['fees']['ancestor'],
                         ancestor_fees + Decimal('0.00001'))
            assert_equal(mempool[x]['ancestorfees'],
                         ancestor_fees * COIN + 1000)

        # Undo the prioritisetransaction for later tests
        self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=-1000)

        # Check that descendant modified fees includes fee deltas from
        # prioritisetransaction
        self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=1000)
        mempool = self.nodes[0].getrawmempool(True)

        descendant_fees = 0
        for x in reversed(chain):
            descendant_fees += mempool[x]['fee']
            assert_equal(mempool[x]['fees']['descendant'],
                         descendant_fees + Decimal('0.00001'))
            assert_equal(mempool[x]['descendantfees'],
                         descendant_fees * COIN + 1000)

        # Adding one more transaction on to the chain should fail.
        assert_raises_rpc_error(-26, "too-long-mempool-chain",
                                chain_transaction, self.nodes[0], [txid],
                                [vout], value, fee, 1)

        # Check that prioritising a tx before it's added to the mempool works
        # First clear the mempool by mining a block.
        self.nodes[0].generate(1)
        self.sync_blocks()
        assert_equal(len(self.nodes[0].getrawmempool()), 0)
        # Prioritise a transaction that has been mined, then add it back to the
        # mempool by using invalidateblock.
        self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=2000)
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        # Keep node1's tip synced with node0
        self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())

        # Now check that the transaction is in the mempool, with the right modified fee
        mempool = self.nodes[0].getrawmempool(True)

        descendant_fees = 0
        for x in reversed(chain):
            descendant_fees += mempool[x]['fee']
            if (x == chain[-1]):
                assert_equal(mempool[x]['modifiedfee'],
                             mempool[x]['fee'] + satoshi_round(0.00002))
                assert_equal(mempool[x]['fees']['modified'],
                             mempool[x]['fee'] + satoshi_round(0.00002))
            assert_equal(mempool[x]['descendantfees'],
                         descendant_fees * COIN + 2000)
            assert_equal(mempool[x]['fees']['descendant'],
                         descendant_fees + satoshi_round(0.00002))

        # Check that node1's mempool is as expected (-> custom ancestor limit)
        mempool0 = self.nodes[0].getrawmempool(False)
        mempool1 = self.nodes[1].getrawmempool(False)
        assert_equal(len(mempool1), MAX_ANCESTORS_CUSTOM)
        assert set(mempool1).issubset(set(mempool0))
        for tx in chain[:MAX_ANCESTORS_CUSTOM]:
            assert tx in mempool1
        # TODO: more detailed check of node1's mempool (fees etc.)
        # check transaction unbroadcast info (should be false if in both mempools)
        mempool = self.nodes[0].getrawmempool(True)
        for tx in mempool:
            assert_equal(mempool[tx]['unbroadcast'], False)

        # TODO: test ancestor size limits

        # Now test descendant chain limits
        txid = utxo[1]['txid']
        value = utxo[1]['amount']
        vout = utxo[1]['vout']

        transaction_package = []
        tx_children = []
        # First create one parent tx with 10 children
        (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [vout],
                                               value, fee, 10)
        parent_transaction = txid
        for i in range(10):
            transaction_package.append({
                'txid': txid,
                'vout': i,
                'amount': sent_value
            })

        # Sign and send up to MAX_DESCENDANT transactions chained off the parent tx
        chain = [
        ]  # save sent txs for the purpose of checking node1's mempool later (see below)
        for _ in range(MAX_DESCENDANTS - 1):
            utxo = transaction_package.pop(0)
            (txid, sent_value) = chain_transaction(self.nodes[0],
                                                   [utxo['txid']],
                                                   [utxo['vout']],
                                                   utxo['amount'], fee, 10)
            chain.append(txid)
            if utxo['txid'] is parent_transaction:
                tx_children.append(txid)
            for j in range(10):
                transaction_package.append({
                    'txid': txid,
                    'vout': j,
                    'amount': sent_value
                })

        mempool = self.nodes[0].getrawmempool(True)
        assert_equal(mempool[parent_transaction]['descendantcount'],
                     MAX_DESCENDANTS)
        assert_equal(sorted(mempool[parent_transaction]['spentby']),
                     sorted(tx_children))

        for child in tx_children:
            assert_equal(mempool[child]['depends'], [parent_transaction])

        # Sending one more chained transaction will fail
        utxo = transaction_package.pop(0)
        assert_raises_rpc_error(-26, "too-long-mempool-chain",
                                chain_transaction, self.nodes[0],
                                [utxo['txid']], [utxo['vout']], utxo['amount'],
                                fee, 10)

        # Check that node1's mempool is as expected, containing:
        # - txs from previous ancestor test (-> custom ancestor limit)
        # - parent tx for descendant test
        # - txs chained off parent tx (-> custom descendant limit)
        self.wait_until(lambda: len(self.nodes[1].getrawmempool(False)) ==
                        MAX_ANCESTORS_CUSTOM + 1 + MAX_DESCENDANTS_CUSTOM,
                        timeout=10)
        mempool0 = self.nodes[0].getrawmempool(False)
        mempool1 = self.nodes[1].getrawmempool(False)
        assert set(mempool1).issubset(set(mempool0))
        assert parent_transaction in mempool1
        for tx in chain[:MAX_DESCENDANTS_CUSTOM]:
            assert tx in mempool1
        for tx in chain[MAX_DESCENDANTS_CUSTOM:]:
            assert tx not in mempool1
        # TODO: more detailed check of node1's mempool (fees etc.)

        # TODO: test descendant size limits

        # Test reorg handling
        # First, the basics:
        self.nodes[0].generate(1)
        self.sync_blocks()
        self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash())
        self.nodes[1].reconsiderblock(self.nodes[0].getbestblockhash())

        # Now test the case where node1 has a transaction T in its mempool that
        # depends on transactions A and B which are in a mined block, and the
        # block containing A and B is disconnected, AND B is not accepted back
        # into node1's mempool because its ancestor count is too high.

        # Create 8 transactions, like so:
        # Tx0 -> Tx1 (vout0)
        #   \--> Tx2 (vout1) -> Tx3 -> Tx4 -> Tx5 -> Tx6 -> Tx7
        #
        # Mine them in the next block, then generate a new tx8 that spends
        # Tx1 and Tx7, and add to node1's mempool, then disconnect the
        # last block.

        # Create tx0 with 2 outputs
        utxo = self.nodes[0].listunspent()
        txid = utxo[0]['txid']
        value = utxo[0]['amount']
        vout = utxo[0]['vout']

        send_value = satoshi_round((value - fee) / 2)
        inputs = [{'txid': txid, 'vout': vout}]
        outputs = {}
        for _ in range(2):
            outputs[self.nodes[0].getnewaddress()] = send_value
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
        txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
        tx0_id = txid
        value = send_value

        # Create tx1
        tx1_id, _ = chain_transaction(self.nodes[0], [tx0_id], [0], value, fee,
                                      1)

        # Create tx2-7
        vout = 1
        txid = tx0_id
        for _ in range(6):
            (txid, sent_value) = chain_transaction(self.nodes[0], [txid],
                                                   [vout], value, fee, 1)
            vout = 0
            value = sent_value

        # Mine these in a block
        self.nodes[0].generate(1)
        self.sync_all()

        # Now generate tx8, with a big fee
        inputs = [{'txid': tx1_id, 'vout': 0}, {'txid': txid, 'vout': 0}]
        outputs = {self.nodes[0].getnewaddress(): send_value + value - 4 * fee}
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
        txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
        self.sync_mempools()

        # Now try to disconnect the tip on each node...
        self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        self.sync_blocks()
    def run_test(self):
        nodes = self.nodes

        ro = nodes[0].extkeyimportmaster(
            'abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'
        )
        assert (ro['account_id'] == 'aaaZf2qnNr5T7PWRmqgmusuu5ACnBcX2ev')
        assert (nodes[0].getwalletinfo()['total_balance'] == 100000)

        addrs = []
        pubkeys = []

        ro = nodes[0].getnewaddress()
        addrs.append(ro)
        pubkeys.append(nodes[0].getaddressinfo(ro)['pubkey'])

        ro = nodes[0].getnewaddress()
        addrs.append(ro)
        pubkeys.append(nodes[0].getaddressinfo(ro)['pubkey'])

        nodes[1].extkeyimportmaster(
            'drip fog service village program equip minute dentist series hawk crop sphere olympic lazy garbage segment fox library good alley steak jazz force inmate'
        )
        nodes[2].extkeyimportmaster(nodes[2].mnemonic('new', '',
                                                      'french')['mnemonic'])

        ro = nodes[1].getnewaddress()
        addrs.append(ro)
        pubkeys.append(nodes[1].getaddressinfo(ro)['pubkey'])

        ro = nodes[2].getnewaddress()
        addrs.append(ro)
        pubkeys.append(nodes[2].getaddressinfo(ro)['pubkey'])

        v = [addrs[0], addrs[1], pubkeys[2]]
        msAddr = nodes[0].addmultisigaddress(2, v)['address']

        ro = nodes[0].getaddressinfo(msAddr)
        assert (ro['isscript'] == True)
        scriptPubKey = ro['scriptPubKey']
        redeemScript = ro['hex']

        mstxid = nodes[0].sendtoaddress(msAddr, 10)
        hexfund = nodes[0].gettransaction(mstxid)['hex']
        ro = nodes[0].decoderawtransaction(hexfund)

        fundscriptpubkey = ''
        fundoutid = -1
        for vout in ro['vout']:
            if not isclose(vout['value'], 10.0):
                continue

            fundoutid = vout['n']
            fundscriptpubkey = vout['scriptPubKey']['hex']
        assert (fundoutid >= 0), "fund output not found"

        addrTo = nodes[2].getnewaddress()

        inputs = [{
            "txid": mstxid,
            "vout": fundoutid,
            "scriptPubKey": fundscriptpubkey,
            "redeemScript": redeemScript,
            "amount": 10.0,
        }]

        outputs = {addrTo: 2, msAddr: 7.99}

        hexRaw = nodes[0].createrawtransaction(inputs, outputs)

        vk0 = nodes[0].dumpprivkey(addrs[0])
        signkeys = [
            vk0,
        ]
        hexRaw1 = nodes[0].signrawtransactionwithkey(hexRaw, signkeys,
                                                     inputs)['hex']

        vk1 = nodes[0].dumpprivkey(addrs[1])
        signkeys = [
            vk1,
        ]
        hexRaw2 = nodes[0].signrawtransactionwithkey(hexRaw1, signkeys,
                                                     inputs)['hex']

        txnid_spendMultisig = nodes[0].sendrawtransaction(hexRaw2)

        self.stakeBlocks(1)
        block1_hash = nodes[0].getblockhash(1)
        ro = nodes[0].getblock(block1_hash)
        assert (txnid_spendMultisig in ro['tx'])

        msAddr256 = nodes[0].addmultisigaddress(2, v, "", False,
                                                True)['address']
        ro = nodes[0].getaddressinfo(msAddr256)
        assert (ro['isscript'] == True)

        msAddr256 = nodes[0].addmultisigaddress(2, v, "", True,
                                                True)['address']
        assert (
            msAddr256 ==
            "tpj1vtll9wnsd7dxzygrjp2j5jr5tgrjsjmj3vwjf7vf60f9p50g5ddqmasmut")

        ro = nodes[0].getaddressinfo(msAddr256)
        assert (ro['isscript'] == True)
        scriptPubKey = ro['scriptPubKey']
        redeemScript = ro['hex']

        mstxid2 = nodes[0].sendtoaddress(msAddr256, 9)
        hexfund = nodes[0].gettransaction(mstxid2)['hex']
        ro = nodes[0].decoderawtransaction(hexfund)

        fundscriptpubkey = ''
        fundoutid = -1
        for vout in ro['vout']:
            if not isclose(vout['value'], 9.0):
                continue
            fundoutid = vout['n']
            fundscriptpubkey = vout['scriptPubKey']['hex']
            assert ('OP_SHA256' in vout['scriptPubKey']['asm'])
        assert (fundoutid >= 0), "fund output not found"

        inputs = [{
            "txid": mstxid2,
            "vout": fundoutid,
            "scriptPubKey": fundscriptpubkey,
            "redeemScript": redeemScript,
            "amount": 9.0,  # Must specify amount
        }]

        addrTo = nodes[2].getnewaddress()
        outputs = {addrTo: 2, msAddr256: 6.99}

        hexRaw = nodes[0].createrawtransaction(inputs, outputs)

        vk0 = nodes[0].dumpprivkey(addrs[0])
        signkeys = [
            vk0,
        ]
        hexRaw1 = nodes[0].signrawtransactionwithkey(hexRaw, signkeys,
                                                     inputs)['hex']

        vk1 = nodes[0].dumpprivkey(addrs[1])
        signkeys = [
            vk1,
        ]
        hexRaw2 = nodes[0].signrawtransactionwithkey(hexRaw1, signkeys,
                                                     inputs)['hex']

        txnid_spendMultisig2 = nodes[0].sendrawtransaction(hexRaw2)

        self.stakeBlocks(1)
        block2_hash = nodes[0].getblockhash(2)
        ro = nodes[0].getblock(block2_hash)
        assert (txnid_spendMultisig2 in ro['tx'])

        ro = nodes[0].getaddressinfo(msAddr)
        scriptPubKey = ro['scriptPubKey']
        redeemScript = ro['hex']

        opts = {"recipe": "abslocktime", "time": 946684800, "addr": msAddr}
        scriptTo = nodes[0].buildscript(opts)['hex']

        outputs = [
            {
                'address': 'script',
                'amount': 8,
                'script': scriptTo
            },
        ]
        mstxid3 = nodes[0].sendtypeto('part', 'part', outputs)

        hexfund = nodes[0].gettransaction(mstxid3)['hex']
        ro = nodes[0].decoderawtransaction(hexfund)

        fundscriptpubkey = ''
        fundoutid = -1
        for vout in ro['vout']:
            if not isclose(vout['value'], 8.0):
                continue
            fundoutid = vout['n']
            fundscriptpubkey = vout['scriptPubKey']['hex']
            assert ('OP_CHECKLOCKTIMEVERIFY' in vout['scriptPubKey']['asm'])
        assert (fundoutid >= 0), "fund output not found"

        inputs = [{
            "txid": mstxid3,
            "vout": fundoutid,
            "scriptPubKey": fundscriptpubkey,
            "redeemScript": redeemScript,
            "amount": 8.0,  # Must specify amount
        }]

        addrTo = nodes[2].getnewaddress()
        outputs = {addrTo: 2, msAddr: 5.99}
        locktime = 946684801

        hexRaw = nodes[0].createrawtransaction(inputs, outputs, locktime)

        vk0 = nodes[0].dumpprivkey(addrs[0])
        signkeys = [
            vk0,
        ]
        hexRaw1 = nodes[0].signrawtransactionwithkey(hexRaw, signkeys,
                                                     inputs)['hex']

        vk1 = nodes[0].dumpprivkey(addrs[1])
        signkeys = [
            vk1,
        ]
        hexRaw2 = nodes[0].signrawtransactionwithkey(hexRaw1, signkeys,
                                                     inputs)['hex']

        txnid_spendMultisig3 = nodes[0].sendrawtransaction(hexRaw2)

        self.stakeBlocks(1)
        block3_hash = nodes[0].getblockhash(3)
        ro = nodes[0].getblock(block3_hash)
        assert (txnid_spendMultisig3 in ro['tx'])

        self.log.info("Coldstake script")

        stakeAddr = nodes[0].getnewaddress()
        addrTo = nodes[0].getnewaddress()

        opts = {
            "recipe": "ifcoinstake",
            "addrstake": stakeAddr,
            "addrspend": msAddr
        }
        scriptTo = nodes[0].buildscript(opts)['hex']

        outputs = [{'address': 'script', 'amount': 1, 'script': scriptTo}]
        txFundId = nodes[0].sendtypeto('part', 'part', outputs)
        hexfund = nodes[0].gettransaction(txFundId)['hex']

        ro = nodes[0].decoderawtransaction(hexfund)
        for vout in ro['vout']:
            if not isclose(vout['value'], 1.0):
                continue
            fundoutn = vout['n']
            fundscriptpubkey = vout['scriptPubKey']['hex']
            assert ('OP_ISCOINSTAKE' in vout['scriptPubKey']['asm'])
        assert (fundoutn >= 0), "fund output not found"

        ro = nodes[0].getaddressinfo(msAddr)
        assert (ro['isscript'] == True)
        scriptPubKey = ro['scriptPubKey']
        redeemScript = ro['hex']

        inputs = [{
            'txid': txFundId,
            'vout': fundoutn,
            'scriptPubKey': fundscriptpubkey,
            'redeemScript': redeemScript,
            'amount': 1.0,
        }]

        outputs = {addrTo: 0.99}
        hexRaw = nodes[0].createrawtransaction(inputs, outputs)

        sig0 = nodes[0].createsignaturewithwallet(hexRaw, inputs[0], addrs[0])
        sig1 = nodes[0].createsignaturewithwallet(hexRaw, inputs[0], addrs[1])

        self.log.info(
            'Test createsignaturewithwallet without providing prevout details')
        outpoint_only = {'txid': txFundId, 'vout': fundoutn}
        sig1_check1 = nodes[0].createsignaturewithwallet(
            hexRaw, outpoint_only, addrs[1])
        assert (sig1 == sig1_check1)
        addr1_privkey = nodes[0].dumpprivkey(addrs[1])
        sig1_check2 = nodes[0].createsignaturewithkey(hexRaw, inputs[0],
                                                      addr1_privkey)
        assert (sig1 == sig1_check2)
        try:
            sig1_check3 = nodes[0].createsignaturewithkey(
                hexRaw, outpoint_only, addr1_privkey)
            assert (
                False), 'createsignaturewithkey passed with no redeemscript'
        except JSONRPCException as e:
            assert ('"redeemScript" is required' in e.error['message'])
        outpoint_only['redeemScript'] = redeemScript
        sig1_check3 = nodes[0].createsignaturewithkey(hexRaw, outpoint_only,
                                                      addr1_privkey)
        assert (sig1 == sig1_check3)

        witnessStack = [
            "",
            sig0,
            sig1,
            redeemScript,
        ]
        hexRawSigned = nodes[0].tx(
            [hexRaw, 'witness=0:' + ':'.join(witnessStack)])

        ro = nodes[0].verifyrawtransaction(hexRawSigned)
        assert (ro['complete'] == True)

        ro = nodes[0].signrawtransactionwithwallet(hexRaw)
        assert (ro['complete'] == True)
        assert (ro['hex'] == hexRawSigned)

        txid = nodes[0].sendrawtransaction(hexRawSigned)

        self.stakeBlocks(1)
        block4_hash = nodes[0].getblockhash(4)
        ro = nodes[0].getblock(block4_hash)
        assert (txid in ro['tx'])

        self.log.info("Test combinerawtransaction")
        unspent0 = nodes[0].listunspent()
        unspent2 = nodes[2].listunspent()

        inputs = [unspent0[0], unspent2[0]]
        outputs = {
            nodes[0].getnewaddress():
            satoshi_round(unspent0[0]['amount'] + unspent2[0]['amount'] -
                          Decimal(0.1))
        }
        rawtx = nodes[0].createrawtransaction(inputs, outputs)

        rawtx0 = nodes[0].signrawtransactionwithwallet(rawtx)
        assert (rawtx0['complete'] == False)

        rawtx2 = nodes[2].signrawtransactionwithwallet(
            rawtx0['hex'])  # Keeps signature from node0
        assert (rawtx2['complete'])

        rawtx2 = nodes[2].signrawtransactionwithwallet(rawtx)
        assert (rawtx2['complete'] == False)

        rawtx_complete = nodes[0].combinerawtransaction(
            [rawtx0['hex'], rawtx2['hex']])
        nodes[0].sendrawtransaction(rawtx_complete)
    def run_test(self):
        self.enable_mocktime()
        self.setup_3_masternodes_network()
        txHashSet = set([
            self.mnOneCollateral.hash, self.mnTwoCollateral.hash,
            self.proRegTx1
        ])
        # check mn list from miner
        self.check_mn_list(self.miner, txHashSet)

        # check status of masternodes
        self.check_mns_status_legacy(self.remoteOne, self.mnOneCollateral.hash)
        self.log.info("MN1 active")
        self.check_mns_status_legacy(self.remoteTwo, self.mnTwoCollateral.hash)
        self.log.info("MN2 active")
        self.check_mns_status(self.remoteDMN1, self.proRegTx1)
        self.log.info("DMN1 active")

        # Prepare the proposal
        self.log.info("preparing budget proposal..")
        firstProposalName = "super-cool"
        firstProposalLink = "https://forum.pivx.org/t/test-proposal"
        firstProposalCycles = 2
        firstProposalAddress = self.miner.getnewaddress()
        firstProposalAmountPerCycle = 300
        nextSuperBlockHeight = self.miner.getnextsuperblock()

        proposalFeeTxId = self.miner.preparebudget(
            firstProposalName, firstProposalLink, firstProposalCycles,
            nextSuperBlockHeight, firstProposalAddress,
            firstProposalAmountPerCycle)

        # generate 3 blocks to confirm the tx (and update the mnping)
        self.stake(3, [self.remoteOne, self.remoteTwo])

        # activate sporks
        self.activate_spork(self.minerPos,
                            "SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT")
        self.activate_spork(self.minerPos,
                            "SPORK_9_MASTERNODE_BUDGET_ENFORCEMENT")
        self.activate_spork(self.minerPos, "SPORK_13_ENABLE_SUPERBLOCKS")

        txinfo = self.miner.gettransaction(proposalFeeTxId)
        assert_equal(txinfo['amount'], -50.00)

        self.log.info("submitting the budget proposal..")

        proposalHash = self.miner.submitbudget(
            firstProposalName, firstProposalLink, firstProposalCycles,
            nextSuperBlockHeight, firstProposalAddress,
            firstProposalAmountPerCycle, proposalFeeTxId)

        # let's wait a little bit and see if all nodes are sync
        time.sleep(1)
        self.check_proposal_existence(firstProposalName, proposalHash)
        self.log.info("proposal broadcast successful!")

        # Proposal is established after 5 minutes. Mine 7 blocks
        # Proposal needs to be on the chain > 5 min.
        self.stake(7, [self.remoteOne, self.remoteTwo])

        # now let's vote for the proposal with the first MN
        self.log.info("Voting with MN1...")
        voteResult = self.ownerOne.mnbudgetvote("alias", proposalHash, "yes",
                                                self.masternodeOneAlias, True)
        assert_equal(voteResult["detail"][0]["result"], "success")

        # check that the vote was accepted everywhere
        self.stake(1, [self.remoteOne, self.remoteTwo])
        self.check_vote_existence(firstProposalName, self.mnOneCollateral.hash,
                                  "YES", True)
        self.log.info("all good, MN1 vote accepted everywhere!")

        # now let's vote for the proposal with the second MN
        self.log.info("Voting with MN2...")
        voteResult = self.ownerTwo.mnbudgetvote("alias", proposalHash, "yes",
                                                self.masternodeTwoAlias, True)
        assert_equal(voteResult["detail"][0]["result"], "success")

        # check that the vote was accepted everywhere
        self.stake(1, [self.remoteOne, self.remoteTwo])
        self.check_vote_existence(firstProposalName, self.mnTwoCollateral.hash,
                                  "YES", True)
        self.log.info("all good, MN2 vote accepted everywhere!")

        # now let's vote for the proposal with the first DMN
        self.log.info("Voting with DMN1...")
        voteResult = self.ownerOne.mnbudgetvote("alias", proposalHash, "yes",
                                                self.proRegTx1)
        assert_equal(voteResult["detail"][0]["result"], "success")

        # check that the vote was accepted everywhere
        self.stake(1, [self.remoteOne, self.remoteTwo])
        self.check_vote_existence(firstProposalName, self.proRegTx1, "YES",
                                  True)
        self.log.info("all good, DMN1 vote accepted everywhere!")

        # Now check the budget
        blockStart = nextSuperBlockHeight
        blockEnd = blockStart + firstProposalCycles * 145
        TotalPayment = firstProposalAmountPerCycle * firstProposalCycles
        Allotted = firstProposalAmountPerCycle
        RemainingPaymentCount = firstProposalCycles
        expected_budget = [
            self.get_proposal_obj(firstProposalName, firstProposalLink,
                                  proposalHash, proposalFeeTxId, blockStart,
                                  blockEnd, firstProposalCycles,
                                  RemainingPaymentCount, firstProposalAddress,
                                  1, 3, 0, 0, satoshi_round(TotalPayment),
                                  satoshi_round(firstProposalAmountPerCycle),
                                  True, True, satoshi_round(Allotted),
                                  satoshi_round(Allotted))
        ]
        self.check_budgetprojection(expected_budget)

        # Quick block count check.
        assert_equal(self.ownerOne.getblockcount(), 276)

        self.log.info("starting budget finalization sync test..")
        self.stake(5, [self.remoteOne, self.remoteTwo])

        # assert that there is no budget finalization first.
        assert_true(len(self.ownerOne.mnfinalbudget("show")) == 0)

        # suggest the budget finalization and confirm the tx (+4 blocks).
        budgetFinHash = self.broadcastbudgetfinalization(
            self.miner, with_ping_mns=[self.remoteOne, self.remoteTwo])
        assert (budgetFinHash != "")
        time.sleep(1)

        self.log.info("checking budget finalization sync..")
        self.check_budget_finalization_sync(0, "OK")

        self.log.info(
            "budget finalization synced!, now voting for the budget finalization.."
        )

        voteResult = self.ownerOne.mnfinalbudget("vote-many", budgetFinHash,
                                                 True)
        assert_equal(voteResult["detail"][0]["result"], "success")
        self.log.info("Remote One voted successfully.")
        voteResult = self.ownerTwo.mnfinalbudget("vote-many", budgetFinHash,
                                                 True)
        assert_equal(voteResult["detail"][0]["result"], "success")
        self.log.info("Remote Two voted successfully.")
        voteResult = self.remoteDMN1.mnfinalbudget("vote", budgetFinHash)
        assert_equal(voteResult["detail"][0]["result"], "success")
        self.log.info("DMN voted successfully.")
        self.stake(2, [self.remoteOne, self.remoteTwo])

        self.log.info("checking finalization votes..")
        self.check_budget_finalization_sync(3, "OK")

        self.stake(8, [self.remoteOne, self.remoteTwo])
        addrInfo = self.miner.listreceivedbyaddress(0, False, False,
                                                    firstProposalAddress)
        assert_equal(addrInfo[0]["amount"], firstProposalAmountPerCycle)

        self.log.info("budget proposal paid!, all good")

        # Check that the proposal info returns updated payment count
        expected_budget[0]["RemainingPaymentCount"] -= 1
        self.check_budgetprojection(expected_budget)

        self.stake(1, [self.remoteOne, self.remoteTwo])

        # now let's verify that votes expire properly.
        # Drop one MN and one DMN
        self.log.info("expiring MN1..")
        self.spend_collateral(self.ownerOne, self.mnOneCollateral, self.miner)
        self.wait_until_mn_vinspent(self.mnOneCollateral.hash, 30,
                                    [self.remoteTwo])
        self.stake(15,
                   [self.remoteTwo])  # create blocks to remove staled votes
        time.sleep(2)  # wait a little bit
        self.check_vote_existence(firstProposalName, self.mnOneCollateral.hash,
                                  "YES", False)
        self.log.info("MN1 vote expired after collateral spend, all good")

        self.log.info("expiring DMN1..")
        lm = self.ownerOne.listmasternodes(self.proRegTx1)[0]
        self.spend_collateral(
            self.ownerOne,
            COutPoint(lm["collateralHash"], lm["collateralIndex"]), self.miner)
        self.wait_until_mn_vinspent(self.proRegTx1, 30, [self.remoteTwo])
        self.stake(15,
                   [self.remoteTwo])  # create blocks to remove staled votes
        time.sleep(2)  # wait a little bit
        self.check_vote_existence(firstProposalName, self.proRegTx1, "YES",
                                  False)
        self.log.info("DMN vote expired after collateral spend, all good")
Beispiel #13
0
    def run_test(self):

        node = self.nodes[0]
        node.spork("SPORK_17_QUORUM_DKG_ENABLED", 0)
        self.wait_for_sporks_same()

        self.mine_quorum()

        txid = node.sendtoaddress(node.getnewaddress(), 1)
        self.wait_for_instantlock(txid, node)

        request_id = self.get_request_id(self.nodes[0].getrawtransaction(txid))
        wait_until(lambda: node.quorum("hasrecsig", 100, request_id, txid))

        rec_sig = node.quorum("getrecsig", 100, request_id, txid)['sig']
        assert (node.verifyislock(request_id, txid, rec_sig))
        # Not mined, should use maxHeight
        assert not node.verifyislock(request_id, txid, rec_sig, 1)
        node.generate(1)
        assert (txid not in node.getrawmempool())
        # Mined but at higher height, should use maxHeight
        assert not node.verifyislock(request_id, txid, rec_sig, 1)
        # Mined, should ignore higher maxHeight
        assert (node.verifyislock(request_id, txid, rec_sig,
                                  node.getblockcount() + 100))

        # Mine one more quorum to have a full active set
        self.mine_quorum()
        # Create an ISLOCK for the oldest quorum i.e. the active quorum which will be moved
        # out of the active set when a new quorum appears
        selected_hash = None
        request_id = None
        oldest_quorum_hash = node.quorum("list")["llmq_test"][-1]
        utxos = node.listunspent()
        fee = 0.001
        amount = 1
        # Try all available utxo's until we have one resulting in a request id which selects the
        # last active quorum
        for utxo in utxos:
            in_amount = float(utxo['amount'])
            if in_amount < amount + fee:
                continue
            outputs = dict()
            outputs[node.getnewaddress()] = satoshi_round(amount)
            change = in_amount - amount - fee
            if change > 0:
                outputs[node.getnewaddress()] = satoshi_round(change)
            rawtx = node.createrawtransaction([utxo], outputs)
            rawtx = node.signrawtransactionwithwallet(rawtx)["hex"]
            request_id = self.get_request_id(rawtx)
            selected_hash = node.quorum('selectquorum', 100,
                                        request_id)["quorumHash"]
            if selected_hash == oldest_quorum_hash:
                break
        assert selected_hash == oldest_quorum_hash
        # Create the ISLOCK, then mine a quorum to move the signing quorum out of the active set
        islock = self.create_islock(rawtx)
        # Mine one block to trigger the "signHeight + dkgInterval" verification for the ISLOCK
        self.mine_quorum()
        # Verify the ISLOCK for a transaction that is not yet known by the node
        rawtx_txid = node.decoderawtransaction(rawtx)["txid"]
        assert_raises_rpc_error(-5,
                                "No such mempool or blockchain transaction",
                                node.getrawtransaction, rawtx_txid)
        assert node.verifyislock(request_id, rawtx_txid,
                                 bytes_to_hex_str(islock.sig),
                                 node.getblockcount())
        # Send the tx and verify the ISLOCK for a now known transaction
        assert rawtx_txid == node.sendrawtransaction(rawtx)
        assert node.verifyislock(request_id, rawtx_txid,
                                 bytes_to_hex_str(islock.sig),
                                 node.getblockcount())
 def total_fees(*txids):
     total = 0
     for txid in txids:
         # '-=' is because gettransaction(txid)['fee'] returns a negative
         total -= self.nodes[0].gettransaction(txid)['fee']
     return satoshi_round(total)
    def run_test(self):
        self.nodes[0].generate(2)
        self.sync_all()
        assert_equal(self.nodes[1].getblockcount(), 202)
        taddr1 = self.nodes[1].getnewaddress()
        saplingAddr0 = self.nodes[0].getnewshieldaddress()
        saplingAddr1 = self.nodes[1].getnewshieldaddress()

        # Verify addresses
        assert(saplingAddr0 in self.nodes[0].listshieldaddresses())
        assert(saplingAddr1 in self.nodes[1].listshieldaddresses())

        # Verify balance
        assert_equal(self.nodes[0].getshieldbalance(saplingAddr0), Decimal('0'))
        assert_equal(self.nodes[1].getshieldbalance(saplingAddr1), Decimal('0'))
        assert_equal(self.nodes[1].getreceivedbyaddress(taddr1), Decimal('0'))

        recipients = [{"address": saplingAddr0, "amount": Decimal('10')}]

        # Try fee too low
        fee_too_low = 0.001
        self.log.info("Trying to send a transaction with fee too low...")
        assert_raises_rpc_error(-4, "Fee set (%.3f) too low. Must be at least" % fee_too_low,
                                self.nodes[0].rawshieldsendmany,
                                "from_transparent", recipients, 1, fee_too_low)

        # Try fee too high.
        fee_too_high = 20
        self.log.info("Good. It was not possible. Now try a tx with fee too high...")
        assert_raises_rpc_error(-4, "The transaction fee is too high: %.2f >" % fee_too_high,
                                self.nodes[0].rawshieldsendmany,
                                "from_transparent", recipients, 1, fee_too_high)

        # Trying to send a rawtx with low fee directly
        self.log.info("Good. It was not possible. Now try with a raw tx...")
        self.restart_node(0, extra_args=self.extra_args[0]+['-minrelaytxfee=0.0000001'])
        rawtx_hex = self.nodes[0].rawshieldsendmany("from_transparent", recipients, 1)
        self.restart_node(0, extra_args=self.extra_args[0])
        connect_nodes(self.nodes[0], 1)
        assert_raises_rpc_error(-26, "insufficient fee",
                                self.nodes[0].sendrawtransaction, rawtx_hex)
        self.log.info("Good. Not accepted in the mempool.")

        # Fixed fee
        fee = 0.05

        # Node 0 shields some funds
        # taddr -> Sapling
        self.log.info("TX 1: shield funds from specified transparent address.")
        mytxid1 = self.nodes[0].shieldsendmany(get_coinstake_address(self.nodes[0]), recipients, 1, fee)

        # shield more funds automatically selecting the transparent inputs
        self.log.info("TX 2: shield funds from any transparent address.")
        mytxid2 = self.nodes[0].shieldsendmany("from_transparent", recipients, 1, fee)

        # Verify priority of tx is INF_PRIORITY, defined as 1E+25 (10000000000000000000000000)
        self.check_tx_priority([mytxid1, mytxid2])
        self.log.info("Priority for tx1 and tx2 checks out")

        self.nodes[2].generate(1)
        self.sync_all()

        # shield more funds creating and then sending a raw transaction
        self.log.info("TX 3: shield funds creating and sending raw transaction.")
        tx_hex = self.nodes[0].rawshieldsendmany("from_transparent", recipients, 1, fee)

        # Check SPORK_20 for sapling maintenance mode
        SPORK_20 = "SPORK_20_SAPLING_MAINTENANCE"
        self.activate_spork(0, SPORK_20)
        self.wait_for_spork(True, SPORK_20)
        assert_raises_rpc_error(-26, "bad-tx-sapling-maintenance",
                                self.nodes[0].sendrawtransaction, tx_hex)
        self.log.info("Good. Not accepted when SPORK_20 is active.")

        # Try with RPC...
        assert_raises_rpc_error(-8, "Invalid parameter, Sapling not active yet",
                                self.nodes[0].shieldsendmany, "from_transparent", recipients, 1, fee)

        # Disable SPORK_20 and retry
        sleep(5)
        self.deactivate_spork(0, SPORK_20)
        self.wait_for_spork(False, SPORK_20)
        mytxid3 = self.nodes[0].sendrawtransaction(tx_hex)
        self.log.info("Good. Accepted when SPORK_20 is not active.")

        # Verify priority of tx is INF_PRIORITY, defined as 1E+25 (10000000000000000000000000)
        self.check_tx_priority([mytxid3])
        self.log.info("Priority for tx3 checks out")

        self.nodes[2].generate(1)
        self.sync_all()

        # Verify balance
        assert_equal(self.nodes[0].getshieldbalance(saplingAddr0), Decimal('30'))
        assert_equal(self.nodes[1].getshieldbalance(saplingAddr1), Decimal('0'))
        assert_equal(self.nodes[1].getreceivedbyaddress(taddr1), Decimal('0'))
        self.log.info("Balances check out")

        # Now disconnect the block, activate SPORK_20, and try to reconnect it
        disconnect_nodes(self.nodes[0], 1)
        tip_hash = self.nodes[0].getbestblockhash()
        self.nodes[0].invalidateblock(tip_hash)
        assert tip_hash != self.nodes[0].getbestblockhash()
        assert_equal(self.nodes[0].getshieldbalance(saplingAddr0), Decimal('20'))
        self.log.info("Now trying to connect block with shield tx, when SPORK_20 is active")
        self.activate_spork(0, SPORK_20)
        self.nodes[0].reconsiderblock(tip_hash)
        assert tip_hash != self.nodes[0].getbestblockhash()         # Block NOT connected
        assert_equal(self.nodes[0].getshieldbalance(saplingAddr0), Decimal('20'))
        self.log.info("Good. Not possible.")

        # Deactivate SPORK_20 and reconnect
        sleep(1)
        self.deactivate_spork(0, SPORK_20)
        self.nodes[0].reconsiderblock(tip_hash)
        self.nodes[0].syncwithvalidationinterfacequeue()
        assert_equal(tip_hash, self.nodes[0].getbestblockhash())    # Block connected
        assert_equal(self.nodes[0].getshieldbalance(saplingAddr0), Decimal('30'))
        self.log.info("Reconnected after deactivation of SPORK_20. Balance restored.")
        connect_nodes(self.nodes[0], 1)

        # Node 0 sends some shield funds to node 1
        # Sapling -> Sapling
        #         -> Sapling (change)
        self.log.info("TX 4: shield transaction from specified sapling address.")
        recipients4 = [{"address": saplingAddr1, "amount": Decimal('10')}]
        mytxid4 = self.nodes[0].shieldsendmany(saplingAddr0, recipients4, 1, fee)
        self.check_tx_priority([mytxid4])

        self.nodes[2].generate(1)
        self.sync_all()

        # Send more shield funds (this time with automatic selection of the source)
        self.log.info("TX 5: shield transaction from any sapling address.")
        recipients5 = [{"address": saplingAddr1, "amount": Decimal('5')}]
        mytxid5 = self.nodes[0].shieldsendmany("from_shield", recipients5, 1, fee)
        self.check_tx_priority([mytxid5])

        self.nodes[2].generate(1)
        self.sync_all()

        # Send more shield funds (with create + send raw transaction)
        self.log.info("TX 6: shield raw transaction.")
        tx_hex = self.nodes[0].rawshieldsendmany("from_shield", recipients5, 1, fee)
        mytxid6 = self.nodes[0].sendrawtransaction(tx_hex)
        self.check_tx_priority([mytxid6])

        self.nodes[2].generate(1)
        self.sync_all()

        # Shield more funds to a different address to verify multi-source notes spending
        saplingAddr2 = self.nodes[0].getnewshieldaddress()
        self.log.info("TX 7: shield funds to later verify multi source notes spending.")
        recipients = [{"address": saplingAddr2, "amount": Decimal('10')}]
        mytxid7 = self.nodes[0].shieldsendmany(get_coinstake_address(self.nodes[0]), recipients, 1, fee)
        self.check_tx_priority([mytxid7])

        self.nodes[2].generate(5)
        self.sync_all()

        # Verify multi-source notes spending
        tAddr0 = self.nodes[0].getnewaddress()
        self.log.info("TX 8: verifying multi source notes spending.")
        recipients = [{"address": tAddr0, "amount": Decimal('11')}]
        mytxid8 = self.nodes[0].shieldsendmany("from_shield", recipients, 1, fee)
        self.check_tx_priority([mytxid8])

        self.nodes[2].generate(1)
        self.sync_all()

        # Verify balance
        assert_equal(self.nodes[0].getshieldbalance(saplingAddr0), Decimal('4.9'))   # 30 received - (20 sent + 0.15 fee) - 4.95 sent
        assert_equal(self.nodes[1].getshieldbalance(saplingAddr1), Decimal('20'))    # 20 received
        assert_equal(self.nodes[0].getshieldbalance(saplingAddr2), Decimal('3.9'))   # 10 received - 10 sent + 3.9 change
        assert_equal(self.nodes[1].getreceivedbyaddress(taddr1), Decimal('0'))
        assert_equal(self.nodes[0].getshieldbalance(), Decimal('8.8'))
        self.log.info("Balances check out")

        # Node 1 sends some shield funds to node 0, as well as unshielding
        # Sapling -> Sapling
        #         -> taddr
        #         -> Sapling (change)
        self.log.info("TX 10: deshield funds from specified sapling address.")
        recipients7 = [{"address": saplingAddr0, "amount": Decimal('8')}]
        recipients7.append({"address": taddr1, "amount": Decimal('10')})
        mytxid7 = self.nodes[1].shieldsendmany(saplingAddr1, recipients7, 1, fee)
        self.check_tx_priority([mytxid7])

        self.nodes[2].generate(1)
        self.sync_all()

        # Verify balance
        assert_equal(self.nodes[0].getshieldbalance(saplingAddr0), Decimal('12.9')) # 4.9 prev balance + 8 received
        assert_equal(self.nodes[1].getshieldbalance(saplingAddr1), Decimal('1.95')) # 20 prev balance - (18 sent + 0.05 fee)
        assert_equal(self.nodes[1].getreceivedbyaddress(taddr1), Decimal('10'))
        self.log.info("Balances check out")

        # Verify existence of Sapling related JSON fields
        resp = self.nodes[0].getrawtransaction(mytxid7, 1)
        assert_equal(Decimal(resp['valueBalance']), Decimal('10.05'))    # 20 shield input - 8 shield spend - 1.95 change
        assert_equal(len(resp['vShieldSpend']), 3)
        assert_equal(len(resp['vShieldOutput']), 2)
        assert('bindingSig' in resp)
        shieldedSpend = resp['vShieldSpend'][0]
        assert('cv' in shieldedSpend)
        assert('anchor' in shieldedSpend)
        assert('nullifier' in shieldedSpend)
        assert('rk' in shieldedSpend)
        assert('proof' in shieldedSpend)
        assert('spendAuthSig' in shieldedSpend)
        shieldedOutput = resp['vShieldOutput'][0]
        assert('cv' in shieldedOutput)
        assert('cmu' in shieldedOutput)
        assert('ephemeralKey' in shieldedOutput)
        assert('encCiphertext' in shieldedOutput)
        assert('outCiphertext' in shieldedOutput)
        assert('proof' in shieldedOutput)
        self.log.info("Raw transaction decoding checks out")

        # Verify importing a spending key will update the nullifiers and witnesses correctly
        self.log.info("Checking exporting/importing a spending key...")
        sk0 = self.nodes[0].exportsaplingkey(saplingAddr0)
        saplingAddrInfo0 = self.nodes[2].importsaplingkey(sk0, "yes")
        assert_equal(saplingAddrInfo0["address"], saplingAddr0)
        assert_equal(self.nodes[2].getshieldbalance(saplingAddrInfo0["address"]), Decimal('12.9'))
        sk1 = self.nodes[1].exportsaplingkey(saplingAddr1)
        saplingAddrInfo1 = self.nodes[2].importsaplingkey(sk1, "yes")
        assert_equal(saplingAddrInfo1["address"], saplingAddr1)
        assert_equal(self.nodes[2].getshieldbalance(saplingAddrInfo1["address"]), Decimal('1.95'))

        # Verify importing a viewing key will update the nullifiers and witnesses correctly
        self.log.info("Checking exporting/importing a viewing key...")
        extfvk0 = self.nodes[0].exportsaplingviewingkey(saplingAddr0)
        saplingAddrInfo0 = self.nodes[3].importsaplingviewingkey(extfvk0, "yes")
        assert_equal(saplingAddrInfo0["address"], saplingAddr0)
        assert_equal(Decimal(self.nodes[3].getshieldbalance(saplingAddrInfo0["address"], 1, True)), Decimal('12.9'))
        extfvk1 = self.nodes[1].exportsaplingviewingkey(saplingAddr1)
        saplingAddrInfo1 = self.nodes[3].importsaplingviewingkey(extfvk1, "yes")
        assert_equal(saplingAddrInfo1["address"], saplingAddr1)
        assert_equal(self.nodes[3].getshieldbalance(saplingAddrInfo1["address"], 1, True), Decimal('1.95'))
        # no balance in the wallet
        assert_equal(self.nodes[3].getshieldbalance(), Decimal('0'))
        # watch only balance
        assert_equal(self.nodes[3].getshieldbalance("*", 1, True), Decimal('14.85'))

        # Now shield some funds using sendmany
        self.log.info("TX11: Shielding coins to multiple destinations with sendmany RPC...")
        prev_balance = self.nodes[0].getbalance()
        recipients8 = {saplingAddr0: Decimal('8'), saplingAddr1: Decimal('1'), saplingAddr2: Decimal('0.5')}
        mytxid11 = self.nodes[0].sendmany("", recipients8)
        self.check_tx_priority([mytxid11])
        self.log.info("Done. Checking details and balances...")

        # Decrypted transaction details should be correct
        pt = self.nodes[0].viewshieldtransaction(mytxid11)
        fee = pt["fee"]
        assert_equal(pt['txid'], mytxid11)
        assert_equal(len(pt['spends']), 0)
        assert_equal(len(pt['outputs']), 3)
        found = [False] * 3
        for out in pt['outputs']:
            assert_equal(pt['outputs'].index(out), out['output'])
            if out['address'] == saplingAddr0:
                assert_equal(out['outgoing'], False)
                assert_equal(out['value'], Decimal('8'))
                found[0] = True
            elif out['address'] == saplingAddr1:
                assert_equal(out['outgoing'], True)
                assert_equal(out['value'], Decimal('1'))
                found[1] = True
            else:
                assert_equal(out['address'], saplingAddr2)
                assert_equal(out['outgoing'], False)
                assert_equal(out['value'], Decimal('0.5'))
                found[2] = True
        assert_equal(found, [True] * 3)

        # Verify balance
        self.nodes[2].generate(1)
        self.sync_all()
        assert_equal(self.nodes[0].getshieldbalance(saplingAddr0), Decimal('20.9'))  # 12.9 prev balance + 8 received
        assert_equal(self.nodes[1].getshieldbalance(saplingAddr1), Decimal('2.95'))  # 1.95 prev balance + 1 received
        assert_equal(self.nodes[0].getshieldbalance(saplingAddr2), Decimal('4.4'))  # 3.9 prev balance + 0.5 received
        # Balance of node 0 is: prev_balance - 1 QRTC (+fee) sent externally +  250 QRTC matured coinbase
        assert_equal(self.nodes[0].getbalance(), satoshi_round(prev_balance + Decimal('249') - Decimal(fee)))

        # Now shield some funds using sendtoaddress
        self.log.info("TX12: Shielding coins with sendtoaddress RPC...")
        prev_balance = self.nodes[0].getbalance()
        mytxid12 = self.nodes[0].sendtoaddress(saplingAddr0, Decimal('10'))
        self.check_tx_priority([mytxid12])
        self.log.info("Done. Checking details and balances...")

        # Decrypted transaction details should be correct
        pt = self.nodes[0].viewshieldtransaction(mytxid12)
        fee = pt["fee"]
        assert_equal(pt['txid'], mytxid12)
        assert_equal(len(pt['spends']), 0)
        assert_equal(len(pt['outputs']), 1)
        out = pt['outputs'][0]
        assert_equal(out['address'], saplingAddr0)
        assert_equal(out['outgoing'], False)
        assert_equal(out['value'], Decimal('10'))

        # Verify balance
        self.nodes[2].generate(1)
        self.sync_all()
        assert_equal(self.nodes[0].getshieldbalance(saplingAddr0), Decimal('30.9'))  # 20.9 prev balance + 10 received

        self.log.info("All good.")
Beispiel #16
0
    def test_compactblock_construction(self, node, test_node, version, use_witness_address):
        # Generate a bunch of transactions.
        node.generate(101)
        num_transactions = 25
        address = node.getnewaddress()
        if use_witness_address:
            # Want at least one segwit spend, so move all funds to
            # a witness address.
            address = node.addwitnessaddress(address)
            value_to_send = node.getbalance()
            node.sendtoaddress(address, satoshi_round(value_to_send-Decimal(0.1)))
            node.generate(1)

        segwit_tx_generated = False
        for i in range(num_transactions):
            txid = node.sendtoaddress(address, 0.1)
            hex_tx = node.gettransaction(txid)["hex"]
            tx = FromHex(CTransaction(), hex_tx)
            if not tx.wit.is_null():
                segwit_tx_generated = True

        if use_witness_address:
            assert(segwit_tx_generated) # check that our test is not broken

        # Wait until we've seen the block announcement for the resulting tip
        tip = int(node.getbestblockhash(), 16)
        test_node.wait_for_block_announcement(tip)

        # Make sure we will receive a fast-announce compact block
        self.request_cb_announcements(test_node, node, version)

        # Now mine a block, and look at the resulting compact block.
        test_node.clear_block_announcement()
        block_hash = int(node.generate(1)[0], 16)

        # Store the raw block in our internal format.
        block = FromHex(CBlock(), node.getblock("%02x" % block_hash, False))
        for tx in block.vtx:
            tx.calc_sha256()
        block.rehash()

        # Wait until the block was announced (via compact blocks)
        wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock)

        # Now fetch and check the compact block
        header_and_shortids = None
        with mininode_lock:
            assert("cmpctblock" in test_node.last_message)
            # Convert the on-the-wire representation to absolute indexes
            header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
        self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)

        # Now fetch the compact block using a normal non-announce getdata
        with mininode_lock:
            test_node.clear_block_announcement()
            inv = CInv(4, block_hash)  # 4 == "CompactBlock"
            test_node.send_message(msg_getdata([inv]))

        wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock)

        # Now fetch and check the compact block
        header_and_shortids = None
        with mininode_lock:
            assert("cmpctblock" in test_node.last_message)
            # Convert the on-the-wire representation to absolute indexes
            header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
        self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)
Beispiel #17
0
    def run_test(self):
        # Mine some blocks and have them mature.
        self.nodes[0].generate(101)
        utxo = self.nodes[0].listunspent(10)
        txid = utxo[0]['txid']
        vout = utxo[0]['vout']
        value = utxo[0]['amount']

        fee = Decimal("0.0001")
        # MAX_ANCESTORS transactions off a confirmed tx should be fine
        chain = []
        for i in range(MAX_ANCESTORS):
            (txid,
             sent_value) = self.chain_transaction(self.nodes[0], txid, 0,
                                                  value, fee, 1)
            value = sent_value
            chain.append(txid)

        # Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor
        # count and fees should look correct
        mempool = self.nodes[0].getrawmempool(True)
        assert_equal(len(mempool), MAX_ANCESTORS)
        descendant_count = 1
        descendant_fees = 0
        descendant_size = 0

        ancestor_size = sum([mempool[tx]['size'] for tx in mempool])
        ancestor_count = MAX_ANCESTORS
        ancestor_fees = sum([mempool[tx]['fee'] for tx in mempool])

        descendants = []
        ancestors = list(chain)
        for x in reversed(chain):
            # Check that getmempoolentry is consistent with getrawmempool
            entry = self.nodes[0].getmempoolentry(x)
            assert_equal(entry, mempool[x])

            # Check that the descendant calculations are correct
            assert_equal(mempool[x]['descendantcount'], descendant_count)
            descendant_fees += mempool[x]['fee']
            assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee'])
            assert_equal(mempool[x]['fees']['base'], mempool[x]['fee'])
            assert_equal(mempool[x]['fees']['modified'],
                         mempool[x]['modifiedfee'])
            assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN)
            assert_equal(mempool[x]['fees']['descendant'], descendant_fees)
            descendant_size += mempool[x]['size']
            assert_equal(mempool[x]['descendantsize'], descendant_size)
            descendant_count += 1

            # Check that ancestor calculations are correct
            assert_equal(mempool[x]['ancestorcount'], ancestor_count)
            assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN)
            assert_equal(mempool[x]['ancestorsize'], ancestor_size)
            ancestor_size -= mempool[x]['size']
            ancestor_fees -= mempool[x]['fee']
            ancestor_count -= 1

            # Check that parent/child list is correct
            assert_equal(mempool[x]['spentby'], descendants[-1:])
            assert_equal(mempool[x]['depends'], ancestors[-2:-1])

            # Check that getmempooldescendants is correct
            assert_equal(sorted(descendants),
                         sorted(self.nodes[0].getmempooldescendants(x)))

            # Check getmempooldescendants verbose output is correct
            for descendant, dinfo in self.nodes[0].getmempooldescendants(
                    x, True).items():
                assert_equal(dinfo['depends'],
                             [chain[chain.index(descendant) - 1]])
                if dinfo['descendantcount'] > 1:
                    assert_equal(dinfo['spentby'],
                                 [chain[chain.index(descendant) + 1]])
                else:
                    assert_equal(dinfo['spentby'], [])
            descendants.append(x)

            # Check that getmempoolancestors is correct
            ancestors.remove(x)
            assert_equal(sorted(ancestors),
                         sorted(self.nodes[0].getmempoolancestors(x)))

            # Check that getmempoolancestors verbose output is correct
            for ancestor, ainfo in self.nodes[0].getmempoolancestors(
                    x, True).items():
                assert_equal(ainfo['spentby'],
                             [chain[chain.index(ancestor) + 1]])
                if ainfo['ancestorcount'] > 1:
                    assert_equal(ainfo['depends'],
                                 [chain[chain.index(ancestor) - 1]])
                else:
                    assert_equal(ainfo['depends'], [])

        # Check that getmempoolancestors/getmempooldescendants correctly handle verbose=true
        v_ancestors = self.nodes[0].getmempoolancestors(chain[-1], True)
        assert_equal(len(v_ancestors), len(chain) - 1)
        for x in v_ancestors.keys():
            assert_equal(mempool[x], v_ancestors[x])
        assert (chain[-1] not in v_ancestors.keys())

        v_descendants = self.nodes[0].getmempooldescendants(chain[0], True)
        assert_equal(len(v_descendants), len(chain) - 1)
        for x in v_descendants.keys():
            assert_equal(mempool[x], v_descendants[x])
        assert (chain[0] not in v_descendants.keys())

        # Check that ancestor modified fees includes fee deltas from
        # prioritisetransaction
        self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=1000)
        mempool = self.nodes[0].getrawmempool(True)
        ancestor_fees = 0
        for x in chain:
            ancestor_fees += mempool[x]['fee']
            assert_equal(mempool[x]['fees']['ancestor'],
                         ancestor_fees + Decimal('0.00001'))
            assert_equal(mempool[x]['ancestorfees'],
                         ancestor_fees * COIN + 1000)

        # Undo the prioritisetransaction for later tests
        self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=-1000)

        # Check that descendant modified fees includes fee deltas from
        # prioritisetransaction
        self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=1000)
        mempool = self.nodes[0].getrawmempool(True)

        descendant_fees = 0
        for x in reversed(chain):
            descendant_fees += mempool[x]['fee']
            assert_equal(mempool[x]['fees']['descendant'],
                         descendant_fees + Decimal('0.00001'))
            assert_equal(mempool[x]['descendantfees'],
                         descendant_fees * COIN + 1000)

        # Adding one more transaction on to the chain should fail.
        assert_raises_rpc_error(-26, "too-long-mempool-chain",
                                self.chain_transaction, self.nodes[0], txid,
                                vout, value, fee, 1)

        # Check that prioritising a tx before it's added to the mempool works
        # First clear the mempool by mining a block.
        self.nodes[0].generate(1)
        sync_blocks(self.nodes)
        assert_equal(len(self.nodes[0].getrawmempool()), 0)
        # Prioritise a transaction that has been mined, then add it back to the
        # mempool by using invalidateblock.
        self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=2000)
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        # Keep node1's tip synced with node0
        self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())

        # Now check that the transaction is in the mempool, with the right modified fee
        mempool = self.nodes[0].getrawmempool(True)

        descendant_fees = 0
        for x in reversed(chain):
            descendant_fees += mempool[x]['fee']
            if (x == chain[-1]):
                assert_equal(mempool[x]['modifiedfee'],
                             mempool[x]['fee'] + satoshi_round(0.00002))
                assert_equal(mempool[x]['fees']['modified'],
                             mempool[x]['fee'] + satoshi_round(0.00002))
            assert_equal(mempool[x]['descendantfees'],
                         descendant_fees * COIN + 2000)
            assert_equal(mempool[x]['fees']['descendant'],
                         descendant_fees + satoshi_round(0.00002))

        # TODO: check that node1's mempool is as expected

        # TODO: test ancestor size limits

        # Now test descendant chain limits
        txid = utxo[1]['txid']
        value = utxo[1]['amount']
        vout = utxo[1]['vout']

        transaction_package = []
        tx_children = []
        # First create one parent tx with 10 children
        (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout,
                                                    value, fee, 10)
        parent_transaction = txid
        for i in range(10):
            transaction_package.append({
                'txid': txid,
                'vout': i,
                'amount': sent_value
            })

        # Sign and send up to MAX_DESCENDANT transactions chained off the parent tx
        for i in range(MAX_DESCENDANTS - 1):
            utxo = transaction_package.pop(0)
            (txid,
             sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'],
                                                  utxo['vout'], utxo['amount'],
                                                  fee, 10)
            if utxo['txid'] is parent_transaction:
                tx_children.append(txid)
            for j in range(10):
                transaction_package.append({
                    'txid': txid,
                    'vout': j,
                    'amount': sent_value
                })

        mempool = self.nodes[0].getrawmempool(True)
        assert_equal(mempool[parent_transaction]['descendantcount'],
                     MAX_DESCENDANTS)
        assert_equal(sorted(mempool[parent_transaction]['spentby']),
                     sorted(tx_children))

        for child in tx_children:
            assert_equal(mempool[child]['depends'], [parent_transaction])

        # Sending one more chained transaction will fail
        utxo = transaction_package.pop(0)
        assert_raises_rpc_error(-26, "too-long-mempool-chain",
                                self.chain_transaction, self.nodes[0],
                                utxo['txid'], utxo['vout'], utxo['amount'],
                                fee, 10)

        # TODO: check that node1's mempool is as expected

        # TODO: test descendant size limits

        # Test reorg handling
        # First, the basics:
        self.nodes[0].generate(1)
        sync_blocks(self.nodes)
        self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash())
        self.nodes[1].reconsiderblock(self.nodes[0].getbestblockhash())

        # Now test the case where node1 has a transaction T in its mempool that
        # depends on transactions A and B which are in a mined block, and the
        # block containing A and B is disconnected, AND B is not accepted back
        # into node1's mempool because its ancestor count is too high.

        # Create 8 transactions, like so:
        # Tx0 -> Tx1 (vout0)
        #   \--> Tx2 (vout1) -> Tx3 -> Tx4 -> Tx5 -> Tx6 -> Tx7
        #
        # Mine them in the next block, then generate a new tx8 that spends
        # Tx1 and Tx7, and add to node1's mempool, then disconnect the
        # last block.

        # Create tx0 with 2 outputs
        utxo = self.nodes[0].listunspent()
        txid = utxo[0]['txid']
        value = utxo[0]['amount']
        vout = utxo[0]['vout']

        send_value = satoshi_round((value - fee) / 2)
        inputs = [{'txid': txid, 'vout': vout}]
        outputs = {}
        for i in range(2):
            outputs[self.nodes[0].getnewaddress()] = send_value
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
        txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
        tx0_id = txid
        value = send_value

        # Create tx1
        tx1_id, _ = self.chain_transaction(self.nodes[0], tx0_id, 0, value,
                                           fee, 1)

        # Create tx2-7
        vout = 1
        txid = tx0_id
        for i in range(6):
            (txid,
             sent_value) = self.chain_transaction(self.nodes[0], txid, vout,
                                                  value, fee, 1)
            vout = 0
            value = sent_value

        # Mine these in a block
        self.nodes[0].generate(1)
        self.sync_all()

        # Now generate tx8, with a big fee
        inputs = [{'txid': tx1_id, 'vout': 0}, {'txid': txid, 'vout': 0}]
        outputs = {self.nodes[0].getnewaddress(): send_value + value - 4 * fee}
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
        txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
        sync_mempools(self.nodes)

        # Now try to disconnect the tip on each node...
        self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        sync_blocks(self.nodes)
Beispiel #18
0
    def run_test(self):
        # Mine some blocks and have them mature.
        self.nodes[0].generate(101)
        utxo = self.nodes[0].listunspent(10)
        txid = utxo[0]['txid']
        vout = utxo[0]['vout']
        value = utxo[0]['amount']

        fee = Decimal("0.0001")
        # MAX_ANCESTORS transactions off a confirmed tx should be fine
        chain = []
        for i in range(MAX_ANCESTORS):
            (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, 0, value, fee, 1)
            value = sent_value
            chain.append(txid)

        # Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor
        # count and fees should look correct
        mempool = self.nodes[0].getrawmempool(True)
        assert_equal(len(mempool), MAX_ANCESTORS)
        descendant_count = 1
        descendant_fees = 0
        descendant_size = 0

        ancestor_size = sum([mempool[tx]['size'] for tx in mempool])
        ancestor_count = MAX_ANCESTORS
        ancestor_fees = sum([mempool[tx]['fee'] for tx in mempool])

        descendants = []
        ancestors = list(chain)
        for x in reversed(chain):
            # Check that getmempoolentry is consistent with getrawmempool
            entry = self.nodes[0].getmempoolentry(x)
            assert_equal(entry, mempool[x])

            # Check that the descendant calculations are correct
            assert_equal(mempool[x]['descendantcount'], descendant_count)
            descendant_fees += mempool[x]['fee']
            assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee'])
            assert_equal(mempool[x]['fees']['base'], mempool[x]['fee'])
            assert_equal(mempool[x]['fees']['modified'], mempool[x]['modifiedfee'])
            assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN)
            assert_equal(mempool[x]['fees']['descendant'], descendant_fees)
            descendant_size += mempool[x]['size']
            assert_equal(mempool[x]['descendantsize'], descendant_size)
            descendant_count += 1

            # Check that ancestor calculations are correct
            assert_equal(mempool[x]['ancestorcount'], ancestor_count)
            assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN)
            assert_equal(mempool[x]['ancestorsize'], ancestor_size)
            ancestor_size -= mempool[x]['size']
            ancestor_fees -= mempool[x]['fee']
            ancestor_count -= 1

            # Check that parent/child list is correct
            assert_equal(mempool[x]['spentby'], descendants[-1:])
            assert_equal(mempool[x]['depends'], ancestors[-2:-1])

            # Check that getmempooldescendants is correct
            assert_equal(sorted(descendants), sorted(self.nodes[0].getmempooldescendants(x)))

            # Check getmempooldescendants verbose output is correct
            for descendant, dinfo in self.nodes[0].getmempooldescendants(x, True).items():
                assert_equal(dinfo['depends'], [chain[chain.index(descendant)-1]])
                if dinfo['descendantcount'] > 1:
                    assert_equal(dinfo['spentby'], [chain[chain.index(descendant)+1]])
                else:
                    assert_equal(dinfo['spentby'], [])
            descendants.append(x)

            # Check that getmempoolancestors is correct
            ancestors.remove(x)
            assert_equal(sorted(ancestors), sorted(self.nodes[0].getmempoolancestors(x)))

            # Check that getmempoolancestors verbose output is correct
            for ancestor, ainfo in self.nodes[0].getmempoolancestors(x, True).items():
                assert_equal(ainfo['spentby'], [chain[chain.index(ancestor)+1]])
                if ainfo['ancestorcount'] > 1:
                    assert_equal(ainfo['depends'], [chain[chain.index(ancestor)-1]])
                else:
                    assert_equal(ainfo['depends'], [])


        # Check that getmempoolancestors/getmempooldescendants correctly handle verbose=true
        v_ancestors = self.nodes[0].getmempoolancestors(chain[-1], True)
        assert_equal(len(v_ancestors), len(chain)-1)
        for x in v_ancestors.keys():
            assert_equal(mempool[x], v_ancestors[x])
        assert(chain[-1] not in v_ancestors.keys())

        v_descendants = self.nodes[0].getmempooldescendants(chain[0], True)
        assert_equal(len(v_descendants), len(chain)-1)
        for x in v_descendants.keys():
            assert_equal(mempool[x], v_descendants[x])
        assert(chain[0] not in v_descendants.keys())

        # Check that ancestor modified fees includes fee deltas from
        # prioritisetransaction
        self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=1000)
        mempool = self.nodes[0].getrawmempool(True)
        ancestor_fees = 0
        for x in chain:
            ancestor_fees += mempool[x]['fee']
            assert_equal(mempool[x]['fees']['ancestor'], ancestor_fees + Decimal('0.00001'))
            assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN + 1000)

        # Undo the prioritisetransaction for later tests
        self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=-1000)

        # Check that descendant modified fees includes fee deltas from
        # prioritisetransaction
        self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=1000)
        mempool = self.nodes[0].getrawmempool(True)

        descendant_fees = 0
        for x in reversed(chain):
            descendant_fees += mempool[x]['fee']
            assert_equal(mempool[x]['fees']['descendant'], descendant_fees + Decimal('0.00001'))
            assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000)

        # Adding one more transaction on to the chain should fail.
        assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1)

        # Check that prioritising a tx before it's added to the mempool works
        # First clear the mempool by mining a block.
        self.nodes[0].generate(1)
        sync_blocks(self.nodes)
        assert_equal(len(self.nodes[0].getrawmempool()), 0)
        # Prioritise a transaction that has been mined, then add it back to the
        # mempool by using invalidateblock.
        self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=2000)
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        # Keep node1's tip synced with node0
        self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())

        # Now check that the transaction is in the mempool, with the right modified fee
        mempool = self.nodes[0].getrawmempool(True)

        descendant_fees = 0
        for x in reversed(chain):
            descendant_fees += mempool[x]['fee']
            if (x == chain[-1]):
                assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']+satoshi_round(0.00002))
                assert_equal(mempool[x]['fees']['modified'], mempool[x]['fee']+satoshi_round(0.00002))
            assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 2000)
            assert_equal(mempool[x]['fees']['descendant'], descendant_fees+satoshi_round(0.00002))

        # TODO: check that node1's mempool is as expected

        # TODO: test ancestor size limits

        # Now test descendant chain limits
        txid = utxo[1]['txid']
        value = utxo[1]['amount']
        vout = utxo[1]['vout']

        transaction_package = []
        tx_children = []
        # First create one parent tx with 10 children
        (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 10)
        parent_transaction = txid
        for i in range(10):
            transaction_package.append({'txid': txid, 'vout': i, 'amount': sent_value})

        # Sign and send up to MAX_DESCENDANT transactions chained off the parent tx
        for i in range(MAX_DESCENDANTS - 1):
            utxo = transaction_package.pop(0)
            (txid, sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
            if utxo['txid'] is parent_transaction:
                tx_children.append(txid)
            for j in range(10):
                transaction_package.append({'txid': txid, 'vout': j, 'amount': sent_value})

        mempool = self.nodes[0].getrawmempool(True)
        assert_equal(mempool[parent_transaction]['descendantcount'], MAX_DESCENDANTS)
        assert_equal(sorted(mempool[parent_transaction]['spentby']), sorted(tx_children))

        for child in tx_children:
            assert_equal(mempool[child]['depends'], [parent_transaction])

        # Sending one more chained transaction will fail
        utxo = transaction_package.pop(0)
        assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)

        # TODO: check that node1's mempool is as expected

        # TODO: test descendant size limits

        # Test reorg handling
        # First, the basics:
        self.nodes[0].generate(1)
        sync_blocks(self.nodes)
        self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash())
        self.nodes[1].reconsiderblock(self.nodes[0].getbestblockhash())

        # Now test the case where node1 has a transaction T in its mempool that
        # depends on transactions A and B which are in a mined block, and the
        # block containing A and B is disconnected, AND B is not accepted back
        # into node1's mempool because its ancestor count is too high.

        # Create 8 transactions, like so:
        # Tx0 -> Tx1 (vout0)
        #   \--> Tx2 (vout1) -> Tx3 -> Tx4 -> Tx5 -> Tx6 -> Tx7
        #
        # Mine them in the next block, then generate a new tx8 that spends
        # Tx1 and Tx7, and add to node1's mempool, then disconnect the
        # last block.

        # Create tx0 with 2 outputs
        utxo = self.nodes[0].listunspent()
        txid = utxo[0]['txid']
        value = utxo[0]['amount']
        vout = utxo[0]['vout']

        send_value = satoshi_round((value - fee)/2)
        inputs = [ {'txid' : txid, 'vout' : vout} ]
        outputs = {}
        for i in range(2):
            outputs[self.nodes[0].getnewaddress()] = send_value
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
        txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
        tx0_id = txid
        value = send_value

        # Create tx1
        tx1_id, _ = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1)

        # Create tx2-7
        vout = 1
        txid = tx0_id
        for i in range(6):
            (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1)
            vout = 0
            value = sent_value

        # Mine these in a block
        self.nodes[0].generate(1)
        self.sync_all()

        # Now generate tx8, with a big fee
        inputs = [ {'txid' : tx1_id, 'vout': 0}, {'txid' : txid, 'vout': 0} ]
        outputs = { self.nodes[0].getnewaddress() : send_value + value - 4*fee }
        rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
        signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
        txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
        sync_mempools(self.nodes)

        # Now try to disconnect the tip on each node...
        self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
        self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
        sync_blocks(self.nodes)
        def total_fees(*txids):
            total = 0
            for txid in txids:
                total += self.nodes[0].calculate_fee_from_txid(txid)

            return satoshi_round(total)