Skip to content

Commit f24bcce

Browse files
committed
Merge pull request #1816
b867e40 CreateNewBlock: Stick height in coinbase so we pass template sanity check (Luke Dashjr) 60755db submitblock: Check for duplicate submissions explicitly (Luke Dashjr) bc6cb41 QA RPC tests: Add tests block block proposals (Luke Dashjr) 9765a50 Implement BIP 23 Block Proposal (Luke Dashjr) 3dcbb9b Abstract DecodeHexBlk and BIP22ValidationResult functions out of submitblock (Luke Dashjr) 132ea9b miner_tests: Disable checkpoints so they don't fail the subsidy-change test (Luke Dashjr) df08a62 TestBlockValidity function for CBlock proposals (used by CreateNewBlock) (Luke Dashjr) 4ea1be7 CreateNewBlock and miner_tests: Also check generated template is valid by CheckBlockHeader, ContextualCheckBlockHeader, CheckBlock, and ContextualCheckBlock (Luke Dashjr) a48f2d6 Abstract context-dependent block checking from acceptance (Luke Dashjr)
2 parents 6582f32 + b867e40 commit f24bcce

File tree

9 files changed

+391
-93
lines changed

9 files changed

+391
-93
lines changed

qa/rpc-tests/getblocktemplate.py renamed to qa/rpc-tests/getblocktemplate_longpoll.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
# Distributed under the MIT software license, see the accompanying
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55

6-
# Exercise the listtransactions API
7-
86
from test_framework import BitcoinTestFramework
97
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
108
from util import *
@@ -46,7 +44,7 @@ def __init__(self, node):
4644
def run(self):
4745
self.node.getblocktemplate({'longpollid':self.longpollid})
4846

49-
class GetBlockTemplateTest(BitcoinTestFramework):
47+
class GetBlockTemplateLPTest(BitcoinTestFramework):
5048
'''
5149
Test longpolling with getblocktemplate.
5250
'''
@@ -90,5 +88,5 @@ def run_test(self):
9088
assert(not thr.is_alive())
9189

9290
if __name__ == '__main__':
93-
GetBlockTemplateTest().main()
91+
GetBlockTemplateLPTest().main()
9492

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
#!/usr/bin/env python
2+
# Copyright (c) 2014 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
from test_framework import BitcoinTestFramework
7+
from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
8+
from util import *
9+
10+
from binascii import a2b_hex, b2a_hex
11+
from hashlib import sha256
12+
from struct import pack
13+
14+
15+
def check_array_result(object_array, to_match, expected):
16+
"""
17+
Pass in array of JSON objects, a dictionary with key/value pairs
18+
to match against, and another dictionary with expected key/value
19+
pairs.
20+
"""
21+
num_matched = 0
22+
for item in object_array:
23+
all_match = True
24+
for key,value in to_match.items():
25+
if item[key] != value:
26+
all_match = False
27+
if not all_match:
28+
continue
29+
for key,value in expected.items():
30+
if item[key] != value:
31+
raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
32+
num_matched = num_matched+1
33+
if num_matched == 0:
34+
raise AssertionError("No objects matched %s"%(str(to_match)))
35+
36+
def b2x(b):
37+
return b2a_hex(b).decode('ascii')
38+
39+
# NOTE: This does not work for signed numbers (set the high bit) or zero (use b'\0')
40+
def encodeUNum(n):
41+
s = bytearray(b'\1')
42+
while n > 127:
43+
s[0] += 1
44+
s.append(n % 256)
45+
n //= 256
46+
s.append(n)
47+
return bytes(s)
48+
49+
def varlenEncode(n):
50+
if n < 0xfd:
51+
return pack('<B', n)
52+
if n <= 0xffff:
53+
return b'\xfd' + pack('<H', n)
54+
if n <= 0xffffffff:
55+
return b'\xfe' + pack('<L', n)
56+
return b'\xff' + pack('<Q', n)
57+
58+
def dblsha(b):
59+
return sha256(sha256(b).digest()).digest()
60+
61+
def genmrklroot(leaflist):
62+
cur = leaflist
63+
while len(cur) > 1:
64+
n = []
65+
if len(cur) & 1:
66+
cur.append(cur[-1])
67+
for i in range(0, len(cur), 2):
68+
n.append(dblsha(cur[i] + cur[i+1]))
69+
cur = n
70+
return cur[0]
71+
72+
def template_to_bytes(tmpl, txlist):
73+
blkver = pack('<L', tmpl['version'])
74+
mrklroot = genmrklroot(list(dblsha(a) for a in txlist))
75+
timestamp = pack('<L', tmpl['curtime'])
76+
nonce = b'\0\0\0\0'
77+
blk = blkver + a2b_hex(tmpl['previousblockhash'])[::-1] + mrklroot + timestamp + a2b_hex(tmpl['bits'])[::-1] + nonce
78+
blk += varlenEncode(len(txlist))
79+
for tx in txlist:
80+
blk += tx
81+
return blk
82+
83+
def template_to_hex(tmpl, txlist):
84+
return b2x(template_to_bytes(tmpl, txlist))
85+
86+
def assert_template(node, tmpl, txlist, expect):
87+
rsp = node.getblocktemplate({'data':template_to_hex(tmpl, txlist),'mode':'proposal'})
88+
if rsp != expect:
89+
raise AssertionError('unexpected: %s' % (rsp,))
90+
91+
class GetBlockTemplateProposalTest(BitcoinTestFramework):
92+
'''
93+
Test block proposals with getblocktemplate.
94+
'''
95+
96+
def run_test(self):
97+
node = self.nodes[0]
98+
tmpl = node.getblocktemplate()
99+
if 'coinbasetxn' not in tmpl:
100+
rawcoinbase = encodeUNum(tmpl['height'])
101+
rawcoinbase += b'\x01-'
102+
hexcoinbase = b2x(rawcoinbase)
103+
hexoutval = b2x(pack('<Q', tmpl['coinbasevalue']))
104+
tmpl['coinbasetxn'] = {'data': '01000000' + '01' + '0000000000000000000000000000000000000000000000000000000000000000ffffffff' + ('%02x' % (len(rawcoinbase),)) + hexcoinbase + 'fffffffe' + '01' + hexoutval + '00' + '00000000'}
105+
txlist = list(bytearray(a2b_hex(a['data'])) for a in (tmpl['coinbasetxn'],) + tuple(tmpl['transactions']))
106+
107+
# Test 0: Capability advertised
108+
assert('proposal' in tmpl['capabilities'])
109+
110+
# NOTE: This test currently FAILS (regtest mode doesn't enforce block height in coinbase)
111+
## Test 1: Bad height in coinbase
112+
#txlist[0][4+1+36+1+1] += 1
113+
#assert_template(node, tmpl, txlist, 'FIXME')
114+
#txlist[0][4+1+36+1+1] -= 1
115+
116+
# Test 2: Bad input hash for gen tx
117+
txlist[0][4+1] += 1
118+
assert_template(node, tmpl, txlist, 'bad-cb-missing')
119+
txlist[0][4+1] -= 1
120+
121+
# Test 3: Truncated final tx
122+
lastbyte = txlist[-1].pop()
123+
try:
124+
assert_template(node, tmpl, txlist, 'n/a')
125+
except JSONRPCException:
126+
pass # Expected
127+
txlist[-1].append(lastbyte)
128+
129+
# Test 4: Add an invalid tx to the end (duplicate of gen tx)
130+
txlist.append(txlist[0])
131+
assert_template(node, tmpl, txlist, 'bad-txns-duplicate')
132+
txlist.pop()
133+
134+
# Test 5: Add an invalid tx to the end (non-duplicate)
135+
txlist.append(bytearray(txlist[0]))
136+
txlist[-1][4+1] = b'\xff'
137+
assert_template(node, tmpl, txlist, 'bad-txns-inputs-missingorspent')
138+
txlist.pop()
139+
140+
# Test 6: Future tx lock time
141+
txlist[0][-4:] = b'\xff\xff\xff\xff'
142+
assert_template(node, tmpl, txlist, 'bad-txns-nonfinal')
143+
txlist[0][-4:] = b'\0\0\0\0'
144+
145+
# Test 7: Bad tx count
146+
txlist.append(b'')
147+
try:
148+
assert_template(node, tmpl, txlist, 'n/a')
149+
except JSONRPCException:
150+
pass # Expected
151+
txlist.pop()
152+
153+
# Test 8: Bad bits
154+
realbits = tmpl['bits']
155+
tmpl['bits'] = '1c0000ff' # impossible in the real world
156+
assert_template(node, tmpl, txlist, 'bad-diffbits')
157+
tmpl['bits'] = realbits
158+
159+
# Test 9: Bad merkle root
160+
rawtmpl = template_to_bytes(tmpl, txlist)
161+
rawtmpl[4+32] = (rawtmpl[4+32] + 1) % 0x100
162+
rsp = node.getblocktemplate({'data':b2x(rawtmpl),'mode':'proposal'})
163+
if rsp != 'bad-txnmrklroot':
164+
raise AssertionError('unexpected: %s' % (rsp,))
165+
166+
# Test 10: Bad timestamps
167+
realtime = tmpl['curtime']
168+
tmpl['curtime'] = 0x7fffffff
169+
assert_template(node, tmpl, txlist, 'time-too-new')
170+
tmpl['curtime'] = 0
171+
assert_template(node, tmpl, txlist, 'time-too-old')
172+
tmpl['curtime'] = realtime
173+
174+
# Test 11: Valid block
175+
assert_template(node, tmpl, txlist, None)
176+
177+
# Test 12: Orphan block
178+
tmpl['previousblockhash'] = 'ff00' * 16
179+
assert_template(node, tmpl, txlist, 'inconclusive-not-best-prevblk')
180+
181+
if __name__ == '__main__':
182+
GetBlockTemplateProposalTest().main()

src/core_io.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <string>
99
#include <vector>
1010

11+
class CBlock;
1112
class CScript;
1213
class CTransaction;
1314
class uint256;
@@ -16,6 +17,7 @@ class UniValue;
1617
// core_read.cpp
1718
extern CScript ParseScript(std::string s);
1819
extern bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
20+
extern bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
1921
extern uint256 ParseHashUV(const UniValue& v, const std::string& strName);
2022
extern std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
2123

src/core_read.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "core_io.h"
66

7+
#include "core/block.h"
78
#include "core/transaction.h"
89
#include "script/script.h"
910
#include "serialize.h"
@@ -108,6 +109,23 @@ bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx)
108109
return true;
109110
}
110111

112+
bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
113+
{
114+
if (!IsHex(strHexBlk))
115+
return false;
116+
117+
std::vector<unsigned char> blockData(ParseHex(strHexBlk));
118+
CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION);
119+
try {
120+
ssBlock >> block;
121+
}
122+
catch (const std::exception &) {
123+
return false;
124+
}
125+
126+
return true;
127+
}
128+
111129
uint256 ParseHashUV(const UniValue& v, const string& strName)
112130
{
113131
string strHex;

0 commit comments

Comments
 (0)