|
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