feature_bip68_sequence.py
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()