Skip to content

Commit d4f0d87

Browse files
committed
[qa] Rewrite BIP65 functional tests
After 122786d, BIP65 activates at a particular height (without regard to version numbers of blocks below that height). Rewrite the BIP65 functional tests to take this into account, and add a test case that exercises OP_CHECKLOCKTIMEVERIFY in a block where the soft-fork is active. Also moves the bip65 functional test out of the extended test suite.
1 parent d4e551a commit d4f0d87

File tree

3 files changed

+116
-210
lines changed

3 files changed

+116
-210
lines changed

test/functional/bip65-cltv-p2p.py

Lines changed: 115 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -4,173 +4,162 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test BIP65 (CHECKLOCKTIMEVERIFY).
66
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.
179
"""
1810

19-
from test_framework.test_framework import ComparisonTestFramework
11+
from test_framework.test_framework import BitcoinTestFramework
2012
from test_framework.util import *
21-
from test_framework.mininode import CTransaction, NetworkThread
13+
from test_framework.mininode import *
2214
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
2516
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
2724

2825
def cltv_invalidate(tx):
2926
'''Modify the signature in vin 0 of the tx to fail CLTV
3027
3128
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).
3232
'''
3333
tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP] +
3434
list(CScript(tx.vin[0].scriptSig)))
3535

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):
3863

3964
def __init__(self):
4065
super().__init__()
4166
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
4369

4470
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+
4776
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)
6683
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)
10189
cltv_invalidate(spendtx)
10290
spendtx.rehash()
10391

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
10696
block.vtx.append(spendtx)
10797
block.hashMerkleRoot = block.calc_merkle_root()
108-
block.rehash()
10998
block.solve()
11099

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)
131107
block.nVersion = 3
132-
block.rehash()
133108
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)
138111

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")
141120
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)
155124
cltv_invalidate(spendtx)
156125
spendtx.rehash()
157126

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.
160134
block.vtx.append(spendtx)
161135
block.hashMerkleRoot = block.calc_merkle_root()
162-
block.rehash()
163136
block.solve()
164-
self.last_block_time += 1
165-
yield TestInstance([[block, False]])
166137

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()
171158
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+
174163

175164
if __name__ == '__main__':
176165
BIP65Test().main()

test/functional/bip65-cltv.py

Lines changed: 0 additions & 82 deletions
This file was deleted.

test/functional/test_runner.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
'listsinceblock.py',
114114
'p2p-leaktests.py',
115115
'wallet-encryption.py',
116+
'bip65-cltv-p2p.py',
116117
'uptime.py',
117118
]
118119

@@ -136,8 +137,6 @@
136137
'rpcbind_test.py',
137138
# vv Tests less than 30s vv
138139
'assumevalid.py',
139-
'bip65-cltv.py',
140-
'bip65-cltv-p2p.py',
141140
'bipdersig-p2p.py',
142141
'bipdersig.py',
143142
'example_test.py',

0 commit comments

Comments
 (0)