def test_prioritised_transactions(self): # Ensure that fee deltas used via prioritisetransaction are # correctly used by replacement logic # 1. Check that feeperkb uses modified fees tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, n_sequence=0)] tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] tx1a_hex = tx_to_hex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) # Higher fee, but the actual fee per KB is much lower. tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, n_sequence=0)] tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*740000]))] tx1b_hex = tx_to_hex(tx1b) # Verify tx1b cannot replace tx1a. assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) # Use prioritisetransaction to set tx1a's fee to 0. self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1*COIN)) # Now tx1b should be able to replace tx1a tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) assert(tx1b_txid in self.nodes[0].getrawmempool()) # 2. Check that absolute fee checks use modified fee. tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) tx2a = CTransaction() tx2a.vin = [CTxIn(tx1_outpoint, n_sequence=0)] tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] tx2a_hex = tx_to_hex(tx2a) self.nodes[0].sendrawtransaction(tx2a_hex, True) # Lower fee, but we'll prioritise it tx2b = CTransaction() tx2b.vin = [CTxIn(tx1_outpoint, n_sequence=0)] tx2b.vout = [CTxOut(int(1.01*COIN), CScript([b'a']))] tx2b.rehash() tx2b_hex = tx_to_hex(tx2b) # Verify tx2b cannot replace tx2a. assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, True) # Now prioritise tx2b to have a higher modified fee self.nodes[0].prioritisetransaction(txid=tx2b.hash, fee_delta=int(0.1*COIN)) # tx2b should now be accepted tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True) assert(tx2b_txid in self.nodes[0].getrawmempool())
def test_too_many_replacements(self): """Replacements that evict too many transactions are rejected""" # Try directly replacing more than MAX_REPLACEMENT_LIMIT # transactions # Start by creating a single transaction with many outputs initial_n_value = 10 * COIN utxo = make_utxo(self.nodes[0], initial_n_value) fee = int(0.0001 * COIN) split_value = int( (initial_n_value - fee) / (MAX_REPLACEMENT_LIMIT + 1)) outputs = [] for i in range(MAX_REPLACEMENT_LIMIT + 1): outputs.append(CTxOut(split_value, CScript([1]))) splitting_tx = CTransaction() splitting_tx.vin = [CTxIn(utxo, n_sequence=0)] splitting_tx.vout = outputs splitting_tx_hex = tx_to_hex(splitting_tx) txid = self.nodes[0].sendrawtransaction(splitting_tx_hex, True) txid = int(txid, 16) # Now spend each of those outputs individually for i in range(MAX_REPLACEMENT_LIMIT + 1): tx_i = CTransaction() tx_i.vin = [CTxIn(COutPoint(txid, i), n_sequence=0)] tx_i.vout = [CTxOut(split_value - fee, CScript([b'a']))] tx_i_hex = tx_to_hex(tx_i) self.nodes[0].sendrawtransaction(tx_i_hex, True) # Now create doublespend of the whole lot; should fail. # Need a big enough fee to cover all spending transactions and have # a higher fee rate double_spend_value = (split_value - 100 * fee) * (MAX_REPLACEMENT_LIMIT + 1) inputs = [] for i in range(MAX_REPLACEMENT_LIMIT + 1): inputs.append(CTxIn(COutPoint(txid, i), n_sequence=0)) double_tx = CTransaction() double_tx.vin = inputs double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))] double_tx_hex = tx_to_hex(double_tx) # This will raise an exception assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, double_tx_hex, True) # If we remove an input, it should pass double_tx = CTransaction() double_tx.vin = inputs[0:-1] double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))] double_tx_hex = tx_to_hex(double_tx) self.nodes[0].sendrawtransaction(double_tx_hex, True)
def test_simple_doublespend(self): """Simple doublespend""" tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN)) # make_utxo may have generated a bunch of blocks, so we need to sync # before we can spend the coins generated, or else the resulting # transactions might not be accepted by our peers. self.sync_all() tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, n_sequence=0)] tx1a.vout = [CTxOut(1 * COIN, CScript([b'a']))] tx1a_hex = tx_to_hex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) self.sync_all() # Should fail because we haven't changed the fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, n_sequence=0)] tx1b.vout = [CTxOut(1 * COIN, CScript([b'b']))] tx1b_hex = tx_to_hex(tx1b) # This will raise an exception due to insufficient fee assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) # This will raise an exception due to transaction replacement being disabled assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) # Extra 0.1 ESS fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, n_sequence=0)] tx1b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b']))] tx1b_hex = tx_to_hex(tx1b) # Replacement still disabled even with "enough fee" assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) # Works when enabled tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) mempool = self.nodes[0].getrawmempool() assert (tx1a_txid not in mempool) assert (tx1b_txid in mempool) assert_equal(tx1b_hex, self.nodes[0].getrawtransaction(tx1b_txid)) # Second node is running mempoolreplacement=0, will not replace originally-seen txn mempool = self.nodes[1].getrawmempool() assert tx1a_txid in mempool assert tx1b_txid not in mempool
def run_test(self): self.log.info("Mining blocks...") self.nodes[0].generate(105) self.sync_all() chain_height = self.nodes[1].getblockcount() assert_equal(chain_height, 105) self.log.info("Testing transaction index...") #privkey = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" #address = "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW" addressHash = bytes([11,47,10,12,49,191,224,64,107,12,204,19,129,253,190,49,25,70,218,220]) scriptPubKey = CScript([OP_DUP, OP_HASH160, addressHash, OP_EQUALVERIFY, OP_CHECKSIG]) unspent = self.nodes[0].listunspent() tx = CTransaction() amount = int(unspent[0]["amount"] * 10000000) tx.vin = [CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"]))] tx.vout = [CTxOut(amount, scriptPubKey)] tx.rehash() signed_tx = self.nodes[0].signrawtransaction(binascii.hexlify(tx.serialize()).decode("utf-8")) self.nodes[0].sendrawtransaction(signed_tx["hex"], True) self.nodes[0].generate(1) self.sync_all() # Check verbose raw transaction results verbose = self.nodes[3].getrawtransaction(unspent[0]["txid"], 1) assert_equal(verbose["vout"][0]["valueSat"], 500000000000) assert_equal(verbose["vout"][0]["value"], 5000) self.log.info("All Tests Passed")
def branch(prevout, initial_value, max_txs, tree_width=5, fee_val=0.0001 * COIN, _total_txs=None): if _total_txs is None: _total_txs = [0] if _total_txs[0] >= max_txs: return txout_value = (initial_value - fee_val) // tree_width if txout_value < fee_val: return vout = [CTxOut(txout_value, CScript([i+1])) for i in range(tree_width)] tx_data = CTransaction() tx_data.vin = [CTxIn(prevout, n_sequence=0)] tx_data.vout = vout tx_hex = tx_to_hex(tx_data) assert(len(tx_data.serialize()) < 100000) txid = self.nodes[0].sendrawtransaction(tx_hex, True) yield tx_data _total_txs[0] += 1 txid = int(txid, 16) for i, _ in enumerate(tx_data.vout): for x in branch(COutPoint(txid, i), txout_value, max_txs, tree_width=tree_width, fee_val=fee_val, _total_txs=_total_txs): yield x
def test_spends_of_conflicting_outputs(self): """Replacements that spend conflicting tx outputs are rejected""" utxo1 = make_utxo(self.nodes[0], int(1.2 * COIN)) utxo2 = make_utxo(self.nodes[0], 3 * COIN) tx1a = CTransaction() tx1a.vin = [CTxIn(utxo1, n_sequence=0)] tx1a.vout = [CTxOut(int(1.1 * COIN), CScript([b'a']))] tx1a_hex = tx_to_hex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) tx1a_txid = int(tx1a_txid, 16) # Direct spend an output of the transaction we're replacing. tx2 = CTransaction() tx2.vin = [CTxIn(utxo1, n_sequence=0), CTxIn(utxo2, n_sequence=0)] tx2.vin.append(CTxIn(COutPoint(tx1a_txid, 0), n_sequence=0)) tx2.vout = tx1a.vout tx2_hex = tx_to_hex(tx2) # This will raise an exception assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True) # Spend tx1a's output to test the indirect case. tx1b = CTransaction() tx1b.vin = [CTxIn(COutPoint(tx1a_txid, 0), n_sequence=0)] tx1b.vout = [CTxOut(1 * COIN, CScript([b'a']))] tx1b_hex = tx_to_hex(tx1b) tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) tx1b_txid = int(tx1b_txid, 16) tx2 = CTransaction() tx2.vin = [ CTxIn(utxo1, n_sequence=0), CTxIn(utxo2, n_sequence=0), CTxIn(COutPoint(tx1b_txid, 0)) ] tx2.vout = tx1a.vout tx2_hex = tx_to_hex(tx2) # This will raise an exception assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True)
def test_new_unconfirmed_inputs(self): """Replacements that add new unconfirmed inputs are rejected""" confirmed_utxo = make_utxo(self.nodes[0], int(1.1*COIN)) unconfirmed_utxo = make_utxo(self.nodes[0], int(0.1*COIN), False) tx1 = CTransaction() tx1.vin = [CTxIn(confirmed_utxo)] tx1.vout = [CTxOut(1*COIN, CScript([b'a']))] tx1_hex = tx_to_hex(tx1) self.nodes[0].sendrawtransaction(tx1_hex, True) tx2 = CTransaction() tx2.vin = [CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)] tx2.vout = tx1.vout tx2_hex = tx_to_hex(tx2) # This will raise an exception assert_raises_rpc_error(-26, "replacement-adds-unconfirmed", self.nodes[0].sendrawtransaction, tx2_hex, True)
def test_replacement_fee_per_kb(self): """Replacement requires fee-per-KB to be higher""" tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, n_sequence=0)] tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] tx1a_hex = tx_to_hex(tx1a) self.nodes[0].sendrawtransaction(tx1a_hex, True) # Higher fee, but the fee per KB is much lower, so the replacement is # rejected. tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, n_sequence=0)] tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*999000]))] tx1b_hex = tx_to_hex(tx1b) # This will raise an exception due to insufficient fee assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
def test_doublespend_chain(self): """Doublespend of a long chain""" initial_n_value = 5000 * COIN tx0_outpoint = make_utxo(self.nodes[0], initial_n_value) prevout = tx0_outpoint remaining_value = initial_n_value chain_txids = [] while remaining_value > 1000 * COIN: remaining_value -= 100 * COIN tx = CTransaction() tx.vin = [CTxIn(prevout, n_sequence=0)] tx.vout = [CTxOut(remaining_value, CScript([1]))] tx_hex = tx_to_hex(tx) txid = self.nodes[0].sendrawtransaction(tx_hex, True) chain_txids.append(txid) prevout = COutPoint(int(txid, 16), 0) # Whether the double-spend is allowed is evaluated by including all # child fees - 40 ESS - so this attempt is rejected. dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, n_sequence=0)] dbl_tx.vout = [CTxOut(initial_n_value - 30 * COIN, CScript([1]))] dbl_tx_hex = tx_to_hex(dbl_tx) # This will raise an exception due to insufficient fee assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) # Accepted with sufficient fee dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, n_sequence=0)] dbl_tx.vout = [CTxOut(1 * COIN, CScript([1]))] dbl_tx_hex = tx_to_hex(dbl_tx) self.nodes[0].sendrawtransaction(dbl_tx_hex, True) mempool = self.nodes[0].getrawmempool() for doublespent_txid in chain_txids: assert (doublespent_txid not in mempool)
def SignatureHash(script, txTo, inIdx, hashtype): """Consensus-correct SignatureHash Returns (hash, err) to precisely match the consensus-critical behavior of the SIGHASH_SINGLE bug. (inIdx is *not* checked for validity) """ HASH_ONE = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' if inIdx >= len(txTo.vin): return (HASH_ONE, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin))) txtmp = CTransaction(txTo) for txin in txtmp.vin: txin.scriptSig = b'' txtmp.vin[inIdx].scriptSig = FindAndDelete(script, CScript([OP_CODESEPARATOR])) if (hashtype & 0x1f) == SIGHASH_NONE: txtmp.vout = [] for i in range(len(txtmp.vin)): if i != inIdx: txtmp.vin[i].nSequence = 0 elif (hashtype & 0x1f) == SIGHASH_SINGLE: outIdx = inIdx if outIdx >= len(txtmp.vout): return (HASH_ONE, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout))) tmp = txtmp.vout[outIdx] txtmp.vout = [] for i in range(outIdx): txtmp.vout.append(CTxOut()) txtmp.vout.append(tmp) for i in range(len(txtmp.vin)): if i != inIdx: txtmp.vin[i].nSequence = 0 if hashtype & SIGHASH_ANYONECANPAY: tmp = txtmp.vin[inIdx] txtmp.vin = [] txtmp.vin.append(tmp) s = txtmp.serialize() s += struct.pack(b"<I", hashtype) hash = hash256(s) return (hash, None)
def make_utxo(node, amount, confirmed=True, script_pub_key=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, script_pub_key)] tx2.rehash() signed_tx = node.signrawtransaction(tx_to_hex(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 run_test(self): # Mine blocks to get spendable utxos self.nodes[0].generate(103) # Check that the first node has 3 utxos utxos = self.nodes[0].listunspent() assert_equal(len(utxos), 3) # Compare gettxouts results to results from gettxout RPC function gettxout_results = [] for i in range(len(utxos)): gettxout_results.append(self.nodes[0].gettxout(txid=utxos[i]["txid"], n=utxos[i]["vout"], include_mempool=True)) utxos_list = [] for utxo in utxos: utxos_list.append({"txid": utxo["txid"], "n": utxo["vout"]}) for i in range(len(utxos)): gettxouts_res = self.nodes[0].gettxouts(utxos_list[:i+1], ["*"], True) # compare values for each result for j in range(i+1): assert_equal(gettxouts_res["txouts"][j]["confirmations"], gettxout_results[j]["confirmations"]) assert_equal(gettxouts_res["txouts"][j]["scriptPubKey"], gettxout_results[j]["scriptPubKey"]["hex"]) assert_equal(gettxouts_res["txouts"][j]["scriptPubKeyLen"], len(gettxout_results[j]["scriptPubKey"]["hex"])/2) assert_equal(gettxouts_res["txouts"][j]["value"], gettxout_results[j]["value"]) assert_equal(gettxouts_res["txouts"][j]["isStandard"], True) # all transactions above are standard # Empty list of txid, n pairs should return empty list gettxouts = self.nodes[0].gettxouts([], ["*"], True) assert_equal(len(gettxouts["txouts"]), 0) # Check various combinations of return types gettxouts_res = self.nodes[0].gettxouts([{"txid": utxos[0]["txid"], "n": utxos[0]["vout"]}], ["scriptPubKey"], True) assert_equal(set(gettxouts_res["txouts"][0].keys()), {"scriptPubKey"}) gettxouts_res = self.nodes[0].gettxouts([{"txid": utxos[0]["txid"], "n": utxos[0]["vout"]}], ["scriptPubKey", "value", "confirmations"], True) assert_equal(set(gettxouts_res["txouts"][0].keys()), {"scriptPubKey", "value", "confirmations"}) gettxouts_res = self.nodes[0].gettxouts([{"txid": utxos[0]["txid"], "n": utxos[0]["vout"]}], ["*"], True) assert_equal(set(gettxouts_res["txouts"][0].keys()), {"scriptPubKey", "scriptPubKeyLen", "value", "isStandard", "confirmations"}) assert_raises_rpc_error( -32602, "No return fields set", self.nodes[0].gettxouts, [{"txid": utxos[0]["txid"], "n": utxos[0]["vout"]}], [], True) # TXOs from mempool assert_equal(len(self.nodes[0].getrawmempool()), 0) # Create, sign and send transaction (from utxos[1]) spent_utxo = utxos[1] # utxo that we want to spend inputs = [] outputs = {} inputs.append({"txid": spent_utxo["txid"], "vout": spent_utxo["vout"]}) outputs[self.nodes[0].getnewaddress()] = spent_utxo["amount"] - 3 raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) signed_tx = self.nodes[0].signrawtransaction(raw_tx) self.nodes[0].sendrawtransaction(signed_tx["hex"], True) # new transaction should appear in the mempool assert_equal(len(self.nodes[0].getrawmempool()), 1) new_utxo_txid = self.nodes[0].getrawmempool()[0] spent_utxo_txid = spent_utxo["txid"] # Check if new_utxo_txid which is in mempool is discovered for mempool=True and not for mempool=Flase gettxouts_res = self.nodes[0].gettxouts([{"txid": new_utxo_txid, "n": 0}], ["*"], False) assert_equal(gettxouts_res["txouts"], [{'error': 'missing'}]) gettxouts_res = self.nodes[0].gettxouts([{"txid": new_utxo_txid, "n": 0}], ["*"], True) assert_equal(set(gettxouts_res["txouts"][0].keys()), {"scriptPubKey", "scriptPubKeyLen", "value", "isStandard", "confirmations"}) # Check if spent_utxo_txid which is spent, but transaction that spends it is in mempool (and not in block) gettxouts_res = self.nodes[0].gettxouts([{"txid": spent_utxo_txid, "n": 0}], ["*"], False) assert_equal(set(gettxouts_res["txouts"][0].keys()), {"scriptPubKey", "scriptPubKeyLen", "value", "isStandard", "confirmations"}) gettxouts_res = self.nodes[0].gettxouts([{"txid": spent_utxo_txid, "n": 0}], ["*"], True) assert_equal(gettxouts_res["txouts"][0]["error"], "spent") assert_equal(gettxouts_res["txouts"][0]["collidedWith"]["txid"], new_utxo_txid) # Check for multiple errors (missing, spent) and utxo that we can obtain gettxouts_res = self.nodes[0].gettxouts([{"txid": spent_utxo_txid, "n": 0}, {"txid": "abc", "n": 0}, {"txid": utxos[2]["txid"], "n": utxos[2]["vout"]}], ["*"], True) assert_equal(gettxouts_res["txouts"][0]["error"], "spent") assert_equal(gettxouts_res["txouts"][1]["error"], "missing") assert_equal(gettxouts_res["txouts"][2].keys(), {"scriptPubKey", "scriptPubKeyLen", "value", "isStandard", "confirmations"}) # now generate block - transaction with txid: new_utxo_txid is now in block # it should be returned regardles of include_mempool parameter value self.nodes[0].generate(1) gettxouts_res = self.nodes[0].gettxouts([{"txid": new_utxo_txid, "n": 0}], ["*"], False) assert_equal(set(gettxouts_res["txouts"][0].keys()), {"scriptPubKey", "scriptPubKeyLen", "value", "isStandard", "confirmations"}) gettxouts_res = self.nodes[0].gettxouts([{"txid": new_utxo_txid, "n": 0}], ["*"], True) assert_equal(set(gettxouts_res["txouts"][0].keys()), {"scriptPubKey", "scriptPubKeyLen", "value", "isStandard", "confirmations"}) # It should not be found after it is spent gettxouts_res = self.nodes[0].gettxouts([{"txid": spent_utxo_txid, "n": 0}], ["*"], False) assert_equal(gettxouts_res["txouts"], [{'error': 'missing'}]) gettxouts_res = self.nodes[0].gettxouts([{"txid": spent_utxo_txid, "n": 0}], ["*"], True) assert_equal(gettxouts_res["txouts"], [{'error': 'missing'}]) # Invalid TXOs (incorrect syntax on input) assert_raises_rpc_error( -32602, "Wrong format. Exactly \"txid\" and \"n\" are required fields.", self.nodes[0].gettxouts, [{"abc": utxos[0]["txid"], "n": utxos[0]["vout"]}], ["*"], True) assert_raises_rpc_error( -32602, "Wrong format. Exactly \"txid\" and \"n\" are required fields.", self.nodes[0].gettxouts, [{"txid": utxos[0]["txid"], "abc": utxos[0]["vout"]}], ["*"], True) assert_raises_rpc_error( -32602, "Wrong format. Exactly \"txid\" and \"n\" are required fields.", self.nodes[0].gettxouts, [{"txid": utxos[0]["txid"]}], ["*"], True) assert_raises_rpc_error( -32602, "Wrong format. Exactly \"txid\" and \"n\" are required fields.", self.nodes[0].gettxouts, [{"n": utxos[0]["vout"]}], ["*"], True) assert_raises_rpc_error( -32602, "Wrong format. Exactly \"txid\" and \"n\" are required fields.", self.nodes[0].gettxouts, [{utxos[0]["txid"]: utxos[0]["vout"]}], ["*"], True) assert_raises_rpc_error( -32602, "Wrong format. Exactly \"txid\" and \"n\" are required fields.", self.nodes[0].gettxouts, [{}], ["*"], True) assert_raises_rpc_error( -32602, "\"*\" should not be used with other return fields", self.nodes[0].gettxouts, [{"abc": utxos[0]["txid"], "n": utxos[0]["vout"]}], ["*", "value"], True) assert_raises_rpc_error( -1, "JSON value is not an object as expected", self.nodes[0].gettxouts, [utxos[0]["txid"]], ["*"], True) assert_raises_rpc_error( -1, "JSON value is not an object as expected", self.nodes[0].gettxouts, [0], ["*"], True) assert_raises_rpc_error( -1, "JSON value is not an object as expected", self.nodes[0].gettxouts, [None], ["*"], True) # Missing/non-existing TXOs gettxouts_res = self.nodes[0].gettxouts([{"txid": "abc", "n": utxos[0]["vout"]}], ["*"], True) assert_equal(gettxouts_res["txouts"], [{'error': 'missing'}]) gettxouts_res = self.nodes[0].gettxouts([{"txid": utxos[0]["txid"], "n": len(utxos[0]) + 1}], ["*"], True) assert_equal(gettxouts_res["txouts"], [{'error': 'missing'}]) # Check with non hex string for txid gettxouts_res = self.nodes[0].gettxouts([{"txid": "z._", "n": utxos[0]["vout"]}], ["*"], True) assert_equal(gettxouts_res["txouts"], [{'error': 'missing'}]) # check non standard transaction utxo = self.nodes[0].listunspent()[0] tx1 = CTransaction() tx1.vout = [CTxOut(450000, CScript([OP_TRUE]))] # This is not a standard transactions tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), 0))] tx_hex = self.nodes[0].signrawtransaction(ToHex(tx1))['hex'] self.nodes[0].sendrawtransaction(tx_hex, True) assert_equal(len(self.nodes[0].getrawmempool()), 1) new_tx = self.nodes[0].getrawmempool()[0] gettxouts_res = self.nodes[0].gettxouts([{"txid": new_tx, "n": 0}], ["*"], True) assert_equal(gettxouts_res["txouts"][0]["isStandard"], False)
def SignatureHash(script, txTo, inIdx, hashtype, amount, consensusBranchId): """Consensus-correct SignatureHash""" if inIdx >= len(txTo.vin): raise ValueError("inIdx %d out of range (%d)" % (inIdx, len(txTo.vin))) if consensusBranchId != 0: # ZIP 243 hashPrevouts = b'\x00' * 32 hashSequence = b'\x00' * 32 hashOutputs = b'\x00' * 32 hashJoinSplits = b'\x00' * 32 hashShieldedSpends = b'\x00' * 32 hashShieldedOutputs = b'\x00' * 32 if not (hashtype & SIGHASH_ANYONECANPAY): hashPrevouts = getHashPrevouts(txTo) if (not (hashtype & SIGHASH_ANYONECANPAY)) and \ (hashtype & 0x1f) != SIGHASH_SINGLE and \ (hashtype & 0x1f) != SIGHASH_NONE: hashSequence = getHashSequence(txTo) if (hashtype & 0x1f) != SIGHASH_SINGLE and \ (hashtype & 0x1f) != SIGHASH_NONE: hashOutputs = getHashOutputs(txTo) elif (hashtype & 0x1f) == SIGHASH_SINGLE and \ 0 <= inIdx and inIdx < len(txTo.vout): digest = blake2b(digest_size=32, person=b'ZcashOutputsHash') digest.update(txTo.vout[inIdx].serialize()) hashOutputs = digest.digest() if len(txTo.vJoinSplit) > 0: hashJoinSplits = getHashJoinSplits(txTo) if len(txTo.shieldedSpends) > 0: hashShieldedSpends = getHashShieldedSpends(txTo) if len(txTo.shieldedOutputs) > 0: hashShieldedOutputs = getHashShieldedOutputs(txTo) digest = blake2b( digest_size=32, person=b'ZcashSigHash' + struct.pack('<I', consensusBranchId), ) digest.update( struct.pack('<I', (int(txTo.fOverwintered) << 31) | txTo.nVersion)) digest.update(struct.pack('<I', txTo.nVersionGroupId)) digest.update(hashPrevouts) digest.update(hashSequence) digest.update(hashOutputs) digest.update(hashJoinSplits) digest.update(hashShieldedSpends) digest.update(hashShieldedOutputs) digest.update(struct.pack('<I', txTo.nLockTime)) digest.update(struct.pack('<I', txTo.nExpiryHeight)) digest.update(struct.pack('<Q', txTo.valueBalance)) digest.update(struct.pack('<I', hashtype)) if inIdx is not None: digest.update(txTo.vin[inIdx].prevout.serialize()) digest.update(ser_string(script)) digest.update(struct.pack('<Q', amount)) digest.update(struct.pack('<I', txTo.vin[inIdx].nSequence)) return (digest.digest(), None) else: # Pre-Overwinter txtmp = CTransaction(txTo) for txin in txtmp.vin: txin.scriptSig = b'' txtmp.vin[inIdx].scriptSig = script if (hashtype & 0x1f) == SIGHASH_NONE: txtmp.vout = [] for i in range(len(txtmp.vin)): if i != inIdx: txtmp.vin[i].nSequence = 0 elif (hashtype & 0x1f) == SIGHASH_SINGLE: outIdx = inIdx if outIdx >= len(txtmp.vout): raise ValueError("outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout))) tmp = txtmp.vout[outIdx] txtmp.vout = [] for i in range(outIdx): txtmp.vout.append(CTxOut()) txtmp.vout.append(tmp) for i in range(len(txtmp.vin)): if i != inIdx: txtmp.vin[i].nSequence = 0 if hashtype & SIGHASH_ANYONECANPAY: tmp = txtmp.vin[inIdx] txtmp.vin = [] txtmp.vin.append(tmp) s = txtmp.serialize() s += struct.pack(b"<I", hashtype) hash = hash256(s) return (hash, None)
def run_test(self): # helper functions def getaddresstxids(node_index, addresses, start, end): return self.nodes[node_index].getaddresstxids({ 'addresses': addresses, 'start': start, 'end': end }) def getaddressdeltas(node_index, addresses, start, end, chainInfo=None): params = { 'addresses': addresses, 'start': start, 'end': end, } if chainInfo is not None: params.update({'chainInfo': chainInfo}) return self.nodes[node_index].getaddressdeltas(params) # default received value is the balance value def check_balance(node_index, address, expected_balance, expected_received=None): if isinstance(address, list): bal = self.nodes[node_index].getaddressbalance( {'addresses': address}) else: bal = self.nodes[node_index].getaddressbalance(address) assert_equal(bal['balance'], expected_balance) if expected_received is None: expected_received = expected_balance assert_equal(bal['received'], expected_received) # begin test self.nodes[0].generate(105) self.sync_all() assert_equal(self.nodes[0].getbalance(), 5 * 10) assert_equal(self.nodes[1].getblockcount(), 105) assert_equal(self.nodes[1].getbalance(), 0) # only the oldest 5; subsequent are not yet mature unspent_txids = [u['txid'] for u in self.nodes[0].listunspent()] # Currently our only unspents are coinbase transactions, choose any one tx = self.nodes[0].getrawtransaction(unspent_txids[0], 1) # It just so happens that the first output is the mining reward, # which has type pay-to-public-key-hash, and the second output # is the founders' reward, which has type pay-to-script-hash. addr_p2pkh = tx['vout'][0]['scriptPubKey']['addresses'][0] addr_p2sh = tx['vout'][1]['scriptPubKey']['addresses'][0] # Check that balances from mining are correct (105 blocks mined); in # regtest, all mining rewards from a single call to generate() are sent # to the same pair of addresses. check_balance(1, addr_p2pkh, 105 * 10 * COIN) check_balance(1, addr_p2sh, 105 * 2.5 * COIN) # Multiple address arguments, results are the sum check_balance(1, [addr_p2sh, addr_p2pkh], 105 * 12.5 * COIN) assert_equal(len(self.nodes[1].getaddresstxids(addr_p2pkh)), 105) assert_equal(len(self.nodes[1].getaddresstxids(addr_p2sh)), 105) # test getaddresstxids for lightwalletd assert_equal(len(self.nodes[3].getaddresstxids(addr_p2pkh)), 105) assert_equal(len(self.nodes[3].getaddresstxids(addr_p2sh)), 105) # only the oldest 5 transactions are in the unspent list, # dup addresses are ignored height_txids = getaddresstxids(1, [addr_p2pkh, addr_p2pkh], 1, 5) assert_equal(sorted(height_txids), sorted(unspent_txids)) height_txids = getaddresstxids(1, [addr_p2sh], 1, 5) assert_equal(sorted(height_txids), sorted(unspent_txids)) # each txid should appear only once height_txids = getaddresstxids(1, [addr_p2pkh, addr_p2sh], 1, 5) assert_equal(sorted(height_txids), sorted(unspent_txids)) # do some transfers, make sure balances are good txids_a1 = [] addr1 = self.nodes[1].getnewaddress() expected = 0 expected_deltas = [] # for checking getaddressdeltas (below) for i in range(5): # first transaction happens at height 105, mined in block 106 txid = self.nodes[0].sendtoaddress(addr1, i + 1) txids_a1.append(txid) self.nodes[0].generate(1) self.sync_all() expected += i + 1 expected_deltas.append({ 'height': 106 + i, 'satoshis': (i + 1) * COIN, 'txid': txid, }) check_balance(1, addr1, expected * COIN) assert_equal(sorted(self.nodes[0].getaddresstxids(addr1)), sorted(txids_a1)) assert_equal(sorted(self.nodes[1].getaddresstxids(addr1)), sorted(txids_a1)) # Restart all nodes to ensure indices are saved to disk and recovered stop_nodes(self.nodes) wait_bitcoinds() self.setup_network() bal = self.nodes[1].getaddressbalance(addr1) assert_equal(bal['balance'], expected * COIN) assert_equal(bal['received'], expected * COIN) assert_equal(sorted(self.nodes[0].getaddresstxids(addr1)), sorted(txids_a1)) assert_equal(sorted(self.nodes[1].getaddresstxids(addr1)), sorted(txids_a1)) # Send 3 from addr1, but -- subtlety alert! -- addr1 at this # time has 4 UTXOs, with values 1, 2, 3, 4. Sending value 3 requires # using up the value 4 UTXO, because of the tx fee # (the 3 UTXO isn't quite large enough). # # The txid from sending *from* addr1 is also added to the list of # txids associated with that address (test will verify below). addr2 = self.nodes[2].getnewaddress() txid = self.nodes[1].sendtoaddress(addr2, 3) self.sync_all() # the one tx in the mempool refers to addresses addr1 and addr2, # check that duplicate addresses are processed correctly mempool = self.nodes[0].getaddressmempool( {'addresses': [addr2, addr1, addr2]}) assert_equal(len(mempool), 3) # test getaddressmempool for lightwalletd node mempool = self.nodes[3].getaddressmempool( {'addresses': [addr2, addr1, addr2]}) assert_equal(len(mempool), 3) # addr2 (first arg) assert_equal(mempool[0]['address'], addr2) assert_equal(mempool[0]['satoshis'], 3 * COIN) assert_equal(mempool[0]['txid'], txid) # addr1 (second arg) assert_equal(mempool[1]['address'], addr1) assert_equal(mempool[1]['satoshis'], (-4) * COIN) assert_equal(mempool[1]['txid'], txid) # addr2 (third arg) assert_equal(mempool[2]['address'], addr2) assert_equal(mempool[2]['satoshis'], 3 * COIN) assert_equal(mempool[2]['txid'], txid) # a single address can be specified as a string (not json object) addr1_mempool = self.nodes[0].getaddressmempool(addr1) assert_equal(len(addr1_mempool), 1) # Don't check the timestamp; it's local to the node, and can mismatch # due to propagation delay. del addr1_mempool[0]['timestamp'] for key in addr1_mempool[0].keys(): assert_equal(mempool[1][key], addr1_mempool[0][key]) tx = self.nodes[0].getrawtransaction(txid, 1) assert_equal(tx['vin'][0]['address'], addr1) assert_equal(tx['vin'][0]['value'], 4) assert_equal(tx['vin'][0]['valueSat'], 4 * COIN) txids_a1.append(txid) expected_deltas.append({ 'height': 111, 'satoshis': (-4) * COIN, 'txid': txid, }) self.sync_all() # ensure transaction is included in the next block self.nodes[0].generate(1) self.sync_all() # the send to addr2 tx is now in a mined block, no longer in the mempool mempool = self.nodes[0].getaddressmempool( {'addresses': [addr2, addr1]}) assert_equal(len(mempool), 0) # Test DisconnectBlock() by invalidating the most recent mined block tip = self.nodes[1].getchaintips()[0] for i in range(self.num_nodes): node = self.nodes[i] # the value 4 UTXO is no longer in our balance check_balance(i, addr1, (expected - 4) * COIN, expected * COIN) check_balance(i, addr2, 3 * COIN) assert_equal(node.getblockcount(), 111) node.invalidateblock(tip['hash']) assert_equal(node.getblockcount(), 110) mempool = node.getaddressmempool({'addresses': [addr2, addr1]}) assert_equal(len(mempool), 2) check_balance(i, addr1, expected * COIN) check_balance(i, addr2, 0) # now re-mine the addr1 to addr2 send self.nodes[0].generate(1) self.sync_all() for node in self.nodes: assert_equal(node.getblockcount(), 111) mempool = self.nodes[0].getaddressmempool( {'addresses': [addr2, addr1]}) assert_equal(len(mempool), 0) # the value 4 UTXO is no longer in our balance check_balance(2, addr1, (expected - 4) * COIN, expected * COIN) # Ensure the change from that transaction appears tx = self.nodes[0].getrawtransaction(txid, 1) change_vout = list( filter(lambda v: v['valueZat'] != 3 * COIN, tx['vout'])) change = change_vout[0]['scriptPubKey']['addresses'][0] # test getaddressbalance for node in (2, 3): bal = self.nodes[node].getaddressbalance(change) assert (bal['received'] > 0) # the inequality is due to randomness in the tx fee assert (bal['received'] < (4 - 3) * COIN) assert_equal(bal['received'], bal['balance']) assert_equal(self.nodes[2].getaddresstxids(change), [txid]) # Further checks that limiting by height works # various ranges for i in range(5): height_txids = getaddresstxids(1, [addr1], 106, 106 + i) assert_equal(height_txids, txids_a1[0:i + 1]) height_txids = getaddresstxids(1, [addr1], 1, 108) assert_equal(height_txids, txids_a1[0:3]) # Further check specifying multiple addresses txids_all = list(txids_a1) txids_all += self.nodes[1].getaddresstxids(addr_p2pkh) txids_all += self.nodes[1].getaddresstxids(addr_p2sh) multitxids = self.nodes[1].getaddresstxids( {'addresses': [addr1, addr_p2sh, addr_p2pkh]}) # No dups in return list from getaddresstxids assert_equal(len(multitxids), len(set(multitxids))) # set(txids_all) removes its (expected) duplicates assert_equal(set(multitxids), set(txids_all)) # test getaddressdeltas for node in (1, 3): deltas = self.nodes[node].getaddressdeltas({'addresses': [addr1]}) assert_equal(len(deltas), len(expected_deltas)) for i in range(len(deltas)): assert_equal(deltas[i]['address'], addr1) assert_equal(deltas[i]['height'], expected_deltas[i]['height']) assert_equal(deltas[i]['satoshis'], expected_deltas[i]['satoshis']) assert_equal(deltas[i]['txid'], expected_deltas[i]['txid']) # 106-111 is the full range (also the default) deltas_limited = getaddressdeltas(1, [addr1], 106, 111) assert_equal(deltas_limited, deltas) # only the first element missing deltas_limited = getaddressdeltas(1, [addr1], 107, 111) assert_equal(deltas_limited, deltas[1:]) deltas_limited = getaddressdeltas(1, [addr1], 109, 109) assert_equal(deltas_limited, deltas[3:4]) # the full range (also the default) deltas_info = getaddressdeltas(1, [addr1], 106, 111, chainInfo=True) assert_equal(deltas_info['deltas'], deltas) # check the additional items returned by chainInfo assert_equal(deltas_info['start']['height'], 106) block_hash = self.nodes[1].getblockhash(106) assert_equal(deltas_info['start']['hash'], block_hash) assert_equal(deltas_info['end']['height'], 111) block_hash = self.nodes[1].getblockhash(111) assert_equal(deltas_info['end']['hash'], block_hash) # Test getaddressutxos by comparing results with deltas utxos = self.nodes[3].getaddressutxos(addr1) # The value 4 note was spent, so won't show up in the utxo list, # so for comparison, remove the 4 (and -4 for output) from the # deltas list deltas = self.nodes[1].getaddressdeltas({'addresses': [addr1]}) deltas = list(filter(lambda d: abs(d['satoshis']) != 4 * COIN, deltas)) assert_equal(len(utxos), len(deltas)) for i in range(len(utxos)): assert_equal(utxos[i]['address'], addr1) assert_equal(utxos[i]['height'], deltas[i]['height']) assert_equal(utxos[i]['satoshis'], deltas[i]['satoshis']) assert_equal(utxos[i]['txid'], deltas[i]['txid']) # Check that outputs with the same address in the same tx return one txid # (can't use createrawtransaction() as it combines duplicate addresses) addr = "t2LMJ6Arw9UWBMWvfUr2QLHM4Xd9w53FftS" addressHash = unhexlify("97643ce74b188f4fb6bbbb285e067a969041caf2") scriptPubKey = CScript([OP_HASH160, addressHash, OP_EQUAL]) # Add an unrecognized script type to vout[], a legal script that pays, # but won't modify the addressindex (since the address can't be extracted). # (This extra output has no effect on the rest of the test.) scriptUnknown = CScript( [OP_HASH160, OP_DUP, OP_DROP, addressHash, OP_EQUAL]) unspent = list( filter(lambda u: u['amount'] >= 4, self.nodes[0].listunspent())) tx = CTransaction() tx.vin = [ CTxIn(COutPoint(int(unspent[0]['txid'], 16), unspent[0]['vout'])) ] tx.vout = [ CTxOut(1 * COIN, scriptPubKey), CTxOut(2 * COIN, scriptPubKey), CTxOut(7 * COIN, scriptUnknown), ] tx = self.nodes[0].signrawtransaction( hexlify(tx.serialize()).decode('utf-8')) txid = self.nodes[0].sendrawtransaction(tx['hex'], True) self.nodes[0].generate(1) self.sync_all() assert_equal(self.nodes[1].getaddresstxids(addr), [txid]) check_balance(2, addr, 3 * COIN)
def run_test(self): self.log.info("Mining blocks...") self.nodes[0].generate(105) self.sync_all() chain_height = self.nodes[1].getblockcount() assert_equal(chain_height, 105) assert_equal(self.nodes[1].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 0) # Check that balances are correct balance0 = self.nodes[1].getaddressbalance( "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") assert_equal(balance0["balance"], 0) # Check p2pkh and p2sh address indexes self.log.info("Testing p2pkh and p2sh address index...") tx_id0 = self.nodes[0].sendtoaddress( "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", 10) self.nodes[0].generate(1) tx_idb0 = self.nodes[0].sendtoaddress( "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", 10) self.nodes[0].generate(1) tx_id1 = self.nodes[0].sendtoaddress( "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", 15) self.nodes[0].generate(1) tx_idb1 = self.nodes[0].sendtoaddress( "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", 15) self.nodes[0].generate(1) tx_id2 = self.nodes[0].sendtoaddress( "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", 20) self.nodes[0].generate(1) tx_idb2 = self.nodes[0].sendtoaddress( "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", 20) self.nodes[0].generate(1) self.sync_all() txids = self.nodes[1].getaddresstxids( "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs") assert_equal(len(txids), 3) assert_equal(txids[0], tx_id0) assert_equal(txids[1], tx_id1) assert_equal(txids[2], tx_id2) tx_idsb = self.nodes[1].getaddresstxids( "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") assert_equal(len(tx_idsb), 3) assert_equal(tx_idsb[0], tx_idb0) assert_equal(tx_idsb[1], tx_idb1) assert_equal(tx_idsb[2], tx_idb2) # Check that limiting by height works self.log.info("Testing querying txids by range of block heights..") height_txids = self.nodes[1].getaddresstxids({ "addresses": ["2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br"], "start": 105, "end": 110 }) assert_equal(len(height_txids), 2) assert_equal(height_txids[0], tx_idb0) assert_equal(height_txids[1], tx_idb1) # Check that multiple addresses works multi_tx_ids = self.nodes[1].getaddresstxids({ "addresses": [ "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs" ] }) assert_equal(len(multi_tx_ids), 6) assert_equal(multi_tx_ids[0], tx_id0) assert_equal(multi_tx_ids[1], tx_idb0) assert_equal(multi_tx_ids[2], tx_id1) assert_equal(multi_tx_ids[3], tx_idb1) assert_equal(multi_tx_ids[4], tx_id2) assert_equal(multi_tx_ids[5], tx_idb2) # Check that balances are correct balance0 = self.nodes[1].getaddressbalance( "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") assert_equal(balance0["balance"], 45 * 100000000) # Check that outputs with the same address will only return one txid self.log.info("Testing for txid uniqueness...") address_hash = bytes([ 99, 73, 164, 24, 252, 69, 120, 209, 10, 55, 43, 84, 180, 92, 40, 12, 200, 196, 56, 47 ]) script_pub_key = CScript([OP_HASH160, address_hash, OP_EQUAL]) unspent = self.nodes[0].listunspent() tx = CTransaction() tx.vin = [ CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"])) ] tx.vout = [CTxOut(10, script_pub_key), CTxOut(11, script_pub_key)] tx.rehash() signed_tx = self.nodes[0].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) sent_txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) self.nodes[0].generate(1) self.sync_all() tx_ids_many = self.nodes[1].getaddresstxids( "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") assert_equal(len(tx_ids_many), 4) assert_equal(tx_ids_many[3], sent_txid) # Check that balances are correct self.log.info("Testing balances...") balance0 = self.nodes[1].getaddressbalance( "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br") assert_equal(balance0["balance"], 45 * 100000000 + 21) # Check that balances are correct after spending self.log.info("Testing balances after spending...") privkey2 = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" address2 = "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW" address_hash2 = bytes([ 11, 47, 10, 12, 49, 191, 224, 64, 107, 12, 204, 19, 129, 253, 190, 49, 25, 70, 218, 220 ]) script_pub_key2 = CScript( [OP_DUP, OP_HASH160, address_hash2, OP_EQUALVERIFY, OP_CHECKSIG]) self.nodes[0].importprivkey(privkey2) unspent = self.nodes[0].listunspent() tx = CTransaction() tx.vin = [ CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"])) ] amount = int(unspent[0]["amount"] * 100000000 - 230000) tx.vout = [CTxOut(amount, script_pub_key2)] signed_tx = self.nodes[0].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) spending_txid = self.nodes[0].sendrawtransaction( signed_tx["hex"], True) self.nodes[0].generate(1) self.sync_all() balance1 = self.nodes[1].getaddressbalance(address2) assert_equal(balance1["balance"], amount) tx = CTransaction() tx.vin = [CTxIn(COutPoint(int(spending_txid, 16), 0))] send_amount = 1 * 100000000 + 12840 change_amount = amount - send_amount - 230000 tx.vout = [ CTxOut(change_amount, script_pub_key2), CTxOut(send_amount, script_pub_key) ] tx.rehash() signed_tx = self.nodes[0].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) self.nodes[0].sendrawtransaction(signed_tx["hex"], True) self.nodes[0].generate(1) self.sync_all() balance2 = self.nodes[1].getaddressbalance(address2) assert_equal(balance2["balance"], change_amount) # Check that deltas are returned correctly deltas = self.nodes[1].getaddressdeltas({ "addresses": [address2], "start": 1, "end": 200 }) balance3 = 0 for delta in deltas: balance3 += delta["satoshis"] assert_equal(balance3, change_amount) assert_equal(deltas[0]["address"], address2) assert_equal(deltas[0]["blockindex"], 1) # Check that entire range will be queried deltas_all = self.nodes[1].getaddressdeltas({"addresses": [address2]}) assert_equal(len(deltas_all), len(deltas)) # Check that deltas can be returned from range of block heights deltas = self.nodes[1].getaddressdeltas({ "addresses": [address2], "start": 113, "end": 113 }) assert_equal(len(deltas), 1) # Check that unspent outputs can be queried self.log.info("Testing utxos...") utxos = self.nodes[1].getaddressutxos({"addresses": [address2]}) assert_equal(len(utxos), 1) assert_equal(utxos[0]["satoshis"], change_amount) # Check that indexes will be updated with a reorg self.log.info("Testing reorg...") best_hash = self.nodes[0].getbestblockhash() self.nodes[0].invalidateblock(best_hash) self.nodes[1].invalidateblock(best_hash) self.nodes[2].invalidateblock(best_hash) self.nodes[3].invalidateblock(best_hash) self.sync_all() balance4 = self.nodes[1].getaddressbalance(address2) assert_equal(balance4, balance1) utxos2 = self.nodes[1].getaddressutxos({"addresses": [address2]}) assert_equal(len(utxos2), 1) assert_equal(utxos2[0]["satoshis"], amount) # Check sorting of utxos self.nodes[2].generate(150) self.nodes[2].sendtoaddress(address2, 50) self.nodes[2].generate(1) self.nodes[2].sendtoaddress(address2, 50) self.nodes[2].generate(1) self.sync_all() utxos3 = self.nodes[1].getaddressutxos({"addresses": [address2]}) assert_equal(len(utxos3), 3) assert_equal(utxos3[0]["height"], 114) assert_equal(utxos3[1]["height"], 264) assert_equal(utxos3[2]["height"], 265) # Check mempool indexing self.log.info("Testing mempool indexing...") priv_key3 = "cVfUn53hAbRrDEuMexyfgDpZPhF7KqXpS8UZevsyTDaugB7HZ3CD" address3 = "mw4ynwhS7MmrQ27hr82kgqu7zryNDK26JB" address_hash3 = bytes([ 170, 152, 114, 181, 187, 205, 181, 17, 216, 158, 14, 17, 170, 39, 218, 115, 253, 44, 63, 80 ]) script_pub_key3 = CScript( [OP_DUP, OP_HASH160, address_hash3, OP_EQUALVERIFY, OP_CHECKSIG]) #address4 = "2N8oFVB2vThAKury4vnLquW2zVjsYjjAkYQ" script_pub_key4 = CScript([OP_HASH160, address_hash3, OP_EQUAL]) unspent = self.nodes[2].listunspent() tx = CTransaction() tx.vin = [ CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"])) ] amount = int(unspent[0]["amount"] * 100000000 - 230000) tx.vout = [CTxOut(amount, script_pub_key3)] tx.rehash() signed_tx = self.nodes[2].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) mem_txid1 = self.nodes[2].sendrawtransaction(signed_tx["hex"], True) time.sleep(2) tx2 = CTransaction() tx2.vin = [ CTxIn(COutPoint(int(unspent[1]["txid"], 16), unspent[1]["vout"])) ] amount = int(unspent[1]["amount"] * 100000000 - 300000) tx2.vout = [ CTxOut(int(amount / 4), script_pub_key3), CTxOut(int(amount / 4), script_pub_key3), CTxOut(int(amount / 4), script_pub_key4), CTxOut(int(amount / 4), script_pub_key4) ] tx2.rehash() signed_tx2 = self.nodes[2].signrawtransaction( binascii.hexlify(tx2.serialize()).decode("utf-8")) mem_txid2 = self.nodes[2].sendrawtransaction(signed_tx2["hex"], True) time.sleep(2) mempool = self.nodes[2].getaddressmempool({"addresses": [address3]}) assert_equal(len(mempool), 3) assert_equal(mempool[0]["txid"], mem_txid1) assert_equal(mempool[0]["address"], address3) assert_equal(mempool[0]["index"], 0) assert_equal(mempool[1]["txid"], mem_txid2) assert_equal(mempool[1]["index"], 0) assert_equal(mempool[2]["txid"], mem_txid2) assert_equal(mempool[2]["index"], 1) self.nodes[2].generate(1) self.sync_all() mempool2 = self.nodes[2].getaddressmempool({"addresses": [address3]}) assert_equal(len(mempool2), 0) tx = CTransaction() tx.vin = [ CTxIn(COutPoint(int(mem_txid2, 16), 0)), CTxIn(COutPoint(int(mem_txid2, 16), 1)) ] tx.vout = [CTxOut(int(amount / 2 - 340000), script_pub_key2)] tx.rehash() self.nodes[2].importprivkey(priv_key3) signed_tx3 = self.nodes[2].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) self.nodes[2].sendrawtransaction(signed_tx3["hex"], True) time.sleep(2) mempool3 = self.nodes[2].getaddressmempool({"addresses": [address3]}) assert_equal(len(mempool3), 2) assert_equal(mempool3[0]["prevtxid"], mem_txid2) assert_equal(mempool3[0]["prevout"], 0) assert_equal(mempool3[1]["prevtxid"], mem_txid2) assert_equal(mempool3[1]["prevout"], 1) # sending and receiving to the same address privkey1 = "cQY2s58LhzUCmEXN8jtAp1Etnijx78YRZ466w4ikX1V4UpTpbsf8" address1 = "myAUWSHnwsQrhuMWv4Br6QsCnpB41vFwHn" address1hash = bytes([ 193, 146, 191, 247, 81, 175, 142, 254, 193, 81, 53, 212, 43, 254, 237, 249, 26, 111, 62, 52 ]) address1script = CScript( [OP_DUP, OP_HASH160, address1hash, OP_EQUALVERIFY, OP_CHECKSIG]) self.nodes[0].sendtoaddress(address1, 10) self.nodes[0].generate(1) self.sync_all() utxos = self.nodes[1].getaddressutxos({"addresses": [address1]}) assert_equal(len(utxos), 1) tx = CTransaction() tx.vin = [ CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["outputIndex"])) ] amount = int(utxos[0]["satoshis"] - 200000) tx.vout = [CTxOut(amount, address1script)] tx.rehash() self.nodes[0].importprivkey(privkey1) signed_tx = self.nodes[0].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) self.nodes[0].sendrawtransaction(signed_tx["hex"], True) self.sync_all() mempool_deltas = self.nodes[2].getaddressmempool( {"addresses": [address1]}) assert_equal(len(mempool_deltas), 2) # Include chaininfo in results self.log.info("Testing results with chain info...") deltas_with_info = self.nodes[1].getaddressdeltas({ "addresses": [address2], "start": 1, "end": 200, "chainInfo": True }) start_block_hash = self.nodes[1].getblockhash(1) end_block_hash = self.nodes[1].getblockhash(200) assert_equal(deltas_with_info["start"]["height"], 1) assert_equal(deltas_with_info["start"]["hash"], start_block_hash) assert_equal(deltas_with_info["end"]["height"], 200) assert_equal(deltas_with_info["end"]["hash"], end_block_hash) utxos_with_info = self.nodes[1].getaddressutxos({ "addresses": [address2], "chainInfo": True }) expected_tip_block_hash = self.nodes[1].getblockhash(267) assert_equal(utxos_with_info["height"], 267) assert_equal(utxos_with_info["hash"], expected_tip_block_hash) self.log.info("All Tests Passed")
def run_test(self): self.log.info("Mining blocks...") self.nodes[0].generate(105) self.sync_all() chain_height = self.nodes[1].getblockcount() assert_equal(chain_height, 105) # Check that self.log.info("Testing spent index...") fee_satoshis = 192000 privkey = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" #address = "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW" address_hash = bytes([ 11, 47, 10, 12, 49, 191, 224, 64, 107, 12, 204, 19, 129, 253, 190, 49, 25, 70, 218, 220 ]) script_pub_key = CScript( [OP_DUP, OP_HASH160, address_hash, OP_EQUALVERIFY, OP_CHECKSIG]) unspent = self.nodes[0].listunspent() tx = CTransaction() amount = int(unspent[0]["amount"] * 100000000 - fee_satoshis) tx.vin = [ CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"])) ] tx.vout = [CTxOut(amount, script_pub_key)] tx.rehash() signed_tx = self.nodes[0].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) self.nodes[0].generate(1) self.sync_all() self.log.info("Testing getspentinfo method...") # Check that the spentinfo works standalone info = self.nodes[1].getspentinfo({ "txid": unspent[0]["txid"], "index": unspent[0]["vout"] }) assert_equal(info["txid"], txid) assert_equal(info["index"], 0) assert_equal(info["height"], 106) self.log.info("Testing getrawtransaction method...") # Check that verbose raw transaction includes spent info tx_verbose = self.nodes[3].getrawtransaction(unspent[0]["txid"], 1) assert_equal(tx_verbose["vout"][unspent[0]["vout"]]["spentTxId"], txid) assert_equal(tx_verbose["vout"][unspent[0]["vout"]]["spentIndex"], 0) assert_equal(tx_verbose["vout"][unspent[0]["vout"]]["spentHeight"], 106) # Check that verbose raw transaction includes input values tx_verbose2 = self.nodes[3].getrawtransaction(txid, 1) assert_equal(float(tx_verbose2["vin"][0]["value"]), (amount + fee_satoshis) / 100000000) assert_equal(tx_verbose2["vin"][0]["valueSat"], amount + fee_satoshis) # Check that verbose raw transaction includes address values and input values #privkey2 = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" address2 = "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW" address_hash2 = bytes([ 11, 47, 10, 12, 49, 191, 224, 64, 107, 12, 204, 19, 129, 253, 190, 49, 25, 70, 218, 220 ]) script_pub_key2 = CScript( [OP_DUP, OP_HASH160, address_hash2, OP_EQUALVERIFY, OP_CHECKSIG]) tx2 = CTransaction() tx2.vin = [CTxIn(COutPoint(int(txid, 16), 0))] amount = int(amount - fee_satoshis) tx2.vout = [CTxOut(amount, script_pub_key2)] tx.rehash() self.nodes[0].importprivkey(privkey) signed_tx2 = self.nodes[0].signrawtransaction( binascii.hexlify(tx2.serialize()).decode("utf-8")) txid2 = self.nodes[0].sendrawtransaction(signed_tx2["hex"], True) # Check the mempool index self.sync_all() tx_verbose3 = self.nodes[1].getrawtransaction(txid2, 1) assert_equal(tx_verbose3["vin"][0]["address"], address2) assert_equal(tx_verbose3["vin"][0]["valueSat"], amount + fee_satoshis) assert_equal(float(tx_verbose3["vin"][0]["value"]), (amount + fee_satoshis) / 100000000) # Check the database index block_hash = self.nodes[0].generate(1) self.sync_all() tx_verbose4 = self.nodes[3].getrawtransaction(txid2, 1) assert_equal(tx_verbose4["vin"][0]["address"], address2) assert_equal(tx_verbose4["vin"][0]["valueSat"], amount + fee_satoshis) assert_equal(float(tx_verbose4["vin"][0]["value"]), (amount + fee_satoshis) / 100000000) # Check block deltas self.log.info("Testing getblockdeltas...") block = self.nodes[3].getblockdeltas(block_hash[0]) assert_equal(len(block["deltas"]), 2) assert_equal(block["deltas"][0]["index"], 0) assert_equal(len(block["deltas"][0]["inputs"]), 0) assert_equal(len(block["deltas"][0]["outputs"]), 0) assert_equal(block["deltas"][1]["index"], 1) assert_equal(block["deltas"][1]["txid"], txid2) assert_equal(block["deltas"][1]["inputs"][0]["index"], 0) assert_equal(block["deltas"][1]["inputs"][0]["address"], "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW") assert_equal(block["deltas"][1]["inputs"][0]["satoshis"], (amount + fee_satoshis) * -1) assert_equal(block["deltas"][1]["inputs"][0]["prevtxid"], txid) assert_equal(block["deltas"][1]["inputs"][0]["prevout"], 0) assert_equal(block["deltas"][1]["outputs"][0]["index"], 0) assert_equal(block["deltas"][1]["outputs"][0]["address"], "mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW") assert_equal(block["deltas"][1]["outputs"][0]["satoshis"], amount) self.log.info("All Tests Passed")
def test_opt_in(self): """Replacing should only work if orig tx opted in""" tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN)) # Create a non-opting in transaction tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, n_sequence=0xffffffff)] tx1a.vout = [CTxOut(1 * COIN, CScript([b'a']))] tx1a_hex = tx_to_hex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) # Shouldn't be able to double-spend tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, n_sequence=0)] tx1b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b']))] tx1b_hex = tx_to_hex(tx1b) # This will raise an exception assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, True) tx1_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN)) # Create a different non-opting in transaction tx2a = CTransaction() tx2a.vin = [CTxIn(tx1_outpoint, n_sequence=0xfffffffe)] tx2a.vout = [CTxOut(1 * COIN, CScript([b'a']))] tx2a_hex = tx_to_hex(tx2a) tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True) # Still shouldn't be able to double-spend tx2b = CTransaction() tx2b.vin = [CTxIn(tx1_outpoint, n_sequence=0)] tx2b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b']))] tx2b_hex = tx_to_hex(tx2b) # This will raise an exception assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, True) # Now create a new transaction that spends from tx1a and tx2a # opt-in on one of the inputs # Transaction should be replaceable on either input tx1a_txid = int(tx1a_txid, 16) tx2a_txid = int(tx2a_txid, 16) tx3a = CTransaction() tx3a.vin = [ CTxIn(COutPoint(tx1a_txid, 0), n_sequence=0xffffffff), CTxIn(COutPoint(tx2a_txid, 0), n_sequence=0xfffffffd) ] tx3a.vout = [ CTxOut(int(0.9 * COIN), CScript([b'c'])), CTxOut(int(0.9 * COIN), CScript([b'd'])) ] tx3a_hex = tx_to_hex(tx3a) self.nodes[0].sendrawtransaction(tx3a_hex, True) tx3b = CTransaction() tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), n_sequence=0)] tx3b.vout = [CTxOut(int(0.5 * COIN), CScript([b'e']))] tx3b_hex = tx_to_hex(tx3b) tx3c = CTransaction() tx3c.vin = [CTxIn(COutPoint(tx2a_txid, 0), n_sequence=0)] tx3c.vout = [CTxOut(int(0.5 * COIN), CScript([b'f']))] tx3c_hex = tx_to_hex(tx3c) self.nodes[0].sendrawtransaction(tx3b_hex, True) # If tx3b was accepted, tx3c won't look like a replacement, # but make sure it is accepted anyway self.nodes[0].sendrawtransaction(tx3c_hex, True)
def run_test(self): print "Mining blocks..." self.nodes[0].generate(105) self.sync_all() chain_height = self.nodes[1].getblockcount() assert_equal(chain_height, 105) assert_equal(self.nodes[1].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 0) addr1 = "zrCBKy4Uoy1X5jws6cxLqrMuE1ukuctSqfS" addr2 = "ztano5XjpquCJdSipz7VRGFgdLqNjXmV9cD" # Check that balances are correct balance0 = self.nodes[1].getaddressbalance(addr1) assert_equal(balance0["balance"], 0) # Check p2pkh and p2sh address indexes print "Testing p2pkh and p2sh address index..." txid0 = self.nodes[0].sendtoaddress(addr2, 10) self.nodes[0].generate(1) txidb0 = self.nodes[0].sendtoaddress(addr1, 10) self.nodes[0].generate(1) txid1 = self.nodes[0].sendtoaddress(addr2, 15) self.nodes[0].generate(1) txidb1 = self.nodes[0].sendtoaddress(addr1, 15) self.nodes[0].generate(1) txid2 = self.nodes[0].sendtoaddress(addr2, 20) self.nodes[0].generate(1) txidb2 = self.nodes[0].sendtoaddress(addr1, 20) self.nodes[0].generate(1) self.sync_all() txids = self.nodes[1].getaddresstxids(addr2) assert_equal(len(txids), 3) assert_equal(txids[0], txid0) assert_equal(txids[1], txid1) assert_equal(txids[2], txid2) txidsb = self.nodes[1].getaddresstxids(addr1) assert_equal(len(txidsb), 3) assert_equal(txidsb[0], txidb0) assert_equal(txidsb[1], txidb1) assert_equal(txidsb[2], txidb2) # Check that limiting by height works print "Testing querying txids by range of block heights.." height_txids = self.nodes[1].getaddresstxids({ "addresses": [addr1], "start": 105, "end": 110 }) assert_equal(len(height_txids), 2) assert_equal(height_txids[0], txidb0) assert_equal(height_txids[1], txidb1) # Check that multiple addresses works multitxids = self.nodes[1].getaddresstxids( {"addresses": [addr1, addr2]}) assert_equal(len(multitxids), 6) assert_equal(multitxids[0], txid0) assert_equal(multitxids[1], txidb0) assert_equal(multitxids[2], txid1) assert_equal(multitxids[3], txidb1) assert_equal(multitxids[4], txid2) assert_equal(multitxids[5], txidb2) # Check that balances are correct balance0 = self.nodes[1].getaddressbalance(addr1) assert_equal(balance0["balance"], 45 * 100000000) # Check that outputs with the same address will only return one txid print "Testing for txid uniqueness..." op_hash160 = "a9" op_push_20_bytes_onto_the_stack = "14" addressHash = "6349a418fc4578d10a372b54b45c280cc8c4382f" op_equal = "87" genesisCbah = "20bb1acf2c1fc1228967a611c7db30632098f0c641855180b5fe23793b72eea50d00b4" scriptPubKey = binascii.unhexlify(op_hash160 + op_push_20_bytes_onto_the_stack + addressHash + op_equal + genesisCbah) unspent = self.nodes[0].listunspent() unspent.sort(key=lambda x: x["amount"], reverse=True) tx = CTransaction() tx.vin = [ CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"])) ] tx.vout = [CTxOut(10, scriptPubKey), CTxOut(11, scriptPubKey)] tx.rehash() signed_tx = self.nodes[0].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) sent_txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) self.nodes[0].generate(1) self.sync_all() txidsmany = self.nodes[1].getaddresstxids(addr1) assert_equal(len(txidsmany), 4) assert_equal(txidsmany[3], sent_txid) # Check that balances are correct print "Testing balances..." balance0 = self.nodes[1].getaddressbalance(addr1) assert_equal(balance0["balance"], 45 * 100000000 + 21) # Check that balances are correct after spending print "Testing balances after spending..." privkey2 = "cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG" address2 = "ztUB6YWTcj2uUe5Rbucnc7oFevn7wCKyN63" op_dup = "76" addressHash2 = "0b2f0a0c31bfe0406b0ccc1381fdbe311946dadc" op_equalverify = "88" op_checksig = "ac" scriptPubKey2 = binascii.unhexlify(op_dup + op_hash160 + op_push_20_bytes_onto_the_stack + addressHash2 + op_equalverify + op_checksig + genesisCbah) self.nodes[0].importprivkey(privkey2) unspent = self.nodes[0].listunspent() unspent.sort(key=lambda x: x["amount"], reverse=True) tx = CTransaction() tx.vin = [ CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"])) ] amount = unspent[0]["amount"] * 100000000 tx.vout = [CTxOut(amount, scriptPubKey2)] tx.rehash() signed_tx = self.nodes[0].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) spending_txid = self.nodes[0].sendrawtransaction( signed_tx["hex"], True) self.nodes[0].generate(1) self.sync_all() balance1 = self.nodes[1].getaddressbalance(address2) assert_equal(balance1["balance"], amount) tx = CTransaction() tx.vin = [CTxIn(COutPoint(int(spending_txid, 16), 0))] send_amount = 1 * 100000000 + 12840 change_amount = amount - send_amount - 10000 tx.vout = [ CTxOut(change_amount, scriptPubKey2), CTxOut(send_amount, scriptPubKey) ] tx.rehash() signed_tx = self.nodes[0].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) sent_txid = self.nodes[0].sendrawtransaction(signed_tx["hex"], True) self.nodes[0].generate(1) self.sync_all() balance2 = self.nodes[1].getaddressbalance(address2) assert_equal(balance2["balance"], change_amount) # Check that deltas are returned correctly deltas = self.nodes[1].getaddressdeltas({ "addresses": [address2], "start": 1, "end": 200 }) balance3 = 0 for delta in deltas: balance3 += delta["satoshis"] assert_equal(balance3, change_amount) assert_equal(deltas[0]["address"], address2) assert_equal(deltas[0]["blockindex"], 1) # Check that entire range will be queried deltasAll = self.nodes[1].getaddressdeltas({"addresses": [address2]}) assert_equal(len(deltasAll), len(deltas)) # Check that deltas can be returned from range of block heights deltas = self.nodes[1].getaddressdeltas({ "addresses": [address2], "start": 113, "end": 113 }) assert_equal(len(deltas), 1) # Check that unspent outputs can be queried print "Testing utxos..." utxos = self.nodes[1].getaddressutxos({"addresses": [address2]}) assert_equal(len(utxos), 1) assert_equal(utxos[0]["satoshis"], change_amount) # Check that indexes will be updated with a reorg print "Testing reorg..." best_hash = self.nodes[0].getbestblockhash() self.nodes[0].invalidateblock(best_hash) self.nodes[1].invalidateblock(best_hash) self.nodes[2].invalidateblock(best_hash) self.nodes[3].invalidateblock(best_hash) self.sync_all() balance4 = self.nodes[1].getaddressbalance(address2) assert_equal(balance4, balance1) utxos2 = self.nodes[1].getaddressutxos({"addresses": [address2]}) assert_equal(len(utxos2), 1) assert_equal(utxos2[0]["satoshis"], amount) # Check sorting of utxos self.nodes[2].generate(150) self.nodes[2].sendtoaddress(address2, 50) self.nodes[2].generate(1) self.nodes[2].sendtoaddress(address2, 50) self.nodes[2].generate(1) self.sync_all() utxos3 = self.nodes[1].getaddressutxos({"addresses": [address2]}) assert_equal(len(utxos3), 3) assert_equal(utxos3[0]["height"], 114) assert_equal(utxos3[1]["height"], 264) assert_equal(utxos3[2]["height"], 265) # Check mempool indexing print "Testing mempool indexing..." privKey3 = "cVfUn53hAbRrDEuMexyfgDpZPhF7KqXpS8UZevsyTDaugB7HZ3CD" address3 = "ztihzFwiPbcoMVWzvMAHf37o8jw9VSHdLtC" addressHash3 = "aa9872b5bbcdb511d89e0e11aa27da73fd2c3f50" scriptPubKey3 = binascii.unhexlify(op_dup + op_hash160 + op_push_20_bytes_onto_the_stack + addressHash3 + op_equalverify + op_checksig + genesisCbah) #address4 = "zrJgNMHvfLY26avAQCeHk8NAQxubq7CExqH" scriptPubKey4 = binascii.unhexlify(op_hash160 + op_push_20_bytes_onto_the_stack + addressHash3 + op_equal + genesisCbah) unspent = self.nodes[2].listunspent() unspent.sort(key=lambda x: x["amount"], reverse=True) tx = CTransaction() tx.vin = [ CTxIn(COutPoint(int(unspent[0]["txid"], 16), unspent[0]["vout"])) ] amount = unspent[0]["amount"] * 100000000 tx.vout = [CTxOut(amount, scriptPubKey3)] tx.rehash() signed_tx = self.nodes[2].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) memtxid1 = self.nodes[2].sendrawtransaction(signed_tx["hex"], True) time.sleep(2) tx2 = CTransaction() tx2.vin = [ CTxIn(COutPoint(int(unspent[1]["txid"], 16), unspent[1]["vout"])) ] amount = unspent[1]["amount"] * 100000000 tx2.vout = [ CTxOut(amount / 4, scriptPubKey3), CTxOut(amount / 4, scriptPubKey3), CTxOut(amount / 4, scriptPubKey4), CTxOut(amount / 4, scriptPubKey4) ] tx2.rehash() signed_tx2 = self.nodes[2].signrawtransaction( binascii.hexlify(tx2.serialize()).decode("utf-8")) memtxid2 = self.nodes[2].sendrawtransaction(signed_tx2["hex"], True) time.sleep(2) mempool = self.nodes[2].getaddressmempool({"addresses": [address3]}) assert_equal(len(mempool), 3) assert_equal(mempool[0]["txid"], memtxid1) assert_equal(mempool[0]["address"], address3) assert_equal(mempool[0]["index"], 0) assert_equal(mempool[1]["txid"], memtxid2) assert_equal(mempool[1]["index"], 0) assert_equal(mempool[2]["txid"], memtxid2) assert_equal(mempool[2]["index"], 1) self.nodes[2].generate(1) self.sync_all() mempool2 = self.nodes[2].getaddressmempool({"addresses": [address3]}) assert_equal(len(mempool2), 0) tx = CTransaction() tx.vin = [ CTxIn(COutPoint(int(memtxid2, 16), 0)), CTxIn(COutPoint(int(memtxid2, 16), 1)) ] tx.vout = [CTxOut(amount / 2 - 10000, scriptPubKey2)] tx.rehash() self.nodes[2].importprivkey(privKey3) signed_tx3 = self.nodes[2].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) self.nodes[2].sendrawtransaction(signed_tx3["hex"], True) time.sleep(2) mempool3 = self.nodes[2].getaddressmempool({"addresses": [address3]}) assert_equal(len(mempool3), 2) assert_equal(mempool3[0]["prevtxid"], memtxid2) assert_equal(mempool3[0]["prevout"], 0) assert_equal(mempool3[1]["prevtxid"], memtxid2) assert_equal(mempool3[1]["prevout"], 1) # sending and receiving to the same address privkey1 = "cQY2s58LhzUCmEXN8jtAp1Etnijx78YRZ466w4ikX1V4UpTpbsf8" address1 = "ztkoUySJkS8SMoQEjR6SkSgmDXtMB531yiw" address1hash = "c192bff751af8efec15135d42bfeedf91a6f3e34" address1script = binascii.unhexlify(op_dup + op_hash160 + op_push_20_bytes_onto_the_stack + address1hash + op_equalverify + op_checksig + genesisCbah) self.nodes[0].sendtoaddress(address1, 10) self.nodes[0].generate(1) self.sync_all() utxos = self.nodes[1].getaddressutxos({"addresses": [address1]}) assert_equal(len(utxos), 1) tx = CTransaction() tx.vin = [ CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["outputIndex"])) ] amount = utxos[0]["satoshis"] - 1000 tx.vout = [CTxOut(amount, address1script)] tx.rehash() self.nodes[0].importprivkey(privkey1) signed_tx = self.nodes[0].signrawtransaction( binascii.hexlify(tx.serialize()).decode("utf-8")) self.nodes[0].sendrawtransaction(signed_tx["hex"], True) self.sync_all() mempool_deltas = self.nodes[2].getaddressmempool( {"addresses": [address1]}) assert_equal(len(mempool_deltas), 2) # Include chaininfo in results print "Testing results with chain info..." deltas_with_info = self.nodes[1].getaddressdeltas({ "addresses": [address2], "start": 1, "end": 200, "chainInfo": True }) start_block_hash = self.nodes[1].getblockhash(1) end_block_hash = self.nodes[1].getblockhash(200) assert_equal(deltas_with_info["start"]["height"], 1) assert_equal(deltas_with_info["start"]["hash"], start_block_hash) assert_equal(deltas_with_info["end"]["height"], 200) assert_equal(deltas_with_info["end"]["hash"], end_block_hash) utxos_with_info = self.nodes[1].getaddressutxos({ "addresses": [address2], "chainInfo": True }) expected_tip_block_hash = self.nodes[1].getblockhash(267) assert_equal(utxos_with_info["height"], 267) assert_equal(utxos_with_info["hash"], expected_tip_block_hash) # Check that indexes don't get updated when checking a new block (e.g. when calling getBlockTemplate) # Initial balance is 0 and no index has been stored for addr3 addr3 = self.nodes[1].getnewaddress() addr3_balance = self.nodes[2].getaddressbalance(addr3) addr3_txs = self.nodes[2].getaddresstxids(addr3) addr3_utxos = self.nodes[2].getaddressutxos(addr3) addr3_mempool = self.nodes[2].getaddressmempool(addr3) # The initial balance must be 0 assert_equal(addr3_balance["balance"], 0) # At the beginning no address index must be stored assert_equal(addr3_txs, []) # At the beginning no unspent index must be stored assert_equal(addr3_utxos, []) # At the beginning no address mempool index must be stored assert_equal(addr3_mempool, []) # Add to mempool a transaction that sends money to addr3 addr3_amount = 0.1 addr3_txid = self.nodes[2].sendtoaddress(addr3, addr3_amount) addr3_balance = self.nodes[2].getaddressbalance(addr3) addr3_txs = self.nodes[2].getaddresstxids(addr3) addr3_utxos = self.nodes[2].getaddressutxos(addr3) addr3_mempool = self.nodes[2].getaddressmempool(addr3) # The balance must still be 0 assert_equal(addr3_balance["balance"], 0) # The address index must still be empty assert_equal(addr3_txs, []) # The unspent index must still be empty assert_equal(addr3_utxos, []) # The address mempool index must contain the new transaction assert_equal(len(addr3_mempool), 1) assert_equal(addr3_mempool[0]["txid"], addr3_txid) # Call getBlockTemplate to trigger a call to VerifyBlock() => ConnectBlock() # It should not update any index self.nodes[2].getblocktemplate() addr3_balance = self.nodes[2].getaddressbalance(addr3) addr3_txs = self.nodes[2].getaddresstxids(addr3) addr3_utxos = self.nodes[2].getaddressutxos(addr3) addr3_mempool = self.nodes[2].getaddressmempool(addr3) # The balance must still be 0 assert_equal(addr3_balance["balance"], 0) # The address index must still be empty assert_equal(addr3_txs, []) # The unspent index must still be empty assert_equal(addr3_utxos, []) # The address mempool index must still be empty assert_equal(len(addr3_mempool), 1) assert_equal(addr3_mempool[0]["txid"], addr3_txid) # Connect a new block "validating" the transaction sending money to addr3 self.nodes[2].generate(1) self.sync_all() addr3_balance = self.nodes[2].getaddressbalance(addr3) addr3_txs = self.nodes[2].getaddresstxids(addr3) addr3_utxos = self.nodes[2].getaddressutxos(addr3) addr3_mempool = self.nodes[2].getaddressmempool(addr3) # The balance must be updated assert_equal(addr3_balance["balance"], 0.1 * 1e8) # The address index must contain only the new transaction assert_equal(len(addr3_txs), 1) assert_equal(addr3_txs[0], addr3_txid) # The unspent index must contain only the new transaction assert_equal(len(addr3_utxos), 1) assert_equal(addr3_utxos[0]["txid"], addr3_txid) # The address mempool index must be empty again assert_equal(addr3_mempool, []) print "Passed\n"
def test_doublespend_tree(self): """Doublespend of a big tree of transactions""" initial_n_value = 50 * COIN tx0_outpoint = make_utxo(self.nodes[0], initial_n_value) def branch(prevout, initial_value, max_txs, tree_width=5, fee_val=0.0001 * COIN, _total_txs=None): if _total_txs is None: _total_txs = [0] if _total_txs[0] >= max_txs: return txout_value = (initial_value - fee_val) // tree_width if txout_value < fee_val: return vout = [ CTxOut(txout_value, CScript([i + 1])) for i in range(tree_width) ] tx_data = CTransaction() tx_data.vin = [CTxIn(prevout, n_sequence=0)] tx_data.vout = vout tx_hex = tx_to_hex(tx_data) assert (len(tx_data.serialize()) < 100000) txid = self.nodes[0].sendrawtransaction(tx_hex, True) yield tx_data _total_txs[0] += 1 txid = int(txid, 16) for i, _ in enumerate(tx_data.vout): for x in branch(COutPoint(txid, i), txout_value, max_txs, tree_width=tree_width, fee_val=fee_val, _total_txs=_total_txs): yield x fee = int(0.0001 * COIN) n = MAX_REPLACEMENT_LIMIT tree_txs = list(branch(tx0_outpoint, initial_n_value, n, fee_val=fee)) assert_equal(len(tree_txs), n) # Attempt double-spend, will fail because too little fee paid dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, n_sequence=0)] dbl_tx.vout = [CTxOut(initial_n_value - fee * n, CScript([1]))] dbl_tx_hex = tx_to_hex(dbl_tx) # This will raise an exception due to insufficient fee assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) # 1 ESS fee is enough dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, n_sequence=0)] dbl_tx.vout = [ CTxOut(initial_n_value - fee * n - 1 * COIN, CScript([1])) ] dbl_tx_hex = tx_to_hex(dbl_tx) self.nodes[0].sendrawtransaction(dbl_tx_hex, True) mempool = self.nodes[0].getrawmempool() for tx in tree_txs: tx.rehash() assert (tx.hash not in mempool) # Try again, but with more total transactions than the "max txs # double-spent at once" anti-DoS limit. for n in (MAX_REPLACEMENT_LIMIT + 1, MAX_REPLACEMENT_LIMIT * 2): fee = int(0.0001 * COIN) tx0_outpoint = make_utxo(self.nodes[0], initial_n_value) tree_txs = list( branch(tx0_outpoint, initial_n_value, n, fee_val=fee)) assert_equal(len(tree_txs), n) dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, n_sequence=0)] dbl_tx.vout = [CTxOut(initial_n_value - 2 * fee * n, CScript([1]))] dbl_tx_hex = tx_to_hex(dbl_tx) # This will raise an exception assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) for tx in tree_txs: tx.rehash() self.nodes[0].getrawtransaction(tx.hash)