Dash Core  0.12.2.1
P2P Digital Currency
bip68-sequence.py
Go to the documentation of this file.
1 #!/usr/bin/env python2
2 # Copyright (c) 2014-2015 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 
6 #
7 # Test BIP68 implementation
8 #
9 
10 from test_framework.test_framework import BitcoinTestFramework
11 from test_framework.util import *
12 from test_framework.script import *
13 from test_framework.mininode import *
14 from test_framework.blocktools import *
15 
16 SEQUENCE_LOCKTIME_DISABLE_FLAG = (1<<31)
17 SEQUENCE_LOCKTIME_TYPE_FLAG = (1<<22) # this means use time (0 means height)
18 SEQUENCE_LOCKTIME_GRANULARITY = 9 # this is a bit-shift
19 SEQUENCE_LOCKTIME_MASK = 0x0000ffff
20 
21 # RPC error for non-BIP68 final transactions
22 NOT_FINAL_ERROR = "64: non-BIP68-final"
23 
25 
26  def setup_network(self):
27  self.nodes = []
28  self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-blockprioritysize=0"]))
29  self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-blockprioritysize=0", "-acceptnonstdtxn=0"]))
30  self.is_network_split = False
31  self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
32  connect_nodes(self.nodes[0], 1)
33 
34  def run_test(self):
35  # Generate some coins
36  self.nodes[0].generate(110)
37 
38  print "Running test disable flag"
39  self.test_disable_flag()
40 
41  print "Running test sequence-lock-confirmed-inputs"
43 
44  print "Running test sequence-lock-unconfirmed-inputs"
46 
47  print "Running test BIP68 not consensus before versionbits activation"
49 
50  print "Verifying nVersion=2 transactions aren't standard"
51  self.test_version2_relay(before_activation=True)
52 
53  print "Activating BIP68 (and 112/113)"
54  self.activateCSV()
55 
56  print "Verifying nVersion=2 transactions are now standard"
57  self.test_version2_relay(before_activation=False)
58 
59  print "Passed\n"
60 
61  # Test that BIP68 is not in effect if tx version is 1, or if
62  # the first sequence bit is set.
63  def test_disable_flag(self):
64  # Create some unconfirmed inputs
65  new_addr = self.nodes[0].getnewaddress()
66  self.nodes[0].sendtoaddress(new_addr, 2) # send 2 BTC
67 
68  utxos = self.nodes[0].listunspent(0, 0)
69  assert(len(utxos) > 0)
70 
71  utxo = utxos[0]
72 
73  tx1 = CTransaction()
74  value = int(satoshi_round(utxo["amount"] - self.relayfee)*COIN)
75 
76  # Check that the disable flag disables relative locktime.
77  # If sequence locks were used, this would require 1 block for the
78  # input to mature.
79  sequence_value = SEQUENCE_LOCKTIME_DISABLE_FLAG | 1
80  tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)]
81  tx1.vout = [CTxOut(value, CScript([b'a']))]
82 
83  tx1_signed = self.nodes[0].signrawtransaction(ToHex(tx1))["hex"]
84  tx1_id = self.nodes[0].sendrawtransaction(tx1_signed)
85  tx1_id = int(tx1_id, 16)
86 
87  # This transaction will enable sequence-locks, so this transaction should
88  # fail
89  tx2 = CTransaction()
90  tx2.nVersion = 2
91  sequence_value = sequence_value & 0x7fffffff
92  tx2.vin = [CTxIn(COutPoint(tx1_id, 0), nSequence=sequence_value)]
93  tx2.vout = [CTxOut(int(value-self.relayfee*COIN), CScript([b'a']))]
94  tx2.rehash()
95 
96  try:
97  self.nodes[0].sendrawtransaction(ToHex(tx2))
98  except JSONRPCException as exp:
99  assert_equal(exp.error["message"], NOT_FINAL_ERROR)
100  else:
101  assert(False)
102 
103  # Setting the version back down to 1 should disable the sequence lock,
104  # so this should be accepted.
105  tx2.nVersion = 1
106 
107  self.nodes[0].sendrawtransaction(ToHex(tx2))
108 
109  # Calculate the median time past of a prior block ("confirmations" before
110  # the current tip).
111  def get_median_time_past(self, confirmations):
112  block_hash = self.nodes[0].getblockhash(self.nodes[0].getblockcount()-confirmations)
113  return self.nodes[0].getblockheader(block_hash)["mediantime"]
114 
115  # Test that sequence locks are respected for transactions spending confirmed inputs.
117  # Create lots of confirmed utxos, and use them to generate lots of random
118  # transactions.
119  max_outputs = 50
120  addresses = []
121  while len(addresses) < max_outputs:
122  addresses.append(self.nodes[0].getnewaddress())
123  while len(self.nodes[0].listunspent()) < 200:
124  import random
125  random.shuffle(addresses)
126  num_outputs = random.randint(1, max_outputs)
127  outputs = {}
128  for i in xrange(num_outputs):
129  outputs[addresses[i]] = random.randint(1, 20)*0.01
130  self.nodes[0].sendmany("", outputs)
131  self.nodes[0].generate(1)
132 
133  utxos = self.nodes[0].listunspent()
134 
135  # Try creating a lot of random transactions.
136  # Each time, choose a random number of inputs, and randomly set
137  # some of those inputs to be sequence locked (and randomly choose
138  # between height/time locking). Small random chance of making the locks
139  # all pass.
140  for i in xrange(400):
141  # Randomly choose up to 10 inputs
142  num_inputs = random.randint(1, 10)
143  random.shuffle(utxos)
144 
145  # Track whether any sequence locks used should fail
146  should_pass = True
147 
148  # Track whether this transaction was built with sequence locks
149  using_sequence_locks = False
150 
151  tx = CTransaction()
152  tx.nVersion = 2
153  value = 0
154  for j in xrange(num_inputs):
155  sequence_value = 0xfffffffe # this disables sequence locks
156 
157  # 50% chance we enable sequence locks
158  if random.randint(0,1):
159  using_sequence_locks = True
160 
161  # 10% of the time, make the input sequence value pass
162  input_will_pass = (random.randint(1,10) == 1)
163  sequence_value = utxos[j]["confirmations"]
164  if not input_will_pass:
165  sequence_value += 1
166  should_pass = False
167 
168  # Figure out what the median-time-past was for the confirmed input
169  # Note that if an input has N confirmations, we're going back N blocks
170  # from the tip so that we're looking up MTP of the block
171  # PRIOR to the one the input appears in, as per the BIP68 spec.
172  orig_time = self.get_median_time_past(utxos[j]["confirmations"])
173  cur_time = self.get_median_time_past(0) # MTP of the tip
174 
175  # can only timelock this input if it's not too old -- otherwise use height
176  can_time_lock = True
177  if ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY) >= SEQUENCE_LOCKTIME_MASK:
178  can_time_lock = False
179 
180  # if time-lockable, then 50% chance we make this a time lock
181  if random.randint(0,1) and can_time_lock:
182  # Find first time-lock value that fails, or latest one that succeeds
183  time_delta = sequence_value << SEQUENCE_LOCKTIME_GRANULARITY
184  if input_will_pass and time_delta > cur_time - orig_time:
185  sequence_value = ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY)
186  elif (not input_will_pass and time_delta <= cur_time - orig_time):
187  sequence_value = ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY)+1
188  sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG
189  tx.vin.append(CTxIn(COutPoint(int(utxos[j]["txid"], 16), utxos[j]["vout"]), nSequence=sequence_value))
190  value += utxos[j]["amount"]*COIN
191  # Overestimate the size of the tx - signatures should be less than 120 bytes, and leave 50 for the output
192  tx_size = len(ToHex(tx))//2 + 120*num_inputs + 50
193  tx.vout.append(CTxOut(int(value-self.relayfee*tx_size*COIN/1000), CScript([b'a'])))
194  rawtx = self.nodes[0].signrawtransaction(ToHex(tx))["hex"]
195 
196  try:
197  self.nodes[0].sendrawtransaction(rawtx)
198  except JSONRPCException as exp:
199  assert(not should_pass and using_sequence_locks)
200  assert_equal(exp.error["message"], NOT_FINAL_ERROR)
201  else:
202  assert(should_pass or not using_sequence_locks)
203  # Recalculate utxos if we successfully sent the transaction
204  utxos = self.nodes[0].listunspent()
205 
206  # Test that sequence locks on unconfirmed inputs must have nSequence
207  # height or time of 0 to be accepted.
208  # Then test that BIP68-invalid transactions are removed from the mempool
209  # after a reorg.
211  # Store height so we can easily reset the chain at the end of the test
212  cur_height = self.nodes[0].getblockcount()
213 
214  # Create a mempool tx.
215  txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)
216  tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
217  tx1.rehash()
218 
219  # Anyone-can-spend mempool tx.
220  # Sequence lock of 0 should pass.
221  tx2 = CTransaction()
222  tx2.nVersion = 2
223  tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)]
224  tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))]
225  tx2_raw = self.nodes[0].signrawtransaction(ToHex(tx2))["hex"]
226  tx2 = FromHex(tx2, tx2_raw)
227  tx2.rehash()
228 
229  self.nodes[0].sendrawtransaction(tx2_raw)
230 
231  # Create a spend of the 0th output of orig_tx with a sequence lock
232  # of 1, and test what happens when submitting.
233  # orig_tx.vout[0] must be an anyone-can-spend output
234  def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock):
235  sequence_value = 1
236  if not use_height_lock:
237  sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG
238 
239  tx = CTransaction()
240  tx.nVersion = 2
241  tx.vin = [CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value)]
242  tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee*COIN), CScript([b'a']))]
243  tx.rehash()
244 
245  try:
246  node.sendrawtransaction(ToHex(tx))
247  except JSONRPCException as exp:
248  assert_equal(exp.error["message"], NOT_FINAL_ERROR)
249  assert(orig_tx.hash in node.getrawmempool())
250  else:
251  # orig_tx must not be in mempool
252  assert(orig_tx.hash not in node.getrawmempool())
253  return tx
254 
255  test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=True)
256  test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False)
257 
258  # Now mine some blocks, but make sure tx2 doesn't get mined.
259  # Use prioritisetransaction to lower the effective feerate to 0
260  self.nodes[0].prioritisetransaction(tx2.hash, -1e15, int(-self.relayfee*COIN))
261  cur_time = int(time.time())
262  for i in xrange(10):
263  self.nodes[0].setmocktime(cur_time + 600)
264  self.nodes[0].generate(1)
265  cur_time += 600
266 
267  assert(tx2.hash in self.nodes[0].getrawmempool())
268 
269  test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=True)
270  test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False)
271 
272  # Mine tx2, and then try again
273  self.nodes[0].prioritisetransaction(tx2.hash, 1e15, int(self.relayfee*COIN))
274 
275  # Advance the time on the node so that we can test timelocks
276  self.nodes[0].setmocktime(cur_time+600)
277  self.nodes[0].generate(1)
278  assert(tx2.hash not in self.nodes[0].getrawmempool())
279 
280  # Now that tx2 is not in the mempool, a sequence locked spend should
281  # succeed
282  tx3 = test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False)
283  assert(tx3.hash in self.nodes[0].getrawmempool())
284 
285  self.nodes[0].generate(1)
286  assert(tx3.hash not in self.nodes[0].getrawmempool())
287 
288  # One more test, this time using height locks
289  tx4 = test_nonzero_locks(tx3, self.nodes[0], self.relayfee, use_height_lock=True)
290  assert(tx4.hash in self.nodes[0].getrawmempool())
291 
292  # Now try combining confirmed and unconfirmed inputs
293  tx5 = test_nonzero_locks(tx4, self.nodes[0], self.relayfee, use_height_lock=True)
294  assert(tx5.hash not in self.nodes[0].getrawmempool())
295 
296  utxos = self.nodes[0].listunspent()
297  tx5.vin.append(CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]), nSequence=1))
298  tx5.vout[0].nValue += int(utxos[0]["amount"]*COIN)
299  raw_tx5 = self.nodes[0].signrawtransaction(ToHex(tx5))["hex"]
300 
301  try:
302  self.nodes[0].sendrawtransaction(raw_tx5)
303  except JSONRPCException as exp:
304  assert_equal(exp.error["message"], NOT_FINAL_ERROR)
305  else:
306  assert(False)
307 
308  # Test mempool-BIP68 consistency after reorg
309  #
310  # State of the transactions in the last blocks:
311  # ... -> [ tx2 ] -> [ tx3 ]
312  # tip-1 tip
313  # And currently tx4 is in the mempool.
314  #
315  # If we invalidate the tip, tx3 should get added to the mempool, causing
316  # tx4 to be removed (fails sequence-lock).
317  self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
318  assert(tx4.hash not in self.nodes[0].getrawmempool())
319  assert(tx3.hash in self.nodes[0].getrawmempool())
320 
321  # Now mine 2 empty blocks to reorg out the current tip (labeled tip-1 in
322  # diagram above).
323  # This would cause tx2 to be added back to the mempool, which in turn causes
324  # tx3 to be removed.
325  tip = int(self.nodes[0].getblockhash(self.nodes[0].getblockcount()-1), 16)
326  height = self.nodes[0].getblockcount()
327  for i in xrange(2):
328  block = create_block(tip, create_coinbase(height), cur_time)
329  block.nVersion = 3
330  block.rehash()
331  block.solve()
332  tip = block.sha256
333  height += 1
334  self.nodes[0].submitblock(ToHex(block))
335  cur_time += 1
336 
337  mempool = self.nodes[0].getrawmempool()
338  assert(tx3.hash not in mempool)
339  assert(tx2.hash in mempool)
340 
341  # Reset the chain and get rid of the mocktimed-blocks
342  self.nodes[0].setmocktime(0)
343  self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height+1))
344  self.nodes[0].generate(10)
345 
346  # Make sure that BIP68 isn't being used to validate blocks, prior to
347  # versionbits activation. If more blocks are mined prior to this test
348  # being run, then it's possible the test has activated the soft fork, and
349  # this test should be moved to run earlier, or deleted.
351  assert(get_bip9_status(self.nodes[0], 'csv')['status'] != 'active')
352  txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)
353 
354  tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
355  tx1.rehash()
356 
357  # Make an anyone-can-spend transaction
358  tx2 = CTransaction()
359  tx2.nVersion = 1
360  tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)]
361  tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))]
362 
363  # sign tx2
364  tx2_raw = self.nodes[0].signrawtransaction(ToHex(tx2))["hex"]
365  tx2 = FromHex(tx2, tx2_raw)
366  tx2.rehash()
367 
368  self.nodes[0].sendrawtransaction(ToHex(tx2))
369 
370  # Now make an invalid spend of tx2 according to BIP68
371  sequence_value = 100 # 100 block relative locktime
372 
373  tx3 = CTransaction()
374  tx3.nVersion = 2
375  tx3.vin = [CTxIn(COutPoint(tx2.sha256, 0), nSequence=sequence_value)]
376  tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))]
377  tx3.rehash()
378 
379  try:
380  self.nodes[0].sendrawtransaction(ToHex(tx3))
381  except JSONRPCException as exp:
382  assert_equal(exp.error["message"], NOT_FINAL_ERROR)
383  else:
384  assert(False)
385 
386  # make a block that violates bip68; ensure that the tip updates
387  tip = int(self.nodes[0].getbestblockhash(), 16)
388  block = create_block(tip, create_coinbase(self.nodes[0].getblockcount()+1))
389  block.nVersion = 3
390  block.vtx.extend([tx1, tx2, tx3])
391  block.hashMerkleRoot = block.calc_merkle_root()
392  block.rehash()
393  block.solve()
394 
395  self.nodes[0].submitblock(ToHex(block))
396  assert_equal(self.nodes[0].getbestblockhash(), block.hash)
397 
398  def activateCSV(self):
399  # activation should happen at block height 432 (3 periods)
400  min_activation_height = 432
401  height = self.nodes[0].getblockcount()
402  assert(height < 432)
403  self.nodes[0].generate(432-height)
404  assert(get_bip9_status(self.nodes[0], 'csv')['status'] == 'active')
405  sync_blocks(self.nodes)
406 
407  # Use self.nodes[1] to test standardness relay policy
408  def test_version2_relay(self, before_activation):
409  inputs = [ ]
410  outputs = { self.nodes[1].getnewaddress() : 1.0 }
411  rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
412  rawtxfund = self.nodes[1].fundrawtransaction(rawtx)['hex']
413  tx = FromHex(CTransaction(), rawtxfund)
414  tx.nVersion = 2
415  tx_signed = self.nodes[1].signrawtransaction(ToHex(tx))["hex"]
416  try:
417  tx_id = self.nodes[1].sendrawtransaction(tx_signed)
418  assert(before_activation == False)
419  except:
420  assert(before_activation)
421 
422 
423 if __name__ == '__main__':
424  BIP68Test().main()
UniValue prioritisetransaction(const UniValue &params, bool fHelp)
Definition: mining.cpp:279
def get_bip9_status(node, key)
Definition: util.py:606
def create_block(hashprev, coinbase, nTime=None)
Definition: blocktools.py:11
UniValue getblockheader(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:336
def FromHex(obj, hex_string)
Definition: mininode.py:242
UniValue getrawtransaction(const UniValue &params, bool fHelp)
UniValue sendmany(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:1024
UniValue listunspent(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:2533
UniValue sendtoaddress(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:409
UniValue submitblock(const UniValue &params, bool fHelp)
Definition: mining.cpp:755
def connect_nodes(from_connection, node_num)
Definition: util.py:343
UniValue getnewaddress(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:113
UniValue getblockhash(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:311
def create_coinbase(height, pubkey=None)
Definition: blocktools.py:43
UniValue getblockcount(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:131
UniValue signrawtransaction(const UniValue &params, bool fHelp)
UniValue createrawtransaction(const UniValue &params, bool fHelp)
def satoshi_round(amount)
Definition: util.py:525
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None)
Definition: util.py:281
def test_version2_relay(self, before_activation)
UniValue generate(const UniValue &params, bool fHelp)
Definition: mining.cpp:122
UniValue sendrawtransaction(const UniValue &params, bool fHelp)
def sync_blocks(rpc_connections, wait=1)
Definition: util.py:117
def test_sequence_lock_unconfirmed_inputs(self)
UniValue getrawmempool(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:234
UniValue getbestblockhash(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:148
UniValue getnetworkinfo(const UniValue &params, bool fHelp)
Definition: net.cpp:392
def get_median_time_past(self, confirmations)
UniValue setmocktime(const UniValue &params, bool fHelp)
Definition: misc.cpp:498
def assert_equal(thing1, thing2)
Definition: util.py:461
def test_sequence_lock_confirmed_inputs(self)