Skip to content

Commit 6fc9285

Browse files
Merge bitcoin#10695: [qa] Rewrite BIP65/BIP66 functional tests
4ccc12a [qa] Rewrite BIP66 functional tests (Suhas Daftuar) d4f0d87 [qa] Rewrite BIP65 functional tests (Suhas Daftuar) Pull request description: After 122786d, BIP65 and BIP66 activate at particular fixed heights (without regard to version numbers of blocks below those heights). Rewrite the functional tests to take this into account, and remove two tests that weren't really testing anything. Moves the rewritten functional tests out of the extended test suite, so that they run in travis regularly. Note: I discovered that the ComparisonTestFramework (which the original versions of these p2p tests were written is, has a bug that caused them to not catch obvious errors, eg if you just comment out setting the script flags for these softforks in ConnectBlock, the versions of these tests in master do not fail(!) -- will separately PR a fix for the comparison test framework).
1 parent eda5dac commit 6fc9285

File tree

5 files changed

+213
-413
lines changed

5 files changed

+213
-413
lines changed

test/functional/bip65-cltv-p2p.py

Lines changed: 115 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -4,172 +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
2617

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
24+
2725
def cltv_invalidate(tx):
2826
'''Modify the signature in vin 0 of the tx to fail CLTV
2927
3028
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).
3132
'''
3233
tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP] +
3334
list(CScript(tx.vin[0].scriptSig)))
3435

35-
36-
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):
3763

3864
def __init__(self):
3965
super().__init__()
4066
self.num_nodes = 1
41-
self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=3', '-dip3params=9000:9000']]
67+
self.extra_args = [['-promiscuousmempoolflags=1', '-whitelist=127.0.0.1']]
68+
self.setup_clean_chain = True
4269

4370
def run_test(self):
44-
test = TestManager(self, self.options.tmpdir)
45-
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+
4676
NetworkThread().start() # Start up network handling in another thread
47-
test.run()
48-
49-
def create_transaction(self, node, coinbase, to_address, amount):
50-
from_txid = node.getblock(coinbase)['tx'][0]
51-
inputs = [{ "txid" : from_txid, "vout" : 0}]
52-
outputs = { to_address : amount }
53-
rawtx = node.createrawtransaction(inputs, outputs)
54-
signresult = node.signrawtransaction(rawtx)
55-
tx = CTransaction()
56-
f = BytesIO(hex_str_to_bytes(signresult['hex']))
57-
tx.deserialize(f)
58-
return tx
59-
60-
def get_tests(self):
61-
62-
self.coinbase_blocks = self.nodes[0].generate(2)
63-
height = 3 # height of the next block to build
64-
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)
6583
self.nodeaddress = self.nodes[0].getnewaddress()
66-
self.last_block_time = self.mocktime + 1
67-
68-
''' 398 more version 3 blocks '''
69-
test_blocks = []
70-
for i in range(398):
71-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
72-
block.nVersion = 3
73-
block.rehash()
74-
block.solve()
75-
test_blocks.append([block, True])
76-
self.last_block_time += 1
77-
self.tip = block.sha256
78-
height += 1
79-
yield TestInstance(test_blocks, sync_every_block=False)
80-
81-
''' Mine 749 version 4 blocks '''
82-
test_blocks = []
83-
for i in range(749):
84-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
85-
block.nVersion = 4
86-
block.rehash()
87-
block.solve()
88-
test_blocks.append([block, True])
89-
self.last_block_time += 1
90-
self.tip = block.sha256
91-
height += 1
92-
yield TestInstance(test_blocks, sync_every_block=False)
93-
94-
'''
95-
Check that the new CLTV rules are not enforced in the 750th
96-
version 3 block.
97-
'''
98-
spendtx = self.create_transaction(self.nodes[0],
99-
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)
10089
cltv_invalidate(spendtx)
10190
spendtx.rehash()
10291

103-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
104-
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
10596
block.vtx.append(spendtx)
10697
block.hashMerkleRoot = block.calc_merkle_root()
107-
block.rehash()
10898
block.solve()
10999

110-
self.last_block_time += 1
111-
self.tip = block.sha256
112-
height += 1
113-
yield TestInstance([[block, True]])
114-
115-
''' Mine 199 new version blocks on last valid tip '''
116-
test_blocks = []
117-
for i in range(199):
118-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
119-
block.nVersion = 4
120-
block.rehash()
121-
block.solve()
122-
test_blocks.append([block, True])
123-
self.last_block_time += 1
124-
self.tip = block.sha256
125-
height += 1
126-
yield TestInstance(test_blocks, sync_every_block=False)
127-
128-
''' Mine 1 old version block '''
129-
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)
130107
block.nVersion = 3
131-
block.rehash()
132108
block.solve()
133-
self.last_block_time += 1
134-
self.tip = block.sha256
135-
height += 1
136-
yield TestInstance([[block, True]])
109+
node0.send_and_ping(msg_block(block))
110+
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
137111

138-
''' Mine 1 new version block '''
139-
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")
140120
block.nVersion = 4
141-
block.rehash()
142-
block.solve()
143-
self.last_block_time += 1
144-
self.tip = block.sha256
145-
height += 1
146-
yield TestInstance([[block, True]])
147-
148-
'''
149-
Check that the new CLTV rules are enforced in the 951st version 4
150-
block.
151-
'''
152-
spendtx = self.create_transaction(self.nodes[0],
153-
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)
154124
cltv_invalidate(spendtx)
155125
spendtx.rehash()
156126

157-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
158-
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.
159134
block.vtx.append(spendtx)
160135
block.hashMerkleRoot = block.calc_merkle_root()
161-
block.rehash()
162136
block.solve()
163-
self.last_block_time += 1
164-
yield TestInstance([[block, False]])
165137

166-
''' Mine 1 old version block, should be invalid '''
167-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
168-
block.nVersion = 3
169-
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()
170158
block.solve()
171-
self.last_block_time += 1
172-
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+
173163

174164
if __name__ == '__main__':
175165
BIP65Test().main()

test/functional/bip65-cltv.py

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

0 commit comments

Comments
 (0)