Skip to content

Commit 1348313

Browse files
committed
add basic functional tests for signed block rpcs and validation
1 parent c07b0af commit 1348313

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed

qa/pull-tester/rpc-tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@
163163
'rpcnamedargs.py',
164164
'listsinceblock.py',
165165
'p2p-leaktests.py',
166+
'signed_blockchain.py',
166167
]
167168
if ENABLE_ZMQ:
168169
testScripts.append('zmq_test.py')

qa/rpc-tests/signed_blockchain.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2014-2016 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+
#
7+
# Test RPC calls related to blockchain state. Tests correspond to code in
8+
# rpc/blockchain.cpp.
9+
#
10+
11+
from decimal import Decimal
12+
from test_framework.test_framework import BitcoinTestFramework
13+
from test_framework.authproxy import JSONRPCException
14+
from test_framework.util import (
15+
assert_equal,
16+
start_nodes,
17+
assert_raises_jsonrpc,
18+
)
19+
20+
21+
class SignedBlockchainTest(BitcoinTestFramework):
22+
"""
23+
Test signed-blockchain-related RPC calls:
24+
25+
- getnewblockhex
26+
- signblock
27+
- combineblocksigs
28+
- submitblock
29+
30+
"""
31+
32+
def __init__(self):
33+
super().__init__()
34+
self.setup_clean_chain = True
35+
self.num_nodes = 3
36+
37+
def setup_network(self, split=False):
38+
# Keys for signing 2-of-3
39+
pubkeys = ["039560e48d4336e40db447fc136ce24ae1dfdefa5701e4d4e57aa1a1a9f47f3faa", "025a66517c1d85adcd909f9f675bf656708edc4da1f614693e347be6baf0fef4ae", "02ca238faeb3b01d26ae8a39869220dbd84cc8516398afa8958fe613fe4fdf1c04"]
40+
# Normal multisig scriptPubKey: 1 <33 byte pubkey> <33 byte pubkey> ... 2 OP_CMS
41+
sign_script = "-signblockscript=5221"+pubkeys[0]+"21"+pubkeys[1]+"21"+pubkeys[2]+"53ae"
42+
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [[sign_script],[sign_script], [sign_script]])
43+
# nodes are disconnected for this test
44+
self.is_network_split = True
45+
46+
def run_test(self):
47+
keys = ["cRANrxPMceu8jKAA76xzpA9PtTEhBknyZyaaQZ3Z5FnFTGkCAqmT", "cTJRjDBWo1JXdie31B5wv5eXNrPGCAjQKn48umubhmNLjsnj951V", "cVjTXVgKE8PhvCgDowRJVQW68q7j5kSbDGfAT5CUwt9D8dn2cAwf"]
48+
assert_equal(self.nodes[0].getblockcount(), 0)
49+
assert_equal(self.nodes[1].getblockcount(), 0)
50+
51+
block_hex = self.nodes[0].getnewblockhex()
52+
53+
# Block needs signatures, but valid and extends chaintip otherwise
54+
assert_equal(self.nodes[0].testproposedblock(block_hex), None)
55+
assert_equal(self.nodes[1].testproposedblock(block_hex), None)
56+
57+
assert_equal(self.nodes[0].submitblock(block_hex), "block-proof-invalid")
58+
assert_equal(self.nodes[1].submitblock(block_hex), "block-proof-invalid")
59+
60+
assert_equal(self.nodes[0].getblockcount(), 0)
61+
assert_equal(self.nodes[1].getblockcount(), 0)
62+
63+
# combineblocksigs only returns true when signatures are appended and enough
64+
# are included to pass validation
65+
assert_equal(self.nodes[0].combineblocksigs(block_hex, [])["complete"], False)
66+
assert_equal(self.nodes[1].combineblocksigs(block_hex, [])["complete"], False)
67+
68+
# Now we can try to sign, without key
69+
assert_equal(self.nodes[0].signblock(block_hex), "00")
70+
assert_equal(self.nodes[1].signblock(block_hex), "00")
71+
72+
# Import keys
73+
self.nodes[0].importprivkey(keys[0])
74+
self.nodes[1].importprivkey(keys[1])
75+
self.nodes[2].importprivkey(keys[2])
76+
77+
sig0 = self.nodes[0].signblock(block_hex)
78+
sig1 = self.nodes[1].signblock(block_hex)
79+
80+
combined0 = self.nodes[0].combineblocksigs(block_hex, [sig0])
81+
assert(not combined0["complete"])
82+
combined1 = self.nodes[0].combineblocksigs(combined0["hex"], [sig1])
83+
assert(combined1["complete"])
84+
85+
# Still haven't moved forward
86+
assert_equal(self.nodes[0].getblockcount(), 0)
87+
assert_equal(self.nodes[1].getblockcount(), 0)
88+
89+
self.nodes[0].submitblock(combined1["hex"])
90+
# Move his chain along for later
91+
self.nodes[2].submitblock(combined1["hex"])
92+
93+
assert_equal(self.nodes[0].getblockcount(), 1)
94+
assert_equal(self.nodes[1].getblockcount(), 0)
95+
assert_equal(self.nodes[2].getblockcount(), 1)
96+
97+
block_too_far = self.nodes[0].getnewblockhex()
98+
99+
assert_raises_jsonrpc,(JSONRPCException, -25, "proposal was not based on our best chain", self.nodes[1].testproposedblock, block_too_far)
100+
101+
# Finally, submit block
102+
self.nodes[1].submitblock(combined1["hex"])
103+
assert_equal(self.nodes[1].getblockcount(), 1)
104+
105+
# Now proposal is fine, aside from sigs
106+
assert_equal(self.nodes[1].testproposedblock(block_too_far), None)
107+
sig0 = self.nodes[0].signblock(block_too_far)
108+
sig1 = self.nodes[1].signblock(block_too_far)
109+
sig2 = self.nodes[2].signblock(block_too_far)
110+
111+
combined0 = self.nodes[0].combineblocksigs(block_too_far, [sig0])
112+
assert(not combined0["complete"])
113+
# combining signature from third node this time
114+
combined1 = self.nodes[0].combineblocksigs(combined0["hex"], [sig2])
115+
assert(combined1["complete"])
116+
117+
# TODO stuff with too many signatures or junk data manually
118+
119+
if __name__ == '__main__':
120+
SignedBlockchainTest().main()

0 commit comments

Comments
 (0)