Skip to content

Commit 4ccc12a

Browse files
committed
[qa] Rewrite BIP66 functional tests
Rewrite the BIP66 functional tests to reflect height-based activation, and move it out of the extended test suite. Remove the unnecessary bipdersig.py test
1 parent d4f0d87 commit 4ccc12a

File tree

3 files changed

+97
-204
lines changed

3 files changed

+97
-204
lines changed

test/functional/bipdersig-p2p.py

Lines changed: 96 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,24 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test BIP66 (DER SIG).
66
7-
Connect to a single node.
8-
Mine 2 (version 2) blocks (save the coinbases for later).
9-
Generate 98 more version 2 blocks, verify the node accepts.
10-
Mine 749 version 3 blocks, verify the node accepts.
11-
Check that the new DERSIG rules are not enforced on the 750th version 3 block.
12-
Check that the new DERSIG rules are enforced on the 751st version 3 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 DERSIG soft-fork activates at (regtest) height 1251.
178
"""
189

19-
from test_framework.test_framework import ComparisonTestFramework
10+
from test_framework.test_framework import BitcoinTestFramework
2011
from test_framework.util import *
21-
from test_framework.mininode import CTransaction, NetworkThread
12+
from test_framework.mininode import *
2213
from test_framework.blocktools import create_coinbase, create_block
23-
from test_framework.comptool import TestInstance, TestManager
2414
from test_framework.script import CScript
2515
from io import BytesIO
26-
import time
2716

28-
# A canonical signature consists of:
17+
DERSIG_HEIGHT = 1251
18+
19+
# Reject codes that we might receive in this test
20+
REJECT_INVALID = 16
21+
REJECT_OBSOLETE = 17
22+
REJECT_NONSTANDARD = 64
23+
24+
# A canonical signature consists of:
2925
# <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
3026
def unDERify(tx):
3127
"""
@@ -40,143 +36,122 @@ def unDERify(tx):
4036
else:
4137
newscript.append(i)
4238
tx.vin[0].scriptSig = CScript(newscript)
43-
44-
class BIP66Test(ComparisonTestFramework):
39+
40+
def create_transaction(node, coinbase, to_address, amount):
41+
from_txid = node.getblock(coinbase)['tx'][0]
42+
inputs = [{ "txid" : from_txid, "vout" : 0}]
43+
outputs = { to_address : amount }
44+
rawtx = node.createrawtransaction(inputs, outputs)
45+
signresult = node.signrawtransaction(rawtx)
46+
tx = CTransaction()
47+
tx.deserialize(BytesIO(hex_str_to_bytes(signresult['hex'])))
48+
return tx
49+
50+
class BIP66Test(BitcoinTestFramework):
4551

4652
def __init__(self):
4753
super().__init__()
4854
self.num_nodes = 1
55+
self.extra_args = [['-promiscuousmempoolflags=1', '-whitelist=127.0.0.1']]
56+
self.setup_clean_chain = True
4957

5058
def run_test(self):
51-
test = TestManager(self, self.options.tmpdir)
52-
test.add_all_connections(self.nodes)
59+
node0 = NodeConnCB()
60+
connections = []
61+
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0))
62+
node0.add_connection(connections[0])
5363
NetworkThread().start() # Start up network handling in another thread
54-
test.run()
55-
56-
def create_transaction(self, node, coinbase, to_address, amount):
57-
from_txid = node.getblock(coinbase)['tx'][0]
58-
inputs = [{ "txid" : from_txid, "vout" : 0}]
59-
outputs = { to_address : amount }
60-
rawtx = node.createrawtransaction(inputs, outputs)
61-
signresult = node.signrawtransaction(rawtx)
62-
tx = CTransaction()
63-
f = BytesIO(hex_str_to_bytes(signresult['hex']))
64-
tx.deserialize(f)
65-
return tx
66-
67-
def get_tests(self):
68-
69-
self.coinbase_blocks = self.nodes[0].generate(2)
70-
height = 3 # height of the next block to build
71-
self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0)
64+
65+
# wait_for_verack ensures that the P2P connection is fully up.
66+
node0.wait_for_verack()
67+
68+
self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2)
69+
self.coinbase_blocks = self.nodes[0].generate(DERSIG_HEIGHT - 2)
7270
self.nodeaddress = self.nodes[0].getnewaddress()
73-
self.last_block_time = int(time.time())
74-
75-
''' 298 more version 2 blocks '''
76-
test_blocks = []
77-
for i in range(298):
78-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
79-
block.nVersion = 2
80-
block.rehash()
81-
block.solve()
82-
test_blocks.append([block, True])
83-
self.last_block_time += 1
84-
self.tip = block.sha256
85-
height += 1
86-
yield TestInstance(test_blocks, sync_every_block=False)
87-
88-
''' Mine 749 version 3 blocks '''
89-
test_blocks = []
90-
for i in range(749):
91-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
92-
block.nVersion = 3
93-
block.rehash()
94-
block.solve()
95-
test_blocks.append([block, True])
96-
self.last_block_time += 1
97-
self.tip = block.sha256
98-
height += 1
99-
yield TestInstance(test_blocks, sync_every_block=False)
100-
101-
'''
102-
Check that the new DERSIG rules are not enforced in the 750th
103-
version 3 block.
104-
'''
105-
spendtx = self.create_transaction(self.nodes[0],
106-
self.coinbase_blocks[0], self.nodeaddress, 1.0)
71+
72+
self.log.info("Test that a transaction with non-DER signature can still appear in a block")
73+
74+
spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[0],
75+
self.nodeaddress, 1.0)
10776
unDERify(spendtx)
10877
spendtx.rehash()
10978

110-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
111-
block.nVersion = 3
79+
tip = self.nodes[0].getbestblockhash()
80+
block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
81+
block = create_block(int(tip, 16), create_coinbase(DERSIG_HEIGHT - 1), block_time)
82+
block.nVersion = 2
11283
block.vtx.append(spendtx)
11384
block.hashMerkleRoot = block.calc_merkle_root()
11485
block.rehash()
11586
block.solve()
11687

117-
self.last_block_time += 1
118-
self.tip = block.sha256
119-
height += 1
120-
yield TestInstance([[block, True]])
121-
122-
''' Mine 199 new version blocks on last valid tip '''
123-
test_blocks = []
124-
for i in range(199):
125-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
126-
block.nVersion = 3
127-
block.rehash()
128-
block.solve()
129-
test_blocks.append([block, True])
130-
self.last_block_time += 1
131-
self.tip = block.sha256
132-
height += 1
133-
yield TestInstance(test_blocks, sync_every_block=False)
134-
135-
''' Mine 1 old version block '''
136-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
88+
node0.send_and_ping(msg_block(block))
89+
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
90+
91+
self.log.info("Test that blocks must now be at least version 3")
92+
tip = block.sha256
93+
block_time += 1
94+
block = create_block(tip, create_coinbase(DERSIG_HEIGHT), block_time)
13795
block.nVersion = 2
13896
block.rehash()
13997
block.solve()
140-
self.last_block_time += 1
141-
self.tip = block.sha256
142-
height += 1
143-
yield TestInstance([[block, True]])
98+
node0.send_and_ping(msg_block(block))
99+
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
144100

145-
''' Mine 1 new version block '''
146-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
101+
assert wait_until(lambda: "reject" in node0.last_message.keys())
102+
with mininode_lock:
103+
assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE)
104+
assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000002)')
105+
assert_equal(node0.last_message["reject"].data, block.sha256)
106+
del node0.last_message["reject"]
107+
108+
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
147109
block.nVersion = 3
148-
block.rehash()
149-
block.solve()
150-
self.last_block_time += 1
151-
self.tip = block.sha256
152-
height += 1
153-
yield TestInstance([[block, True]])
154-
155-
'''
156-
Check that the new DERSIG rules are enforced in the 951st version 3
157-
block.
158-
'''
159-
spendtx = self.create_transaction(self.nodes[0],
160-
self.coinbase_blocks[1], self.nodeaddress, 1.0)
110+
111+
spendtx = create_transaction(self.nodes[0], self.coinbase_blocks[1],
112+
self.nodeaddress, 1.0)
161113
unDERify(spendtx)
162114
spendtx.rehash()
163115

164-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
165-
block.nVersion = 3
116+
# First we show that this tx is valid except for DERSIG by getting it
117+
# accepted to the mempool (which we can achieve with
118+
# -promiscuousmempoolflags).
119+
node0.send_and_ping(msg_tx(spendtx))
120+
assert spendtx.hash in self.nodes[0].getrawmempool()
121+
122+
# Now we verify that a block with this transaction is invalid.
166123
block.vtx.append(spendtx)
167124
block.hashMerkleRoot = block.calc_merkle_root()
168125
block.rehash()
169126
block.solve()
170-
self.last_block_time += 1
171-
yield TestInstance([[block, False]])
172127

173-
''' Mine 1 old version block, should be invalid '''
174-
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
175-
block.nVersion = 2
128+
node0.send_and_ping(msg_block(block))
129+
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
130+
131+
assert wait_until (lambda: "reject" in node0.last_message.keys())
132+
with mininode_lock:
133+
# We can receive different reject messages depending on whether
134+
# bitcoind is running with multiple script check threads. If script
135+
# check threads are not in use, then transaction script validation
136+
# happens sequentially, and bitcoind produces more specific reject
137+
# reasons.
138+
assert node0.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD]
139+
assert_equal(node0.last_message["reject"].data, block.sha256)
140+
if node0.last_message["reject"].code == REJECT_INVALID:
141+
# Generic rejection when a block is invalid
142+
assert_equal(node0.last_message["reject"].reason, b'block-validation-failed')
143+
else:
144+
assert b'Non-canonical DER signature' in node0.last_message["reject"].reason
145+
146+
self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
147+
block.vtx[1] = create_transaction(self.nodes[0],
148+
self.coinbase_blocks[1], self.nodeaddress, 1.0)
149+
block.hashMerkleRoot = block.calc_merkle_root()
176150
block.rehash()
177151
block.solve()
178-
self.last_block_time += 1
179-
yield TestInstance([[block, False]])
152+
153+
node0.send_and_ping(msg_block(block))
154+
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
180155

181156
if __name__ == '__main__':
182157
BIP66Test().main()

test/functional/bipdersig.py

Lines changed: 0 additions & 81 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+
'bipdersig-p2p.py',
116117
'bip65-cltv-p2p.py',
117118
'uptime.py',
118119
]
@@ -137,8 +138,6 @@
137138
'rpcbind_test.py',
138139
# vv Tests less than 30s vv
139140
'assumevalid.py',
140-
'bipdersig-p2p.py',
141-
'bipdersig.py',
142141
'example_test.py',
143142
'getblocktemplate_proposals.py',
144143
'txn_doublespend.py',

0 commit comments

Comments
 (0)