1 #!/usr/bin/env python3
2 # Copyright (c) 2014-present The Bitcoin Core developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test BIP68 implementation."""
6 7 import random
8 import time
9 10 from test_framework.blocktools import (
11 NORMAL_GBT_REQUEST_PARAMS,
12 add_witness_commitment,
13 create_block,
14 script_to_p2wsh_script,
15 )
16 from test_framework.messages import (
17 COIN,
18 COutPoint,
19 CTransaction,
20 CTxIn,
21 CTxInWitness,
22 CTxOut,
23 tx_from_hex,
24 )
25 from test_framework.script import (
26 CScript,
27 OP_TRUE,
28 SEQUENCE_LOCKTIME_DISABLE_FLAG,
29 SEQUENCE_LOCKTIME_TYPE_FLAG,
30 SEQUENCE_LOCKTIME_GRANULARITY,
31 SEQUENCE_LOCKTIME_MASK,
32 )
33 from test_framework.test_framework import BitcoinTestFramework
34 from test_framework.util import (
35 assert_equal,
36 assert_greater_than,
37 assert_raises_rpc_error,
38 softfork_active,
39 )
40 from test_framework.wallet import MiniWallet
41 42 SCRIPT_W0_SH_OP_TRUE = script_to_p2wsh_script(CScript([OP_TRUE]))
43 44 # RPC error for non-BIP68 final transactions
45 NOT_FINAL_ERROR = "non-BIP68-final"
46 47 class BIP68Test(BitcoinTestFramework):
48 def set_test_params(self):
49 self.num_nodes = 2
50 self.extra_args = [
51 [
52 '-testactivationheight=csv@432',
53 ],
54 [
55 '-testactivationheight=csv@432',
56 ],
57 ]
58 59 def run_test(self):
60 self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
61 self.wallet = MiniWallet(self.nodes[0])
62 63 self.log.info("Running test disable flag")
64 self.test_disable_flag()
65 66 self.log.info("Running test sequence-lock-confirmed-inputs")
67 self.test_sequence_lock_confirmed_inputs()
68 69 self.log.info("Running test sequence-lock-unconfirmed-inputs")
70 self.test_sequence_lock_unconfirmed_inputs()
71 72 self.log.info("Running test BIP68 not consensus before activation")
73 self.test_bip68_not_consensus()
74 75 self.log.info("Activating BIP68 (and 112/113)")
76 self.activateCSV()
77 78 self.log.info("Verifying version=2 transactions are standard.")
79 self.log.info("Note that version=2 transactions are always standard (independent of BIP68 activation status).")
80 self.test_version2_relay()
81 82 self.log.info("Passed")
83 84 # Test that BIP68 is not in effect if tx version is 1, or if
85 # the first sequence bit is set.
86 def test_disable_flag(self):
87 # Create some unconfirmed inputs
88 utxo = self.wallet.send_self_transfer(from_node=self.nodes[0])["new_utxo"]
89 90 tx1 = CTransaction()
91 value = int((utxo["value"] - self.relayfee) * COIN)
92 93 # Check that the disable flag disables relative locktime.
94 # If sequence locks were used, this would require 1 block for the
95 # input to mature.
96 sequence_value = SEQUENCE_LOCKTIME_DISABLE_FLAG | 1
97 tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)]
98 tx1.vout = [CTxOut(value, SCRIPT_W0_SH_OP_TRUE)]
99 100 self.wallet.sign_tx(tx=tx1)
101 tx1_id = self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx1.serialize().hex())
102 tx1_id = int(tx1_id, 16)
103 104 # This transaction will enable sequence-locks, so this transaction should
105 # fail
106 tx2 = CTransaction()
107 tx2.version = 2
108 sequence_value = sequence_value & 0x7fffffff
109 tx2.vin = [CTxIn(COutPoint(tx1_id, 0), nSequence=sequence_value)]
110 tx2.wit.vtxinwit = [CTxInWitness()]
111 tx2.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
112 tx2.vout = [CTxOut(int(value - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
113 114 assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx2.serialize().hex())
115 116 # Setting the version back down to 1 should disable the sequence lock,
117 # so this should be accepted.
118 tx2.version = 1
119 120 self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2.serialize().hex())
121 122 # Calculate the median time past of a prior block ("confirmations" before
123 # the current tip).
124 def get_median_time_past(self, confirmations):
125 block_hash = self.nodes[0].getblockhash(self.nodes[0].getblockcount()-confirmations)
126 return self.nodes[0].getblockheader(block_hash)["mediantime"]
127 128 # Test that sequence locks are respected for transactions spending confirmed inputs.
129 def test_sequence_lock_confirmed_inputs(self):
130 # Create lots of confirmed utxos, and use them to generate lots of random
131 # transactions.
132 max_outputs = 50
133 while len(self.wallet.get_utxos(include_immature_coinbase=False, mark_as_spent=False)) < 200:
134 num_outputs = random.randint(1, max_outputs)
135 self.wallet.send_self_transfer_multi(from_node=self.nodes[0], num_outputs=num_outputs)
136 self.generate(self.wallet, 1)
137 138 utxos = self.wallet.get_utxos(include_immature_coinbase=False)
139 140 # Try creating a lot of random transactions.
141 # Each time, choose a random number of inputs, and randomly set
142 # some of those inputs to be sequence locked (and randomly choose
143 # between height/time locking). Small random chance of making the locks
144 # all pass.
145 for _ in range(400):
146 available_utxos = len(utxos)
147 148 # Randomly choose up to 10 inputs
149 num_inputs = random.randint(1, min(10, available_utxos))
150 random.shuffle(utxos)
151 152 # Track whether any sequence locks used should fail
153 should_pass = True
154 155 # Track whether this transaction was built with sequence locks
156 using_sequence_locks = False
157 158 tx = CTransaction()
159 tx.version = 2
160 value = 0
161 for j in range(num_inputs):
162 sequence_value = 0xfffffffe # this disables sequence locks
163 164 # 50% chance we enable sequence locks
165 if random.randint(0,1):
166 using_sequence_locks = True
167 168 # 10% of the time, make the input sequence value pass
169 input_will_pass = (random.randint(1,10) == 1)
170 sequence_value = utxos[j]["confirmations"]
171 if not input_will_pass:
172 sequence_value += 1
173 should_pass = False
174 175 # Figure out what the median-time-past was for the confirmed input
176 # Note that if an input has N confirmations, we're going back N blocks
177 # from the tip so that we're looking up MTP of the block
178 # PRIOR to the one the input appears in, as per the BIP68 spec.
179 orig_time = self.get_median_time_past(utxos[j]["confirmations"])
180 cur_time = self.get_median_time_past(0) # MTP of the tip
181 182 # can only timelock this input if it's not too old -- otherwise use height
183 can_time_lock = True
184 if ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY) >= SEQUENCE_LOCKTIME_MASK:
185 can_time_lock = False
186 187 # if time-lockable, then 50% chance we make this a time lock
188 if random.randint(0,1) and can_time_lock:
189 # Find first time-lock value that fails, or latest one that succeeds
190 time_delta = sequence_value << SEQUENCE_LOCKTIME_GRANULARITY
191 if input_will_pass and time_delta > cur_time - orig_time:
192 sequence_value = ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY)
193 elif (not input_will_pass and time_delta <= cur_time - orig_time):
194 sequence_value = ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY)+1
195 sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG
196 tx.vin.append(CTxIn(COutPoint(int(utxos[j]["txid"], 16), utxos[j]["vout"]), nSequence=sequence_value))
197 value += utxos[j]["value"]*COIN
198 # Overestimate the size of the tx - signatures should be less than 120 bytes, and leave 50 for the output
199 tx_size = len(tx.serialize().hex())//2 + 120*num_inputs + 50
200 tx.vout.append(CTxOut(int(value - self.relayfee * tx_size * COIN / 1000), self.wallet.get_output_script()))
201 self.wallet.sign_tx(tx=tx)
202 203 if (using_sequence_locks and not should_pass):
204 # This transaction should be rejected
205 assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx.serialize().hex())
206 else:
207 # This raw transaction should be accepted
208 self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx.serialize().hex())
209 self.wallet.rescan_utxos()
210 utxos = self.wallet.get_utxos(include_immature_coinbase=False)
211 212 # Test that sequence locks on unconfirmed inputs must have nSequence
213 # height or time of 0 to be accepted.
214 # Then test that BIP68-invalid transactions are removed from the mempool
215 # after a reorg.
216 def test_sequence_lock_unconfirmed_inputs(self):
217 # Store height so we can easily reset the chain at the end of the test
218 cur_height = self.nodes[0].getblockcount()
219 220 # Create a mempool tx.
221 self.wallet.rescan_utxos()
222 tx1 = self.wallet.send_self_transfer(from_node=self.nodes[0])["tx"]
223 224 # Anyone-can-spend mempool tx.
225 # Sequence lock of 0 should pass.
226 tx2 = CTransaction()
227 tx2.version = 2
228 tx2.vin = [CTxIn(COutPoint(tx1.txid_int, 0), nSequence=0)]
229 tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
230 self.wallet.sign_tx(tx=tx2)
231 tx2_raw = tx2.serialize().hex()
232 233 self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2_raw)
234 235 # Create a spend of the 0th output of orig_tx with a sequence lock
236 # of 1, and test what happens when submitting.
237 # orig_tx.vout[0] must be an anyone-can-spend output
238 def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock):
239 sequence_value = 1
240 if not use_height_lock:
241 sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG
242 243 tx = CTransaction()
244 tx.version = 2
245 tx.vin = [CTxIn(COutPoint(orig_tx.txid_int, 0), nSequence=sequence_value)]
246 tx.wit.vtxinwit = [CTxInWitness()]
247 tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
248 tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
249 250 if (orig_tx.txid_hex in node.getrawmempool()):
251 # sendrawtransaction should fail if the tx is in the mempool
252 assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=node, tx_hex=tx.serialize().hex())
253 else:
254 # sendrawtransaction should succeed if the tx is not in the mempool
255 self.wallet.sendrawtransaction(from_node=node, tx_hex=tx.serialize().hex())
256 257 return tx
258 259 test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=True)
260 test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False)
261 262 # Now mine some blocks, but make sure tx2 doesn't get mined.
263 # Use prioritisetransaction to lower the effective feerate to 0
264 self.nodes[0].prioritisetransaction(txid=tx2.txid_hex, fee_delta=int(-self.relayfee*COIN))
265 cur_time = int(time.time())
266 for _ in range(10):
267 self.nodes[0].setmocktime(cur_time + 600)
268 self.generate(self.wallet, 1, sync_fun=self.no_op)
269 cur_time += 600
270 271 assert tx2.txid_hex in self.nodes[0].getrawmempool()
272 273 test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=True)
274 test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False)
275 276 # Mine tx2, and then try again
277 self.nodes[0].prioritisetransaction(txid=tx2.txid_hex, fee_delta=int(self.relayfee*COIN))
278 279 # Advance the time on the node so that we can test timelocks
280 self.nodes[0].setmocktime(cur_time+600)
281 # Save block template now to use for the reorg later
282 tmpl = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
283 self.generate(self.nodes[0], 1)
284 assert tx2.txid_hex not in self.nodes[0].getrawmempool()
285 286 # Now that tx2 is not in the mempool, a sequence locked spend should
287 # succeed
288 tx3 = test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False)
289 assert tx3.txid_hex in self.nodes[0].getrawmempool()
290 291 self.generate(self.nodes[0], 1)
292 assert tx3.txid_hex not in self.nodes[0].getrawmempool()
293 294 # One more test, this time using height locks
295 tx4 = test_nonzero_locks(tx3, self.nodes[0], self.relayfee, use_height_lock=True)
296 assert tx4.txid_hex in self.nodes[0].getrawmempool()
297 298 # Now try combining confirmed and unconfirmed inputs
299 tx5 = test_nonzero_locks(tx4, self.nodes[0], self.relayfee, use_height_lock=True)
300 assert tx5.txid_hex not in self.nodes[0].getrawmempool()
301 302 utxo = self.wallet.get_utxo()
303 tx5.vin.append(CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=1))
304 tx5.vout[0].nValue += int(utxo["value"]*COIN)
305 self.wallet.sign_tx(tx=tx5)
306 307 assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx5.serialize().hex())
308 309 # Test mempool-BIP68 consistency after reorg
310 #
311 # State of the transactions in the last blocks:
312 # ... -> [ tx2 ] -> [ tx3 ]
313 # tip-1 tip
314 # And currently tx4 is in the mempool.
315 #
316 # If we invalidate the tip, tx3 should get added to the mempool, causing
317 # tx4 to be removed (fails sequence-lock).
318 self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
319 assert tx4.txid_hex not in self.nodes[0].getrawmempool()
320 assert tx3.txid_hex in self.nodes[0].getrawmempool()
321 322 # Now mine 2 empty blocks to reorg out the current tip (labeled tip-1 in
323 # diagram above).
324 # This would cause tx2 to be added back to the mempool, which in turn causes
325 # tx3 to be removed.
326 for i in range(2):
327 block = create_block(tmpl=tmpl, ntime=cur_time)
328 block.solve()
329 tip = block.hash_int
330 assert_equal(None if i == 1 else 'inconclusive', self.nodes[0].submitblock(block.serialize().hex()))
331 tmpl = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
332 tmpl['previousblockhash'] = '%x' % tip
333 tmpl['transactions'] = []
334 cur_time += 1
335 336 mempool = self.nodes[0].getrawmempool()
337 assert tx3.txid_hex not in mempool
338 assert tx2.txid_hex in mempool
339 340 # Reset the chain and get rid of the mocktimed-blocks
341 self.nodes[0].setmocktime(0)
342 self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height+1))
343 self.generate(self.wallet, 10, sync_fun=self.no_op)
344 345 # Make sure that BIP68 isn't being used to validate blocks prior to
346 # activation height. If more blocks are mined prior to this test
347 # being run, then it's possible the test has activated the soft fork, and
348 # this test should be moved to run earlier, or deleted.
349 def test_bip68_not_consensus(self):
350 assert not softfork_active(self.nodes[0], 'csv')
351 352 tx1 = self.wallet.send_self_transfer(from_node=self.nodes[0])["tx"]
353 354 # Make an anyone-can-spend transaction
355 tx2 = CTransaction()
356 tx2.version = 1
357 tx2.vin = [CTxIn(COutPoint(tx1.txid_int, 0), nSequence=0)]
358 tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
359 360 # sign tx2
361 self.wallet.sign_tx(tx=tx2)
362 tx2_raw = tx2.serialize().hex()
363 tx2 = tx_from_hex(tx2_raw)
364 365 self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2_raw)
366 367 # Now make an invalid spend of tx2 according to BIP68
368 sequence_value = 100 # 100 block relative locktime
369 370 tx3 = CTransaction()
371 tx3.version = 2
372 tx3.vin = [CTxIn(COutPoint(tx2.txid_int, 0), nSequence=sequence_value)]
373 tx3.wit.vtxinwit = [CTxInWitness()]
374 tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
375 tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
376 377 assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx3.serialize().hex())
378 379 # make a block that violates bip68; ensure that the tip updates
380 block = create_block(tmpl=self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS), txlist=[tx1, tx2, tx3])
381 add_witness_commitment(block)
382 block.solve()
383 384 assert_equal(None, self.nodes[0].submitblock(block.serialize().hex()))
385 assert_equal(self.nodes[0].getbestblockhash(), block.hash_hex)
386 387 def activateCSV(self):
388 # activation should happen at block height 432 (3 periods)
389 # getblockchaininfo will show CSV as active at block 431 (144 * 3 -1) since it's returning whether CSV is active for the next block.
390 min_activation_height = 432
391 height = self.nodes[0].getblockcount()
392 assert_greater_than(min_activation_height - height, 2)
393 self.generate(self.wallet, min_activation_height - height - 2, sync_fun=self.no_op)
394 assert not softfork_active(self.nodes[0], 'csv')
395 self.generate(self.wallet, 1, sync_fun=self.no_op)
396 assert softfork_active(self.nodes[0], 'csv')
397 self.sync_blocks()
398 399 # Use self.nodes[1] to test that version 2 transactions are standard.
400 def test_version2_relay(self):
401 mini_wallet = MiniWallet(self.nodes[1])
402 mini_wallet.send_self_transfer(from_node=self.nodes[1], version=2)
403 404 405 if __name__ == '__main__':
406 BIP68Test(__file__).main()
407