|
4 | 4 | # file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
5 | 5 | """Test BIP65 (CHECKLOCKTIMEVERIFY).
|
6 | 6 |
|
7 |
| -Connect to a single node. |
8 |
| -Mine 2 (version 3) blocks (save the coinbases for later). |
9 |
| -Generate 98 more version 3 blocks, verify the node accepts. |
10 |
| -Mine 749 version 4 blocks, verify the node accepts. |
11 |
| -Check that the new CLTV rules are not enforced on the 750th version 4 block. |
12 |
| -Check that the new CLTV rules are enforced on the 751st version 4 block. |
13 |
| -Mine 199 new version blocks. |
14 |
| -Mine 1 old-version block. |
15 |
| -Mine 1 new version block. |
16 |
| -Mine 1 old version block, see that the node rejects. |
| 7 | +Test that the CHECKLOCKTIMEVERIFY soft-fork activates at (regtest) block height |
| 8 | +1351. |
17 | 9 | """
|
18 | 10 |
|
19 |
| -from test_framework.test_framework import ComparisonTestFramework |
| 11 | +from test_framework.test_framework import BitcoinTestFramework |
20 | 12 | from test_framework.util import *
|
21 |
| -from test_framework.mininode import CTransaction, NetworkThread |
| 13 | +from test_framework.mininode import * |
22 | 14 | from test_framework.blocktools import create_coinbase, create_block
|
23 |
| -from test_framework.comptool import TestInstance, TestManager |
24 |
| -from test_framework.script import CScript, OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP |
| 15 | +from test_framework.script import CScript, OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP, CScriptNum |
25 | 16 | from io import BytesIO
|
26 |
| -import time |
| 17 | + |
| 18 | +CLTV_HEIGHT = 1351 |
| 19 | + |
| 20 | +# Reject codes that we might receive in this test |
| 21 | +REJECT_INVALID = 16 |
| 22 | +REJECT_OBSOLETE = 17 |
| 23 | +REJECT_NONSTANDARD = 64 |
27 | 24 |
|
28 | 25 | def cltv_invalidate(tx):
|
29 | 26 | '''Modify the signature in vin 0 of the tx to fail CLTV
|
30 | 27 |
|
31 | 28 | Prepends -1 CLTV DROP in the scriptSig itself.
|
| 29 | +
|
| 30 | + TODO: test more ways that transactions using CLTV could be invalid (eg |
| 31 | + locktime requirements fail, sequence time requirements fail, etc). |
32 | 32 | '''
|
33 | 33 | tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP] +
|
34 | 34 | list(CScript(tx.vin[0].scriptSig)))
|
35 | 35 |
|
36 |
| - |
37 |
| -class BIP65Test(ComparisonTestFramework): |
| 36 | +def cltv_validate(node, tx, height): |
| 37 | + '''Modify the signature in vin 0 of the tx to pass CLTV |
| 38 | + Prepends <height> CLTV DROP in the scriptSig, and sets |
| 39 | + the locktime to height''' |
| 40 | + tx.vin[0].nSequence = 0 |
| 41 | + tx.nLockTime = height |
| 42 | + |
| 43 | + # Need to re-sign, since nSequence and nLockTime changed |
| 44 | + signed_result = node.signrawtransaction(ToHex(tx)) |
| 45 | + new_tx = CTransaction() |
| 46 | + new_tx.deserialize(BytesIO(hex_str_to_bytes(signed_result['hex']))) |
| 47 | + |
| 48 | + new_tx.vin[0].scriptSig = CScript([CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP] + |
| 49 | + list(CScript(new_tx.vin[0].scriptSig))) |
| 50 | + return new_tx |
| 51 | + |
| 52 | +def create_transaction(node, coinbase, to_address, amount): |
| 53 | + from_txid = node.getblock(coinbase)['tx'][0] |
| 54 | + inputs = [{ "txid" : from_txid, "vout" : 0}] |
| 55 | + outputs = { to_address : amount } |
| 56 | + rawtx = node.createrawtransaction(inputs, outputs) |
| 57 | + signresult = node.signrawtransaction(rawtx) |
| 58 | + tx = CTransaction() |
| 59 | + tx.deserialize(BytesIO(hex_str_to_bytes(signresult['hex']))) |
| 60 | + return tx |
| 61 | + |
| 62 | +class BIP65Test(BitcoinTestFramework): |
38 | 63 |
|
39 | 64 | def __init__(self):
|
40 | 65 | super().__init__()
|
41 | 66 | self.num_nodes = 1
|
42 |
| - self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=3']] |
| 67 | + self.extra_args = [['-promiscuousmempoolflags=1', '-whitelist=127.0.0.1']] |
| 68 | + self.setup_clean_chain = True |
43 | 69 |
|
44 | 70 | def run_test(self):
|
45 |
| - test = TestManager(self, self.options.tmpdir) |
46 |
| - test.add_all_connections(self.nodes) |
| 71 | + node0 = NodeConnCB() |
| 72 | + connections = [] |
| 73 | + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0)) |
| 74 | + node0.add_connection(connections[0]) |
| 75 | + |
47 | 76 | NetworkThread().start() # Start up network handling in another thread
|
48 |
| - test.run() |
49 |
| - |
50 |
| - def create_transaction(self, node, coinbase, to_address, amount): |
51 |
| - from_txid = node.getblock(coinbase)['tx'][0] |
52 |
| - inputs = [{ "txid" : from_txid, "vout" : 0}] |
53 |
| - outputs = { to_address : amount } |
54 |
| - rawtx = node.createrawtransaction(inputs, outputs) |
55 |
| - signresult = node.signrawtransaction(rawtx) |
56 |
| - tx = CTransaction() |
57 |
| - f = BytesIO(hex_str_to_bytes(signresult['hex'])) |
58 |
| - tx.deserialize(f) |
59 |
| - return tx |
60 |
| - |
61 |
| - def get_tests(self): |
62 |
| - |
63 |
| - self.coinbase_blocks = self.nodes[0].generate(2) |
64 |
| - height = 3 # height of the next block to build |
65 |
| - self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0) |
| 77 | + |
| 78 | + # wait_for_verack ensures that the P2P connection is fully up. |
| 79 | + node0.wait_for_verack() |
| 80 | + |
| 81 | + self.log.info("Mining %d blocks", CLTV_HEIGHT - 2) |
| 82 | + self.coinbase_blocks = self.nodes[0].generate(CLTV_HEIGHT - 2) |
66 | 83 | self.nodeaddress = self.nodes[0].getnewaddress()
|
67 |
| - self.last_block_time = int(time.time()) |
68 |
| - |
69 |
| - ''' 398 more version 3 blocks ''' |
70 |
| - test_blocks = [] |
71 |
| - for i in range(398): |
72 |
| - block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) |
73 |
| - block.nVersion = 3 |
74 |
| - block.rehash() |
75 |
| - block.solve() |
76 |
| - test_blocks.append([block, True]) |
77 |
| - self.last_block_time += 1 |
78 |
| - self.tip = block.sha256 |
79 |
| - height += 1 |
80 |
| - yield TestInstance(test_blocks, sync_every_block=False) |
81 |
| - |
82 |
| - ''' Mine 749 version 4 blocks ''' |
83 |
| - test_blocks = [] |
84 |
| - for i in range(749): |
85 |
| - block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) |
86 |
| - block.nVersion = 4 |
87 |
| - block.rehash() |
88 |
| - block.solve() |
89 |
| - test_blocks.append([block, True]) |
90 |
| - self.last_block_time += 1 |
91 |
| - self.tip = block.sha256 |
92 |
| - height += 1 |
93 |
| - yield TestInstance(test_blocks, sync_every_block=False) |
94 |
| - |
95 |
| - ''' |
96 |
| - Check that the new CLTV rules are not enforced in the 750th |
97 |
| - version 3 block. |
98 |
| - ''' |
99 |
| - spendtx = self.create_transaction(self.nodes[0], |
100 |
| - self.coinbase_blocks[0], self.nodeaddress, 1.0) |
| 84 | + |
| 85 | + self.log.info("Test that an invalid-according-to-CLTV transaction can still appear in a block") |
| 86 | + |
| 87 | + spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[0], |
| 88 | + self.nodeaddress, 1.0) |
101 | 89 | cltv_invalidate(spendtx)
|
102 | 90 | spendtx.rehash()
|
103 | 91 |
|
104 |
| - block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) |
105 |
| - block.nVersion = 4 |
| 92 | + tip = self.nodes[0].getbestblockhash() |
| 93 | + block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 |
| 94 | + block = create_block(int(tip, 16), create_coinbase(CLTV_HEIGHT - 1), block_time) |
| 95 | + block.nVersion = 3 |
106 | 96 | block.vtx.append(spendtx)
|
107 | 97 | block.hashMerkleRoot = block.calc_merkle_root()
|
108 |
| - block.rehash() |
109 | 98 | block.solve()
|
110 | 99 |
|
111 |
| - self.last_block_time += 1 |
112 |
| - self.tip = block.sha256 |
113 |
| - height += 1 |
114 |
| - yield TestInstance([[block, True]]) |
115 |
| - |
116 |
| - ''' Mine 199 new version blocks on last valid tip ''' |
117 |
| - test_blocks = [] |
118 |
| - for i in range(199): |
119 |
| - block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) |
120 |
| - block.nVersion = 4 |
121 |
| - block.rehash() |
122 |
| - block.solve() |
123 |
| - test_blocks.append([block, True]) |
124 |
| - self.last_block_time += 1 |
125 |
| - self.tip = block.sha256 |
126 |
| - height += 1 |
127 |
| - yield TestInstance(test_blocks, sync_every_block=False) |
128 |
| - |
129 |
| - ''' Mine 1 old version block ''' |
130 |
| - block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) |
| 100 | + node0.send_and_ping(msg_block(block)) |
| 101 | + assert_equal(self.nodes[0].getbestblockhash(), block.hash) |
| 102 | + |
| 103 | + self.log.info("Test that blocks must now be at least version 4") |
| 104 | + tip = block.sha256 |
| 105 | + block_time += 1 |
| 106 | + block = create_block(tip, create_coinbase(CLTV_HEIGHT), block_time) |
131 | 107 | block.nVersion = 3
|
132 |
| - block.rehash() |
133 | 108 | block.solve()
|
134 |
| - self.last_block_time += 1 |
135 |
| - self.tip = block.sha256 |
136 |
| - height += 1 |
137 |
| - yield TestInstance([[block, True]]) |
| 109 | + node0.send_and_ping(msg_block(block)) |
| 110 | + assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) |
138 | 111 |
|
139 |
| - ''' Mine 1 new version block ''' |
140 |
| - block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) |
| 112 | + assert wait_until(lambda: "reject" in node0.last_message.keys()) |
| 113 | + with mininode_lock: |
| 114 | + assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE) |
| 115 | + assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000003)') |
| 116 | + assert_equal(node0.last_message["reject"].data, block.sha256) |
| 117 | + del node0.last_message["reject"] |
| 118 | + |
| 119 | + self.log.info("Test that invalid-according-to-cltv transactions cannot appear in a block") |
141 | 120 | block.nVersion = 4
|
142 |
| - block.rehash() |
143 |
| - block.solve() |
144 |
| - self.last_block_time += 1 |
145 |
| - self.tip = block.sha256 |
146 |
| - height += 1 |
147 |
| - yield TestInstance([[block, True]]) |
148 |
| - |
149 |
| - ''' |
150 |
| - Check that the new CLTV rules are enforced in the 951st version 4 |
151 |
| - block. |
152 |
| - ''' |
153 |
| - spendtx = self.create_transaction(self.nodes[0], |
154 |
| - self.coinbase_blocks[1], self.nodeaddress, 1.0) |
| 121 | + |
| 122 | + spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[1], |
| 123 | + self.nodeaddress, 1.0) |
155 | 124 | cltv_invalidate(spendtx)
|
156 | 125 | spendtx.rehash()
|
157 | 126 |
|
158 |
| - block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) |
159 |
| - block.nVersion = 4 |
| 127 | + # First we show that this tx is valid except for CLTV by getting it |
| 128 | + # accepted to the mempool (which we can achieve with |
| 129 | + # -promiscuousmempoolflags). |
| 130 | + node0.send_and_ping(msg_tx(spendtx)) |
| 131 | + assert spendtx.hash in self.nodes[0].getrawmempool() |
| 132 | + |
| 133 | + # Now we verify that a block with this transaction is invalid. |
160 | 134 | block.vtx.append(spendtx)
|
161 | 135 | block.hashMerkleRoot = block.calc_merkle_root()
|
162 |
| - block.rehash() |
163 | 136 | block.solve()
|
164 |
| - self.last_block_time += 1 |
165 |
| - yield TestInstance([[block, False]]) |
166 | 137 |
|
167 |
| - ''' Mine 1 old version block, should be invalid ''' |
168 |
| - block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1) |
169 |
| - block.nVersion = 3 |
170 |
| - block.rehash() |
| 138 | + node0.send_and_ping(msg_block(block)) |
| 139 | + assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) |
| 140 | + |
| 141 | + assert wait_until (lambda: "reject" in node0.last_message.keys()) |
| 142 | + with mininode_lock: |
| 143 | + assert node0.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD] |
| 144 | + assert_equal(node0.last_message["reject"].data, block.sha256) |
| 145 | + if node0.last_message["reject"].code == REJECT_INVALID: |
| 146 | + # Generic rejection when a block is invalid |
| 147 | + assert_equal(node0.last_message["reject"].reason, b'block-validation-failed') |
| 148 | + else: |
| 149 | + assert b'Negative locktime' in node0.last_message["reject"].reason |
| 150 | + |
| 151 | + self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted") |
| 152 | + spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1) |
| 153 | + spendtx.rehash() |
| 154 | + |
| 155 | + block.vtx.pop(1) |
| 156 | + block.vtx.append(spendtx) |
| 157 | + block.hashMerkleRoot = block.calc_merkle_root() |
171 | 158 | block.solve()
|
172 |
| - self.last_block_time += 1 |
173 |
| - yield TestInstance([[block, False]]) |
| 159 | + |
| 160 | + node0.send_and_ping(msg_block(block)) |
| 161 | + assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256) |
| 162 | + |
174 | 163 |
|
175 | 164 | if __name__ == '__main__':
|
176 | 165 | BIP65Test().main()
|
0 commit comments