From 92b5911234af3892a0f7c741dc7d0fcc6ae55d69 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Wed, 20 Sep 2017 15:37:30 -0400 Subject: [PATCH 1/2] mininode: CTxOut has default asset bitcoin, sets explicit value via int --- qa/rpc-tests/test_framework/mininode.py | 10 +++++++--- qa/rpc-tests/test_framework/util.py | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 280d18f596..3f4a3be9e6 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -28,7 +28,7 @@ import asyncore import time import sys import random -from .util import hex_str_to_bytes, bytes_to_hex_str +from .util import hex_str_to_bytes, bytes_to_hex_str, BITCOIN_ASSET_OUT from io import BytesIO from codecs import encode import hashlib @@ -445,10 +445,14 @@ class CTxOutNonce(object): def __repr__(self): return "CTxOutNonce(vchCommitment=%s)" % self.vchCommitment +# Asset type defaults to bitcoin class CTxOut(object): - def __init__(self, nValue=CTxOutValue(), scriptPubKey=b'', nAsset=CTxOutAsset(), nNonce=CTxOutNonce()): + def __init__(self, nValue=CTxOutValue(), scriptPubKey=b'', nAsset=CTxOutAsset(BITCOIN_ASSET_OUT), nNonce=CTxOutNonce()): self.nAsset = nAsset - self.nValue = nValue + if type(nValue) is int: + self.nValue = CTxOutValue(nValue) + else: + self.nValue = nValue self.nNonce = nNonce self.scriptPubKey = scriptPubKey diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 285f3e8cb4..d6cc550324 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -28,6 +28,10 @@ from .authproxy import AuthServiceProxy, JSONRPCException COVERAGE_DIR = None +BITCOIN_ASSET = bytearray.fromhex("b2e15d0d7a0c94e4e2ce0fe6e8691b9e451377f6e46e8045a86f7c4b5d4f0f23") +BITCOIN_ASSET.reverse() +BITCOIN_ASSET_OUT = b"\x01"+BITCOIN_ASSET + # The maximum number of nodes a single test can spawn MAX_NODES = 8 # Don't assign rpc or p2p ports lower than this From 5af4d76f180f5d65e18d8505ae2a5a13de6a9515 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Wed, 20 Sep 2017 15:38:30 -0400 Subject: [PATCH 2/2] re-activate transaction replacement functional test --- qa/rpc-tests/replace-by-fee.py | 85 ++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/qa/rpc-tests/replace-by-fee.py b/qa/rpc-tests/replace-by-fee.py index bd33fd3635..3de9cc97b9 100755 --- a/qa/rpc-tests/replace-by-fee.py +++ b/qa/rpc-tests/replace-by-fee.py @@ -11,7 +11,6 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * from test_framework.script import * from test_framework.mininode import * - MAX_REPLACEMENT_LIMIT = 100 def txToHex(tx): @@ -26,11 +25,8 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])): unconfirmed otherwise. """ fee = 1*COIN - while node.getbalance() < satoshi_round((amount + fee)/COIN): - node.generate(100) - #print (node.getbalance(), amount, fee) - new_addr = node.getnewaddress() + new_addr = node.validateaddress(node.getnewaddress())['unconfidential'] #print new_addr txid = node.sendtoaddress(new_addr, satoshi_round((amount+fee)/COIN)) tx1 = node.getrawtransaction(txid, 1) @@ -39,14 +35,15 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])): for i, txout in enumerate(tx1['vout']): #print i, txout['scriptPubKey']['addresses'] - if txout['scriptPubKey']['addresses'] == [new_addr]: + if 'addresses' in txout['scriptPubKey'] and txout['scriptPubKey']['addresses'] == [new_addr]: #print i break assert i is not None tx2 = CTransaction() tx2.vin = [CTxIn(COutPoint(txid, i))] - tx2.vout = [CTxOut(amount, scriptPubKey)] + tx2.vout = [CTxOut(amount, scriptPubKey), CTxOut(fee)] + tx2.vout = [CTxOut(amount, scriptPubKey), CTxOut(fee)] tx2.rehash() signed_tx = node.signrawtransaction(txToHex(tx2)) @@ -85,7 +82,6 @@ class ReplaceByFeeTest(BitcoinTestFramework): self.is_network_split = False def run_test(self): - return #TODO make_utxo(self.nodes[0], 1*COIN) print("Running test simple doublespend...") @@ -123,14 +119,14 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a.vout = [CTxOut(1*COIN, CScript([b'a'])), CTxOut(int(0.1*COIN), b'')] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) # Should fail because we haven't changed the fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(1*COIN, CScript([b'b']))] + tx1b.vout = [CTxOut(1*COIN, CScript([b'b'])), CTxOut(int(0.1*COIN), b'')] tx1b_hex = txToHex(tx1b) try: @@ -143,7 +139,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Extra 0.1 BTC fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] + tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b'])), CTxOut(int(0.2*COIN), b'')] tx1b_hex = txToHex(tx1b) tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) @@ -167,7 +163,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): remaining_value -= 1*COIN tx = CTransaction() tx.vin = [CTxIn(prevout, nSequence=0)] - tx.vout = [CTxOut(remaining_value, CScript([1]))] + tx.vout = [CTxOut(remaining_value, CScript([1])), CTxOut(1*COIN)] tx_hex = txToHex(tx) txid = self.nodes[0].sendrawtransaction(tx_hex, True) chain_txids.append(txid) @@ -177,7 +173,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # child fees - 40 BTC - so this attempt is rejected. dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - 30*COIN, CScript([1]))] + dbl_tx.vout = [CTxOut(initial_nValue - 30*COIN, CScript([1])), CTxOut(30*COIN)] dbl_tx_hex = txToHex(dbl_tx) try: @@ -190,7 +186,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Accepted with sufficient fee dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(1*COIN, CScript([1]))] + dbl_tx.vout = [CTxOut(1*COIN, CScript([1])), CTxOut(49*COIN)] dbl_tx_hex = txToHex(dbl_tx) self.nodes[0].sendrawtransaction(dbl_tx_hex, True) @@ -200,27 +196,27 @@ class ReplaceByFeeTest(BitcoinTestFramework): def test_doublespend_tree(self): """Doublespend of a big tree of transactions""" - initial_nValue = 50*COIN tx0_outpoint = make_utxo(self.nodes[0], initial_nValue) - def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001*COIN, _total_txs=None): + def branch(prevout, initial_value, max_txs, tree_width=5, fee=int(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) // tree_width + fee += (initial_value - fee) % tree_width if txout_value < fee: return vout = [CTxOut(txout_value, CScript([i+1])) for i in range(tree_width)] + vout.append(CTxOut(int(fee))) tx = CTransaction() tx.vin = [CTxIn(prevout, nSequence=0)] tx.vout = vout tx_hex = txToHex(tx) - assert(len(tx.serialize()) < 100000) txid = self.nodes[0].sendrawtransaction(tx_hex, True) yield tx @@ -229,6 +225,9 @@ class ReplaceByFeeTest(BitcoinTestFramework): txid = int(txid, 16) for i, txout in enumerate(tx.vout): + # Don't grab fee outputs + if i == len(tx.vout)-1: + continue for x in branch(COutPoint(txid, i), txout_value, max_txs, tree_width=tree_width, fee=fee, @@ -243,7 +242,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Attempt double-spend, will fail because too little fee paid dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - fee*n, CScript([1]))] + dbl_tx.vout = [CTxOut(initial_nValue - fee*n, CScript([1])), CTxOut(fee*n)] dbl_tx_hex = txToHex(dbl_tx) try: self.nodes[0].sendrawtransaction(dbl_tx_hex, True) @@ -255,7 +254,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # 1 BTC fee is enough dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - fee*n - 1*COIN, CScript([1]))] + dbl_tx.vout = [CTxOut(initial_nValue - fee*n - 1*COIN, CScript([1])), CTxOut(fee*n+1*COIN)] dbl_tx_hex = txToHex(dbl_tx) self.nodes[0].sendrawtransaction(dbl_tx_hex, True) @@ -275,7 +274,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - 2*fee*n, CScript([1]))] + dbl_tx.vout = [CTxOut(initial_nValue - 2*fee*n, CScript([1])), CTxOut(2*fee*n)] dbl_tx_hex = txToHex(dbl_tx) try: self.nodes[0].sendrawtransaction(dbl_tx_hex, True) @@ -295,7 +294,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a.vout = [CTxOut(1*COIN, CScript([b'a'])), CTxOut(int(0.1*COIN))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) @@ -303,7 +302,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # rejected. tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*999000]))] + tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*999000])), CTxOut(int(1.099*COIN))] tx1b_hex = txToHex(tx1b) try: @@ -320,7 +319,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a = CTransaction() tx1a.vin = [CTxIn(utxo1, nSequence=0)] - tx1a.vout = [CTxOut(int(1.1*COIN), CScript([b'a']))] + tx1a.vout = [CTxOut(int(1.1*COIN), CScript([b'a'])), CTxOut(int(0.1*COIN))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) @@ -331,6 +330,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx2.vin = [CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0)] tx2.vin.append(CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)) tx2.vout = tx1a.vout + tx2.vout[-1] = CTxOut(int(1.9*COIN)) tx2_hex = txToHex(tx2) try: @@ -343,7 +343,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Spend tx1a's output to test the indirect case. tx1b = CTransaction() tx1b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] - tx1b.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1b.vout = [CTxOut(1*COIN, CScript([b'a'])), CTxOut(int(0.1*COIN))] tx1b_hex = txToHex(tx1b) tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) tx1b_txid = int(tx1b_txid, 16) @@ -368,13 +368,14 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1 = CTransaction() tx1.vin = [CTxIn(confirmed_utxo)] - tx1.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1.vout = [CTxOut(1*COIN, CScript([b'a'])), CTxOut(int(0.1*COIN))] tx1_hex = txToHex(tx1) tx1_txid = self.nodes[0].sendrawtransaction(tx1_hex, True) tx2 = CTransaction() tx2.vin = [CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)] tx2.vout = tx1.vout + tx2.vout[-1] = CTxOut(int(0.2*COIN)) tx2_hex = txToHex(tx2) try: @@ -394,10 +395,12 @@ class ReplaceByFeeTest(BitcoinTestFramework): utxo = make_utxo(self.nodes[0], initial_nValue) fee = int(0.0001*COIN) split_value = int((initial_nValue-fee)/(MAX_REPLACEMENT_LIMIT+1)) + fee += int((initial_nValue-fee)%(MAX_REPLACEMENT_LIMIT+1)) outputs = [] for i in range(MAX_REPLACEMENT_LIMIT+1): outputs.append(CTxOut(split_value, CScript([1]))) + outputs.append(CTxOut(fee)) splitting_tx = CTransaction() splitting_tx.vin = [CTxIn(utxo, nSequence=0)] @@ -411,20 +414,22 @@ class ReplaceByFeeTest(BitcoinTestFramework): for i in range(MAX_REPLACEMENT_LIMIT+1): tx_i = CTransaction() tx_i.vin = [CTxIn(COutPoint(txid, i), nSequence=0)] - tx_i.vout = [CTxOut(split_value-fee, CScript([b'a']))] + tx_i.vout = [CTxOut(split_value-fee, CScript([b'a'])), CTxOut(fee)] tx_i_hex = txToHex(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) + double_spend_fee = 100*fee*(MAX_REPLACEMENT_LIMIT+1) + double_spend_value = split_value*(MAX_REPLACEMENT_LIMIT+1)-double_spend_fee inputs = [] for i in range(MAX_REPLACEMENT_LIMIT+1): inputs.append(CTxIn(COutPoint(txid, i), nSequence=0)) + double_tx = CTransaction() double_tx.vin = inputs - double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))] + double_tx.vout = [CTxOut(double_spend_value, CScript([b'a'])), CTxOut(double_spend_fee)] double_tx_hex = txToHex(double_tx) try: @@ -438,7 +443,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # 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.vout = [CTxOut(double_spend_value, CScript([b'a'])), CTxOut(double_spend_fee-split_value)] double_tx_hex = txToHex(double_tx) self.nodes[0].sendrawtransaction(double_tx_hex, True) @@ -449,14 +454,14 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Create a non-opting in transaction tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0xffffffff)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a.vout = [CTxOut(1*COIN, CScript([b'a'])), CTxOut(int(0.1*COIN))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) # Shouldn't be able to double-spend tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] + tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b'])), CTxOut(int(0.2*COIN))] tx1b_hex = txToHex(tx1b) try: @@ -472,14 +477,14 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Create a different non-opting in transaction tx2a = CTransaction() tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0xfffffffe)] - tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx2a.vout = [CTxOut(1*COIN, CScript([b'a'])), CTxOut(int(0.1*COIN))] tx2a_hex = txToHex(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, nSequence=0)] - tx2b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] + tx2b.vout = [CTxOut(int(0.9*COIN), CScript([b'b'])), CTxOut(int(0.2*COIN))] tx2b_hex = txToHex(tx2b) try: @@ -499,19 +504,19 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx3a = CTransaction() tx3a.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0xffffffff), CTxIn(COutPoint(tx2a_txid, 0), nSequence=0xfffffffd)] - tx3a.vout = [CTxOut(int(0.9*COIN), CScript([b'c'])), CTxOut(int(0.9*COIN), CScript([b'd']))] + tx3a.vout = [CTxOut(int(0.9*COIN), CScript([b'c'])), CTxOut(int(0.9*COIN), CScript([b'd'])), CTxOut(int(0.2*COIN))] tx3a_hex = txToHex(tx3a) self.nodes[0].sendrawtransaction(tx3a_hex, True) tx3b = CTransaction() tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] - tx3b.vout = [CTxOut(int(0.5*COIN), CScript([b'e']))] + tx3b.vout = [CTxOut(int(0.5*COIN), CScript([b'e'])), CTxOut(int(0.5*COIN))] tx3b_hex = txToHex(tx3b) tx3c = CTransaction() tx3c.vin = [CTxIn(COutPoint(tx2a_txid, 0), nSequence=0)] - tx3c.vout = [CTxOut(int(0.5*COIN), CScript([b'f']))] + tx3c.vout = [CTxOut(int(0.5*COIN), CScript([b'f'])), CTxOut(int(0.5*COIN))] tx3c_hex = txToHex(tx3c) self.nodes[0].sendrawtransaction(tx3b_hex, True) @@ -528,14 +533,14 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a.vout = [CTxOut(1*COIN, CScript([b'a'])), CTxOut(int(0.1*COIN))] tx1a_hex = txToHex(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, nSequence=0)] - tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*740000]))] + tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*740000])), CTxOut(int(1.099*COIN))] tx1b_hex = txToHex(tx1b) # Verify tx1b cannot replace tx1a. @@ -559,14 +564,14 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx2a = CTransaction() tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)] - tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx2a.vout = [CTxOut(1*COIN, CScript([b'a'])), CTxOut(int(0.1*COIN))] tx2a_hex = txToHex(tx2a) tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True) # Lower fee, but we'll prioritise it tx2b = CTransaction() tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] - tx2b.vout = [CTxOut(int(1.01*COIN), CScript([b'a']))] + tx2b.vout = [CTxOut(int(1.01*COIN), CScript([b'a'])), CTxOut(int(0.09*COIN))] tx2b.rehash() tx2b_hex = txToHex(tx2b)