4
4
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
5
"""Test BIP66 (DER SIG).
6
6
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.
17
8
"""
18
9
19
- from test_framework .test_framework import ComparisonTestFramework
10
+ from test_framework .test_framework import BitcoinTestFramework
20
11
from test_framework .util import *
21
- from test_framework .mininode import CTransaction , NetworkThread
12
+ from test_framework .mininode import *
22
13
from test_framework .blocktools import create_coinbase , create_block
23
- from test_framework .comptool import TestInstance , TestManager
24
14
from test_framework .script import CScript
25
15
from io import BytesIO
26
- import time
27
16
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:
29
25
# <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
30
26
def unDERify (tx ):
31
27
"""
@@ -40,143 +36,122 @@ def unDERify(tx):
40
36
else :
41
37
newscript .append (i )
42
38
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 ):
45
51
46
52
def __init__ (self ):
47
53
super ().__init__ ()
48
54
self .num_nodes = 1
55
+ self .extra_args = [['-promiscuousmempoolflags=1' , '-whitelist=127.0.0.1' ]]
56
+ self .setup_clean_chain = True
49
57
50
58
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 ])
53
63
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 )
72
70
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 )
107
76
unDERify (spendtx )
108
77
spendtx .rehash ()
109
78
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
112
83
block .vtx .append (spendtx )
113
84
block .hashMerkleRoot = block .calc_merkle_root ()
114
85
block .rehash ()
115
86
block .solve ()
116
87
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 )
137
95
block .nVersion = 2
138
96
block .rehash ()
139
97
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 )
144
100
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" )
147
109
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 )
161
113
unDERify (spendtx )
162
114
spendtx .rehash ()
163
115
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.
166
123
block .vtx .append (spendtx )
167
124
block .hashMerkleRoot = block .calc_merkle_root ()
168
125
block .rehash ()
169
126
block .solve ()
170
- self .last_block_time += 1
171
- yield TestInstance ([[block , False ]])
172
127
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 ()
176
150
block .rehash ()
177
151
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 )
180
155
181
156
if __name__ == '__main__' :
182
157
BIP66Test ().main ()
0 commit comments