Skip to content

Commit

Permalink
finalize functional test for blocksigning
Browse files Browse the repository at this point in the history
  • Loading branch information
instagibbs committed Nov 15, 2018
1 parent d1c1749 commit 7f2302b
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 153 deletions.
83 changes: 65 additions & 18 deletions test/functional/feature_blocksign.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
import random

from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (assert_raises_rpc_error, assert_equal, connect_nodes_bi)
from test_framework.authproxy import JSONRPCException
from test_framework import (
address,
key,
test_framework,
util,
)

# Generate wallet import format from private key.
Expand All @@ -32,6 +33,22 @@ def make_signblockscript(num_nodes, required_signers, keys):
return script

class BlockSignTest(BitcoinTestFramework):
"""
Test signed-blockchain-related RPC calls and functionality:
- getnewblockhex
- signblock
- combineblocksigs
- submitblock
- testproposedblock
- getcompactsketch
- consumecompactsketch
- consumegetblocktxn
- finalizecompactblock
As well as syncing blocks over p2p
"""
# Dynamically generate N keys to be used for block signing.
def init_keys(self, num_keys):
self.keys = []
Expand All @@ -48,23 +65,26 @@ def init_keys(self, num_keys):
self.wifs.append(wif(pk_bytes))

def set_test_params(self):
self.num_nodes = 4
self.num_nodes = 5
self.num_keys = 4
self.required_signers = 3
self.setup_clean_chain = True
self.init_keys(self.num_nodes)
signblockscript = make_signblockscript(self.num_nodes, self.required_signers, self.keys)
self.init_keys(self.num_nodes-1) # Last node cannot sign and is connected to all via p2p
signblockscript = make_signblockscript(self.num_keys, self.required_signers, self.keys)
self.extra_args = [[
"-signblockscript={}".format(signblockscript),
"-con_max_block_sig_size={}".format(self.num_nodes*74),
"-con_max_block_sig_size={}".format(self.required_signers*74),
"-con_blocksubsidy=5000000000",
]] * self.num_nodes

def setup_network(self):
self.setup_nodes()
# Connect non-signing node to a single signing one (to not pass blocks between signers)
connect_nodes_bi(self.nodes, 0, self.num_nodes-1)

def check_height(self, expected_height):
for n in self.nodes:
util.assert_equal(n.getblockcount(), expected_height)
assert_equal(n.getblockcount(), expected_height)

def mine_block(self, make_transactions):
# mine block in round robin sense: depending on the block number, a node
Expand All @@ -84,7 +104,7 @@ def mine_block(self, make_transactions):
block = miner.getnewblockhex()

# other nodes get fed compact blocks
for i in range(self.required_signers):
for i in range(self.num_nodes):
if i == mineridx:
continue
sketch = miner.getcompactsketch(block)
Expand All @@ -98,30 +118,41 @@ def mine_block(self, make_transactions):
# Block should be complete, sans signatures
self.nodes[i].testproposedblock(final_block)

# non-signing node can not sign
assert_raises_rpc_error(-25, "Could not sign the block.", self.nodes[-1].signblock, block)

# collect required_signers signatures
# collect num_keys signatures from signers, reduce to required_signers sigs during combine
sigs = []
for i in range(self.required_signers):
for i in range(self.num_keys):
result = miner.combineblocksigs(block, sigs)
util.assert_equal(result["complete"], False)
miner.submitblock(result["hex"])
self.check_height(blockcount)
sigs = sigs + self.nodes[i].signblock(block)
assert_equal(result["complete"], i >= self.required_signers)
# submitting should have no effect pre-threshhold
if i < self.required_signers:
miner.submitblock(result["hex"])
self.check_height(blockcount)

result = miner.combineblocksigs(block, sigs)
util.assert_equal(result["complete"], True)
assert_equal(result["complete"], True)

# All signing nodes must submit... we're not connected!
self.nodes[0].submitblock(result["hex"])
early_proposal = self.nodes[0].getnewblockhex() # testproposedblock should reject
# Submit blocks to all other signing nodes next, as well as too-early block proposal
for i in range(1, self.num_keys):
assert_raises_rpc_error(-25, "proposal was not based on our best chain", self.nodes[i].testproposedblock, early_proposal)
self.nodes[i].submitblock(result["hex"])

# All must submit... we're not connected!
for node in self.nodes:
node.submitblock(result["hex"])
# All nodes should be synced in blocks and transactions(mempool should be empty)
self.sync_all()

def mine_blocks(self, num_blocks, transactions):
for i in range(num_blocks):
self.mine_block(transactions)

def run_test(self):
# Have every node import its block signing private key.
for i in range(self.num_nodes):
# Have every node except last import its block signing private key.
for i in range(self.num_keys):
self.nodes[i].importprivkey(self.wifs[i])

self.check_height(0)
Expand All @@ -134,7 +165,23 @@ def run_test(self):
print("Mining and signing non-empty blocks")
self.mine_blocks(10, True)

# Height check also makes sure non-signing, p2p connected node gets block
self.check_height(111)

# signblock rpc field stuff
tip = self.nodes[0].getblockhash(self.nodes[0].getblockcount())
header = self.nodes[0].getblockheader(tip)
block = self.nodes[0].getblock(tip)
info = self.nodes[0].getblockchaininfo()

assert('signblock_witness_asm' in header)
assert('signblock_witness_hex' in header)
assert('signblock_witness_asm' in block)
assert('signblock_witness_hex' in block)

signblockscript = make_signblockscript(self.num_keys, self.required_signers, self.keys)
assert_equal(info['signblock_asm'], self.nodes[0].decodescript(signblockscript)['asm'])
assert_equal(info['signblock_hex'], signblockscript)

if __name__ == '__main__':
BlockSignTest().main()
135 changes: 0 additions & 135 deletions test/functional/feature_signed_blockchain.py

This file was deleted.

0 comments on commit 7f2302b

Please sign in to comment.