7
7
from copy import deepcopy
8
8
from decimal import Decimal
9
9
10
- from test_framework .blocktools import COINBASE_MATURITY
11
10
from test_framework .messages import (
12
11
BIP125_SEQUENCE_NUMBER ,
13
12
COIN ,
18
17
)
19
18
from test_framework .script import CScript , OP_DROP
20
19
from test_framework .test_framework import BitcoinTestFramework
21
- from test_framework .util import assert_equal , assert_raises_rpc_error , satoshi_round
22
- from test_framework .script_util import DUMMY_P2WPKH_SCRIPT , DUMMY_2_P2WPKH_SCRIPT
20
+ from test_framework .util import (
21
+ assert_equal ,
22
+ assert_greater_than ,
23
+ assert_raises_rpc_error ,
24
+ )
25
+ from test_framework .script_util import (
26
+ DUMMY_P2WPKH_SCRIPT ,
27
+ DUMMY_2_P2WPKH_SCRIPT ,
28
+ )
23
29
from test_framework .wallet import MiniWallet
24
30
31
+
25
32
MAX_REPLACEMENT_LIMIT = 100
26
33
class ReplaceByFeeTest (BitcoinTestFramework ):
27
34
def set_test_params (self ):
@@ -89,29 +96,23 @@ def run_test(self):
89
96
def make_utxo (self , node , amount , confirmed = True , scriptPubKey = DUMMY_P2WPKH_SCRIPT ):
90
97
"""Create a txout with a given amount and scriptPubKey
91
98
92
- Mines coins as needed.
99
+ Assumes that MiniWallet has enough funds to cover the amount and the fixed fee
100
+ (from it's internal utxos, the one with the largest value is taken).
93
101
94
102
confirmed - txouts created will be confirmed in the blockchain;
95
103
unconfirmed otherwise.
96
104
"""
97
- fee = 1 * COIN
98
- while node .getbalance () < satoshi_round ((amount + fee ) / COIN ):
99
- self .generate (node , COINBASE_MATURITY )
100
-
101
- new_addr = node .getnewaddress ()
102
- txid = node .sendtoaddress (new_addr , satoshi_round ((amount + fee ) / COIN ))
103
- tx1 = node .getrawtransaction (txid , 1 )
104
- txid = int (txid , 16 )
105
- i , _ = next (filter (lambda vout : new_addr == vout [1 ]['scriptPubKey' ]['address' ], enumerate (tx1 ['vout' ])))
106
-
107
- tx2 = CTransaction ()
108
- tx2 .vin = [CTxIn (COutPoint (txid , i ))]
109
- tx2 .vout = [CTxOut (amount , scriptPubKey )]
110
- tx2 .rehash ()
111
-
112
- signed_tx = node .signrawtransactionwithwallet (tx2 .serialize ().hex ())
113
-
114
- txid = node .sendrawtransaction (signed_tx ['hex' ], 0 )
105
+ # MiniWallet only supports sweeping utxos to its own internal scriptPubKey, so in
106
+ # order to create an output with arbitrary amount/scriptPubKey, we have to add it
107
+ # manually after calling the create_self_transfer method. The MiniWallet output's
108
+ # nValue has to be adapted accordingly (amount and fee deduction). To keep things
109
+ # simple, we use a fixed fee of 1000 Satoshis here.
110
+ fee = 1000
111
+ tx = self .wallet .create_self_transfer (from_node = node , fee_rate = 0 , mempool_valid = False )['tx' ]
112
+ assert_greater_than (tx .vout [0 ].nValue , amount + fee )
113
+ tx .vout [0 ].nValue -= (amount + fee ) # change output -> MiniWallet
114
+ tx .vout .append (CTxOut (amount , scriptPubKey )) # desired output -> to be returned
115
+ txid = self .wallet .sendrawtransaction (from_node = node , tx_hex = tx .serialize ().hex ())
115
116
116
117
# If requested, ensure txouts are confirmed.
117
118
if confirmed :
@@ -124,7 +125,7 @@ def make_utxo(self, node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRI
124
125
assert new_size < mempool_size
125
126
mempool_size = new_size
126
127
127
- return COutPoint (int (txid , 16 ), 0 )
128
+ return COutPoint (int (txid , 16 ), 1 )
128
129
129
130
def test_simple_doublespend (self ):
130
131
"""Simple doublespend"""
@@ -161,14 +162,14 @@ def test_simple_doublespend(self):
161
162
def test_doublespend_chain (self ):
162
163
"""Doublespend of a long chain"""
163
164
164
- initial_nValue = 50 * COIN
165
+ initial_nValue = 5 * COIN
165
166
tx0_outpoint = self .make_utxo (self .nodes [0 ], initial_nValue )
166
167
167
168
prevout = tx0_outpoint
168
169
remaining_value = initial_nValue
169
170
chain_txids = []
170
- while remaining_value > 10 * COIN :
171
- remaining_value -= 1 * COIN
171
+ while remaining_value > 1 * COIN :
172
+ remaining_value -= int ( 0. 1 * COIN )
172
173
tx = CTransaction ()
173
174
tx .vin = [CTxIn (prevout , nSequence = 0 )]
174
175
tx .vout = [CTxOut (remaining_value , CScript ([1 , OP_DROP ] * 15 + [1 ]))]
@@ -178,10 +179,10 @@ def test_doublespend_chain(self):
178
179
prevout = COutPoint (int (txid , 16 ), 0 )
179
180
180
181
# Whether the double-spend is allowed is evaluated by including all
181
- # child fees - 40 BTC - so this attempt is rejected.
182
+ # child fees - 4 BTC - so this attempt is rejected.
182
183
dbl_tx = CTransaction ()
183
184
dbl_tx .vin = [CTxIn (tx0_outpoint , nSequence = 0 )]
184
- dbl_tx .vout = [CTxOut (initial_nValue - 30 * COIN , DUMMY_P2WPKH_SCRIPT )]
185
+ dbl_tx .vout = [CTxOut (initial_nValue - 3 * COIN , DUMMY_P2WPKH_SCRIPT )]
185
186
dbl_tx_hex = dbl_tx .serialize ().hex ()
186
187
187
188
# This will raise an exception due to insufficient fee
@@ -190,7 +191,7 @@ def test_doublespend_chain(self):
190
191
# Accepted with sufficient fee
191
192
dbl_tx = CTransaction ()
192
193
dbl_tx .vin = [CTxIn (tx0_outpoint , nSequence = 0 )]
193
- dbl_tx .vout = [CTxOut (1 * COIN , DUMMY_P2WPKH_SCRIPT )]
194
+ dbl_tx .vout = [CTxOut (int ( 0. 1 * COIN ) , DUMMY_P2WPKH_SCRIPT )]
194
195
dbl_tx_hex = dbl_tx .serialize ().hex ()
195
196
self .nodes [0 ].sendrawtransaction (dbl_tx_hex , 0 )
196
197
@@ -201,10 +202,10 @@ def test_doublespend_chain(self):
201
202
def test_doublespend_tree (self ):
202
203
"""Doublespend of a big tree of transactions"""
203
204
204
- initial_nValue = 50 * COIN
205
+ initial_nValue = 5 * COIN
205
206
tx0_outpoint = self .make_utxo (self .nodes [0 ], initial_nValue )
206
207
207
- def branch (prevout , initial_value , max_txs , tree_width = 5 , fee = 0.0001 * COIN , _total_txs = None ):
208
+ def branch (prevout , initial_value , max_txs , tree_width = 5 , fee = 0.00001 * COIN , _total_txs = None ):
208
209
if _total_txs is None :
209
210
_total_txs = [0 ]
210
211
if _total_txs [0 ] >= max_txs :
@@ -235,7 +236,7 @@ def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001 * COIN, _to
235
236
_total_txs = _total_txs ):
236
237
yield x
237
238
238
- fee = int (0.0001 * COIN )
239
+ fee = int (0.00001 * COIN )
239
240
n = MAX_REPLACEMENT_LIMIT
240
241
tree_txs = list (branch (tx0_outpoint , initial_nValue , n , fee = fee ))
241
242
assert_equal (len (tree_txs ), n )
@@ -248,10 +249,10 @@ def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001 * COIN, _to
248
249
# This will raise an exception due to insufficient fee
249
250
assert_raises_rpc_error (- 26 , "insufficient fee" , self .nodes [0 ].sendrawtransaction , dbl_tx_hex , 0 )
250
251
251
- # 1 BTC fee is enough
252
+ # 0. 1 BTC fee is enough
252
253
dbl_tx = CTransaction ()
253
254
dbl_tx .vin = [CTxIn (tx0_outpoint , nSequence = 0 )]
254
- dbl_tx .vout = [CTxOut (initial_nValue - fee * n - 1 * COIN , DUMMY_P2WPKH_SCRIPT )]
255
+ dbl_tx .vout = [CTxOut (initial_nValue - fee * n - int ( 0. 1 * COIN ) , DUMMY_P2WPKH_SCRIPT )]
255
256
dbl_tx_hex = dbl_tx .serialize ().hex ()
256
257
self .nodes [0 ].sendrawtransaction (dbl_tx_hex , 0 )
257
258
@@ -264,7 +265,7 @@ def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001 * COIN, _to
264
265
# Try again, but with more total transactions than the "max txs
265
266
# double-spent at once" anti-DoS limit.
266
267
for n in (MAX_REPLACEMENT_LIMIT + 1 , MAX_REPLACEMENT_LIMIT * 2 ):
267
- fee = int (0.0001 * COIN )
268
+ fee = int (0.00001 * COIN )
268
269
tx0_outpoint = self .make_utxo (self .nodes [0 ], initial_nValue )
269
270
tree_txs = list (branch (tx0_outpoint , initial_nValue , n , fee = fee ))
270
271
assert_equal (len (tree_txs ), n )
0 commit comments