Extend getchaintips RPC test.

Add the capability to simulate network splits to the RPC test framework
and use it to do more extensive testing of 'getchaintips'.
This commit is contained in:
Daniel Kraft 2014-10-20 14:14:04 +02:00
parent 3552d4b859
commit dcb98466b4
7 changed files with 206 additions and 134 deletions

View File

@ -17,31 +17,30 @@ class ForkNotifyTest(BitcoinTestFramework):
alert_filename = None # Set by setup_network alert_filename = None # Set by setup_network
def setup_network(self, test_dir): def setup_network(self):
nodes = [] self.nodes = []
self.alert_filename = os.path.join(test_dir, "alert.txt") self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
with open(self.alert_filename, 'w') as f: with open(self.alert_filename, 'w') as f:
pass # Just open then close to create zero-length file pass # Just open then close to create zero-length file
nodes.append(start_node(0, test_dir, self.nodes.append(start_node(0, self.options.tmpdir,
["-blockversion=2", "-alertnotify=echo %s >> '" + self.alert_filename + "'"])) ["-blockversion=2", "-alertnotify=echo %s >> '" + self.alert_filename + "'"]))
# Node1 mines block.version=211 blocks # Node1 mines block.version=211 blocks
nodes.append(start_node(1, test_dir, self.nodes.append(start_node(1, self.options.tmpdir,
["-blockversion=211"])) ["-blockversion=211"]))
connect_nodes(nodes[1], 0) connect_nodes(self.nodes[1], 0)
sync_blocks(nodes) self.is_network_split = False
return nodes self.sync_all()
def run_test(self, nodes): def run_test(self):
# Mine 51 up-version blocks # Mine 51 up-version blocks
nodes[1].setgenerate(True, 51) self.nodes[1].setgenerate(True, 51)
sync_blocks(nodes) self.sync_all()
# -alertnotify should trigger on the 51'st, # -alertnotify should trigger on the 51'st,
# but mine and sync another to give # but mine and sync another to give
# -alertnotify time to write # -alertnotify time to write
nodes[1].setgenerate(True, 1) self.nodes[1].setgenerate(True, 1)
sync_blocks(nodes) self.sync_all()
with open(self.alert_filename, 'r') as f: with open(self.alert_filename, 'r') as f:
alert_text = f.read() alert_text = f.read()
@ -50,10 +49,10 @@ class ForkNotifyTest(BitcoinTestFramework):
raise AssertionError("-alertnotify did not warn of up-version blocks") raise AssertionError("-alertnotify did not warn of up-version blocks")
# Mine more up-version blocks, should not get more alerts: # Mine more up-version blocks, should not get more alerts:
nodes[1].setgenerate(True, 1) self.nodes[1].setgenerate(True, 1)
sync_blocks(nodes) self.sync_all()
nodes[1].setgenerate(True, 1) self.nodes[1].setgenerate(True, 1)
sync_blocks(nodes) self.sync_all()
with open(self.alert_filename, 'r') as f: with open(self.alert_filename, 'r') as f:
alert_text2 = f.read() alert_text2 = f.read()

View File

@ -51,40 +51,40 @@ class GetBlockTemplateTest(BitcoinTestFramework):
Test longpolling with getblocktemplate. Test longpolling with getblocktemplate.
''' '''
def run_test(self, nodes): def run_test(self):
print "Warning: this test will take about 70 seconds in the best case. Be patient." print "Warning: this test will take about 70 seconds in the best case. Be patient."
nodes[0].setgenerate(True, 10) self.nodes[0].setgenerate(True, 10)
templat = nodes[0].getblocktemplate() templat = self.nodes[0].getblocktemplate()
longpollid = templat['longpollid'] longpollid = templat['longpollid']
# longpollid should not change between successive invocations if nothing else happens # longpollid should not change between successive invocations if nothing else happens
templat2 = nodes[0].getblocktemplate() templat2 = self.nodes[0].getblocktemplate()
assert(templat2['longpollid'] == longpollid) assert(templat2['longpollid'] == longpollid)
# Test 1: test that the longpolling wait if we do nothing # Test 1: test that the longpolling wait if we do nothing
thr = LongpollThread(nodes[0]) thr = LongpollThread(self.nodes[0])
thr.start() thr.start()
# check that thread still lives # check that thread still lives
thr.join(5) # wait 5 seconds or until thread exits thr.join(5) # wait 5 seconds or until thread exits
assert(thr.is_alive()) assert(thr.is_alive())
# Test 2: test that longpoll will terminate if another node generates a block # Test 2: test that longpoll will terminate if another node generates a block
nodes[1].setgenerate(True, 1) # generate a block on another node self.nodes[1].setgenerate(True, 1) # generate a block on another node
# check that thread will exit now that new transaction entered mempool # check that thread will exit now that new transaction entered mempool
thr.join(5) # wait 5 seconds or until thread exits thr.join(5) # wait 5 seconds or until thread exits
assert(not thr.is_alive()) assert(not thr.is_alive())
# Test 3: test that longpoll will terminate if we generate a block ourselves # Test 3: test that longpoll will terminate if we generate a block ourselves
thr = LongpollThread(nodes[0]) thr = LongpollThread(self.nodes[0])
thr.start() thr.start()
nodes[0].setgenerate(True, 1) # generate a block on another node self.nodes[0].setgenerate(True, 1) # generate a block on another node
thr.join(5) # wait 5 seconds or until thread exits thr.join(5) # wait 5 seconds or until thread exits
assert(not thr.is_alive()) assert(not thr.is_alive())
# Test 4: test that introducing a new transaction into the mempool will terminate the longpoll # Test 4: test that introducing a new transaction into the mempool will terminate the longpoll
thr = LongpollThread(nodes[0]) thr = LongpollThread(self.nodes[0])
thr.start() thr.start()
# generate a random transaction and submit it # generate a random transaction and submit it
(txid, txhex, fee) = random_transaction(nodes, Decimal("1.1"), Decimal("0.0"), Decimal("0.001"), 20) (txid, txhex, fee) = random_transaction(self.nodes, Decimal("1.1"), Decimal("0.0"), Decimal("0.001"), 20)
# after one minute, every 10 seconds the mempool is probed, so in 80 seconds it should have returned # after one minute, every 10 seconds the mempool is probed, so in 80 seconds it should have returned
thr.join(60 + 20) thr.join(60 + 20)
assert(not thr.is_alive()) assert(not thr.is_alive())

View File

@ -3,22 +3,52 @@
# Distributed under the MIT/X11 software license, see the accompanying # Distributed under the MIT/X11 software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
# Exercise the getchaintips API. # Exercise the getchaintips API. We introduce a network split, work
# on chains of different lengths, and join the network together again.
# Since the test framework does not generate orphan blocks, we can # This gives us two tips, verify that it works.
# unfortunately not check for them!
from test_framework import BitcoinTestFramework from test_framework import BitcoinTestFramework
from util import assert_equal from util import assert_equal
class GetChainTipsTest (BitcoinTestFramework): class GetChainTipsTest (BitcoinTestFramework):
def run_test (self, nodes): def run_test (self):
res = nodes[0].getchaintips () BitcoinTestFramework.run_test (self)
assert_equal (len (res), 1)
res = res[0] tips = self.nodes[0].getchaintips ()
assert_equal (res['branchlen'], 0) assert_equal (len (tips), 1)
assert_equal (res['height'], 200) assert_equal (tips[0]['branchlen'], 0)
assert_equal (tips[0]['height'], 200)
# Split the network and build two chains of different lengths.
self.split_network ()
self.nodes[0].setgenerate (True, 10);
self.nodes[2].setgenerate (True, 20);
self.sync_all ()
tips = self.nodes[1].getchaintips ()
assert_equal (len (tips), 1)
shortTip = tips[0]
assert_equal (shortTip['branchlen'], 0)
assert_equal (shortTip['height'], 210)
tips = self.nodes[3].getchaintips ()
assert_equal (len (tips), 1)
longTip = tips[0]
assert_equal (longTip['branchlen'], 0)
assert_equal (longTip['height'], 220)
# Join the network halves and check that we now have two tips
# (at least at the nodes that previously had the short chain).
self.join_network ()
tips = self.nodes[0].getchaintips ()
assert_equal (len (tips), 2)
assert_equal (tips[0], longTip)
assert_equal (tips[1]['branchlen'], 10)
tips[1]['branchlen'] = 0;
assert_equal (tips[1], shortTip)
if __name__ == '__main__': if __name__ == '__main__':
GetChainTipsTest ().main () GetChainTipsTest ().main ()

View File

@ -33,62 +33,64 @@ def check_array_result(object_array, to_match, expected):
class ListTransactionsTest(BitcoinTestFramework): class ListTransactionsTest(BitcoinTestFramework):
def run_test(self, nodes): def run_test(self):
# Simple send, 0 to 1: # Simple send, 0 to 1:
txid = nodes[0].sendtoaddress(nodes[1].getnewaddress(), 0.1) txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
sync_mempools(nodes) self.sync_all()
check_array_result(nodes[0].listtransactions(), check_array_result(self.nodes[0].listtransactions(),
{"txid":txid}, {"txid":txid},
{"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":0}) {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":0})
check_array_result(nodes[1].listtransactions(), check_array_result(self.nodes[1].listtransactions(),
{"txid":txid}, {"txid":txid},
{"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":0}) {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":0})
# mine a block, confirmations should change: # mine a block, confirmations should change:
nodes[0].setgenerate(True, 1) self.nodes[0].setgenerate(True, 1)
sync_blocks(nodes) self.sync_all()
check_array_result(nodes[0].listtransactions(), check_array_result(self.nodes[0].listtransactions(),
{"txid":txid}, {"txid":txid},
{"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":1}) {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":1})
check_array_result(nodes[1].listtransactions(), check_array_result(self.nodes[1].listtransactions(),
{"txid":txid}, {"txid":txid},
{"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":1}) {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":1})
# send-to-self: # send-to-self:
txid = nodes[0].sendtoaddress(nodes[0].getnewaddress(), 0.2) txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)
check_array_result(nodes[0].listtransactions(), check_array_result(self.nodes[0].listtransactions(),
{"txid":txid, "category":"send"}, {"txid":txid, "category":"send"},
{"amount":Decimal("-0.2")}) {"amount":Decimal("-0.2")})
check_array_result(nodes[0].listtransactions(), check_array_result(self.nodes[0].listtransactions(),
{"txid":txid, "category":"receive"}, {"txid":txid, "category":"receive"},
{"amount":Decimal("0.2")}) {"amount":Decimal("0.2")})
# sendmany from node1: twice to self, twice to node2: # sendmany from node1: twice to self, twice to node2:
send_to = { nodes[0].getnewaddress() : 0.11, nodes[1].getnewaddress() : 0.22, send_to = { self.nodes[0].getnewaddress() : 0.11,
nodes[0].getaccountaddress("from1") : 0.33, nodes[1].getaccountaddress("toself") : 0.44 } self.nodes[1].getnewaddress() : 0.22,
txid = nodes[1].sendmany("", send_to) self.nodes[0].getaccountaddress("from1") : 0.33,
sync_mempools(nodes) self.nodes[1].getaccountaddress("toself") : 0.44 }
check_array_result(nodes[1].listtransactions(), txid = self.nodes[1].sendmany("", send_to)
self.sync_all()
check_array_result(self.nodes[1].listtransactions(),
{"category":"send","amount":Decimal("-0.11")}, {"category":"send","amount":Decimal("-0.11")},
{"txid":txid} ) {"txid":txid} )
check_array_result(nodes[0].listtransactions(), check_array_result(self.nodes[0].listtransactions(),
{"category":"receive","amount":Decimal("0.11")}, {"category":"receive","amount":Decimal("0.11")},
{"txid":txid} ) {"txid":txid} )
check_array_result(nodes[1].listtransactions(), check_array_result(self.nodes[1].listtransactions(),
{"category":"send","amount":Decimal("-0.22")}, {"category":"send","amount":Decimal("-0.22")},
{"txid":txid} ) {"txid":txid} )
check_array_result(nodes[1].listtransactions(), check_array_result(self.nodes[1].listtransactions(),
{"category":"receive","amount":Decimal("0.22")}, {"category":"receive","amount":Decimal("0.22")},
{"txid":txid} ) {"txid":txid} )
check_array_result(nodes[1].listtransactions(), check_array_result(self.nodes[1].listtransactions(),
{"category":"send","amount":Decimal("-0.33")}, {"category":"send","amount":Decimal("-0.33")},
{"txid":txid} ) {"txid":txid} )
check_array_result(nodes[0].listtransactions(), check_array_result(self.nodes[0].listtransactions(),
{"category":"receive","amount":Decimal("0.33")}, {"category":"receive","amount":Decimal("0.33")},
{"txid":txid, "account" : "from1"} ) {"txid":txid, "account" : "from1"} )
check_array_result(nodes[1].listtransactions(), check_array_result(self.nodes[1].listtransactions(),
{"category":"send","amount":Decimal("-0.44")}, {"category":"send","amount":Decimal("-0.44")},
{"txid":txid, "account" : ""} ) {"txid":txid, "account" : ""} )
check_array_result(nodes[1].listtransactions(), check_array_result(self.nodes[1].listtransactions(),
{"category":"receive","amount":Decimal("0.44")}, {"category":"receive","amount":Decimal("0.44")},
{"txid":txid, "account" : "toself"} ) {"txid":txid, "account" : "toself"} )

View File

@ -54,36 +54,36 @@ def check_array_result(object_array, to_match, expected, should_not_find = False
class ReceivedByTest(BitcoinTestFramework): class ReceivedByTest(BitcoinTestFramework):
def run_test(self, nodes): def run_test(self):
''' '''
listreceivedbyaddress Test listreceivedbyaddress Test
''' '''
# Send from node 0 to 1 # Send from node 0 to 1
addr = nodes[1].getnewaddress() addr = self.nodes[1].getnewaddress()
txid = nodes[0].sendtoaddress(addr, 0.1) txid = self.nodes[0].sendtoaddress(addr, 0.1)
sync_mempools(nodes) self.sync_all()
#Check not listed in listreceivedbyaddress because has 0 confirmations #Check not listed in listreceivedbyaddress because has 0 confirmations
check_array_result(nodes[1].listreceivedbyaddress(), check_array_result(self.nodes[1].listreceivedbyaddress(),
{"address":addr}, {"address":addr},
{ }, { },
True) True)
#Bury Tx under 10 block so it will be returned by listreceivedbyaddress #Bury Tx under 10 block so it will be returned by listreceivedbyaddress
nodes[1].setgenerate(True, 10) self.nodes[1].setgenerate(True, 10)
sync_blocks(nodes) self.sync_all()
check_array_result(nodes[1].listreceivedbyaddress(), check_array_result(self.nodes[1].listreceivedbyaddress(),
{"address":addr}, {"address":addr},
{"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}) {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]})
#With min confidence < 10 #With min confidence < 10
check_array_result(nodes[1].listreceivedbyaddress(5), check_array_result(self.nodes[1].listreceivedbyaddress(5),
{"address":addr}, {"address":addr},
{"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]}) {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]})
#With min confidence > 10, should not find Tx #With min confidence > 10, should not find Tx
check_array_result(nodes[1].listreceivedbyaddress(11),{"address":addr},{ },True) check_array_result(self.nodes[1].listreceivedbyaddress(11),{"address":addr},{ },True)
#Empty Tx #Empty Tx
addr = nodes[1].getnewaddress() addr = self.nodes[1].getnewaddress()
check_array_result(nodes[1].listreceivedbyaddress(0,True), check_array_result(self.nodes[1].listreceivedbyaddress(0,True),
{"address":addr}, {"address":addr},
{"address":addr, "account":"", "amount":0, "confirmations":0, "txids":[]}) {"address":addr, "account":"", "amount":0, "confirmations":0, "txids":[]})
@ -91,24 +91,24 @@ class ReceivedByTest(BitcoinTestFramework):
getreceivedbyaddress Test getreceivedbyaddress Test
''' '''
# Send from node 0 to 1 # Send from node 0 to 1
addr = nodes[1].getnewaddress() addr = self.nodes[1].getnewaddress()
txid = nodes[0].sendtoaddress(addr, 0.1) txid = self.nodes[0].sendtoaddress(addr, 0.1)
sync_mempools(nodes) self.sync_all()
#Check balance is 0 because of 0 confirmations #Check balance is 0 because of 0 confirmations
balance = nodes[1].getreceivedbyaddress(addr) balance = self.nodes[1].getreceivedbyaddress(addr)
if balance != Decimal("0.0"): if balance != Decimal("0.0"):
raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance)) raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
#Check balance is 0.1 #Check balance is 0.1
balance = nodes[1].getreceivedbyaddress(addr,0) balance = self.nodes[1].getreceivedbyaddress(addr,0)
if balance != Decimal("0.1"): if balance != Decimal("0.1"):
raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance)) raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
#Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress #Bury Tx under 10 block so it will be returned by the default getreceivedbyaddress
nodes[1].setgenerate(True, 10) self.nodes[1].setgenerate(True, 10)
sync_blocks(nodes) self.sync_all()
balance = nodes[1].getreceivedbyaddress(addr) balance = self.nodes[1].getreceivedbyaddress(addr)
if balance != Decimal("0.1"): if balance != Decimal("0.1"):
raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance)) raise AssertionError("Wrong balance returned by getreceivedbyaddress, %0.2f"%(balance))
@ -116,40 +116,40 @@ class ReceivedByTest(BitcoinTestFramework):
listreceivedbyaccount + getreceivedbyaccount Test listreceivedbyaccount + getreceivedbyaccount Test
''' '''
#set pre-state #set pre-state
addrArr = nodes[1].getnewaddress() addrArr = self.nodes[1].getnewaddress()
account = nodes[1].getaccount(addrArr) account = self.nodes[1].getaccount(addrArr)
received_by_account_json = get_sub_array_from_array(nodes[1].listreceivedbyaccount(),{"account":account}) received_by_account_json = get_sub_array_from_array(self.nodes[1].listreceivedbyaccount(),{"account":account})
if len(received_by_account_json) == 0: if len(received_by_account_json) == 0:
raise AssertionError("No accounts found in node") raise AssertionError("No accounts found in node")
balance_by_account = rec_by_accountArr = nodes[1].getreceivedbyaccount(account) balance_by_account = rec_by_accountArr = self.nodes[1].getreceivedbyaccount(account)
txid = nodes[0].sendtoaddress(addr, 0.1) txid = self.nodes[0].sendtoaddress(addr, 0.1)
# listreceivedbyaccount should return received_by_account_json because of 0 confirmations # listreceivedbyaccount should return received_by_account_json because of 0 confirmations
check_array_result(nodes[1].listreceivedbyaccount(), check_array_result(self.nodes[1].listreceivedbyaccount(),
{"account":account}, {"account":account},
received_by_account_json) received_by_account_json)
# getreceivedbyaddress should return same balance because of 0 confirmations # getreceivedbyaddress should return same balance because of 0 confirmations
balance = nodes[1].getreceivedbyaccount(account) balance = self.nodes[1].getreceivedbyaccount(account)
if balance != balance_by_account: if balance != balance_by_account:
raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance)) raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
nodes[1].setgenerate(True, 10) self.nodes[1].setgenerate(True, 10)
sync_blocks(nodes) self.sync_all()
# listreceivedbyaccount should return updated account balance # listreceivedbyaccount should return updated account balance
check_array_result(nodes[1].listreceivedbyaccount(), check_array_result(self.nodes[1].listreceivedbyaccount(),
{"account":account}, {"account":account},
{"account":received_by_account_json["account"], "amount":(received_by_account_json["amount"] + Decimal("0.1"))}) {"account":received_by_account_json["account"], "amount":(received_by_account_json["amount"] + Decimal("0.1"))})
# getreceivedbyaddress should return updates balance # getreceivedbyaddress should return updates balance
balance = nodes[1].getreceivedbyaccount(account) balance = self.nodes[1].getreceivedbyaccount(account)
if balance != balance_by_account + Decimal("0.1"): if balance != balance_by_account + Decimal("0.1"):
raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance)) raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))
#Create a new account named "mynewaccount" that has a 0 balance #Create a new account named "mynewaccount" that has a 0 balance
nodes[1].getaccountaddress("mynewaccount") self.nodes[1].getaccountaddress("mynewaccount")
received_by_account_json = get_sub_array_from_array(nodes[1].listreceivedbyaccount(0,True),{"account":"mynewaccount"}) received_by_account_json = get_sub_array_from_array(self.nodes[1].listreceivedbyaccount(0,True),{"account":"mynewaccount"})
if len(received_by_account_json) == 0: if len(received_by_account_json) == 0:
raise AssertionError("No accounts found in node") raise AssertionError("No accounts found in node")
@ -158,7 +158,7 @@ class ReceivedByTest(BitcoinTestFramework):
raise AssertionError("Wrong balance returned by listreceivedbyaccount, %0.2f"%(received_by_account_json["amount"])) raise AssertionError("Wrong balance returned by listreceivedbyaccount, %0.2f"%(received_by_account_json["amount"]))
# Test getreceivedbyaccount for 0 amount accounts # Test getreceivedbyaccount for 0 amount accounts
balance = nodes[1].getreceivedbyaccount("mynewaccount") balance = self.nodes[1].getreceivedbyaccount("mynewaccount")
if balance != Decimal("0.0"): if balance != Decimal("0.0"):
raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance)) raise AssertionError("Wrong balance returned by getreceivedbyaccount, %0.2f"%(balance))

View File

@ -10,48 +10,48 @@ from util import *
class EstimateFeeTest(BitcoinTestFramework): class EstimateFeeTest(BitcoinTestFramework):
def setup_network(self, test_dir): def setup_network(self):
nodes = [] self.nodes = []
nodes.append(start_node(0, test_dir, self.nodes.append(start_node(0, self.options.tmpdir,
["-debug=mempool", "-debug=estimatefee"])) ["-debug=mempool", "-debug=estimatefee"]))
# Node1 mines small-but-not-tiny blocks, and allows free transactions. # Node1 mines small-but-not-tiny blocks, and allows free transactions.
# NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes, # NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes,
# so blockmaxsize of 2,000 is really just 1,000 bytes (room enough for # so blockmaxsize of 2,000 is really just 1,000 bytes (room enough for
# 6 or 7 transactions) # 6 or 7 transactions)
nodes.append(start_node(1, test_dir, self.nodes.append(start_node(1, self.options.tmpdir,
["-blockprioritysize=1500", "-blockmaxsize=2000", ["-blockprioritysize=1500", "-blockmaxsize=2000",
"-debug=mempool", "-debug=estimatefee"])) "-debug=mempool", "-debug=estimatefee"]))
connect_nodes(nodes[1], 0) connect_nodes(self.nodes[1], 0)
# Node2 is a stingy miner, that # Node2 is a stingy miner, that
# produces very small blocks (room for only 3 or so transactions) # produces very small blocks (room for only 3 or so transactions)
node2args = [ "-blockprioritysize=0", "-blockmaxsize=1500", node2args = [ "-blockprioritysize=0", "-blockmaxsize=1500",
"-debug=mempool", "-debug=estimatefee"] "-debug=mempool", "-debug=estimatefee"]
nodes.append(start_node(2, test_dir, node2args)) self.nodes.append(start_node(2, self.options.tmpdir, node2args))
connect_nodes(nodes[2], 0) connect_nodes(self.nodes[2], 0)
sync_blocks(nodes) self.is_network_split = False
return nodes self.sync_all()
def run_test(self, nodes): def run_test(self):
# Prime the memory pool with pairs of transactions # Prime the memory pool with pairs of transactions
# (high-priority, random fee and zero-priority, random fee) # (high-priority, random fee and zero-priority, random fee)
min_fee = Decimal("0.001") min_fee = Decimal("0.001")
fees_per_kb = []; fees_per_kb = [];
for i in range(12): for i in range(12):
(txid, txhex, fee) = random_zeropri_transaction(nodes, Decimal("1.1"), (txid, txhex, fee) = random_zeropri_transaction(self.nodes, Decimal("1.1"),
min_fee, min_fee, 20) min_fee, min_fee, 20)
tx_kbytes = (len(txhex)/2)/1000.0 tx_kbytes = (len(txhex)/2)/1000.0
fees_per_kb.append(float(fee)/tx_kbytes) fees_per_kb.append(float(fee)/tx_kbytes)
# Mine blocks with node2 until the memory pool clears: # Mine blocks with node2 until the memory pool clears:
count_start = nodes[2].getblockcount() count_start = self.nodes[2].getblockcount()
while len(nodes[2].getrawmempool()) > 0: while len(self.nodes[2].getrawmempool()) > 0:
nodes[2].setgenerate(True, 1) self.nodes[2].setgenerate(True, 1)
sync_blocks(nodes) self.sync_all()
all_estimates = [ nodes[0].estimatefee(i) for i in range(1,20) ] all_estimates = [ self.nodes[0].estimatefee(i) for i in range(1,20) ]
print("Fee estimates, super-stingy miner: "+str([str(e) for e in all_estimates])) print("Fee estimates, super-stingy miner: "+str([str(e) for e in all_estimates]))
# Estimates should be within the bounds of what transactions fees actually were: # Estimates should be within the bounds of what transactions fees actually were:
@ -63,25 +63,25 @@ class EstimateFeeTest(BitcoinTestFramework):
# Generate transactions while mining 30 more blocks, this time with node1: # Generate transactions while mining 30 more blocks, this time with node1:
for i in range(30): for i in range(30):
for j in range(random.randrange(6-4,6+4)): for j in range(random.randrange(6-4,6+4)):
(txid, txhex, fee) = random_transaction(nodes, Decimal("1.1"), (txid, txhex, fee) = random_transaction(self.nodes, Decimal("1.1"),
Decimal("0.0"), min_fee, 20) Decimal("0.0"), min_fee, 20)
tx_kbytes = (len(txhex)/2)/1000.0 tx_kbytes = (len(txhex)/2)/1000.0
fees_per_kb.append(float(fee)/tx_kbytes) fees_per_kb.append(float(fee)/tx_kbytes)
nodes[1].setgenerate(True, 1) self.nodes[1].setgenerate(True, 1)
sync_blocks(nodes) self.sync_all()
all_estimates = [ nodes[0].estimatefee(i) for i in range(1,20) ] all_estimates = [ self.nodes[0].estimatefee(i) for i in range(1,20) ]
print("Fee estimates, more generous miner: "+str([ str(e) for e in all_estimates])) print("Fee estimates, more generous miner: "+str([ str(e) for e in all_estimates]))
for e in filter(lambda x: x >= 0, all_estimates): for e in filter(lambda x: x >= 0, all_estimates):
if float(e)+delta < min(fees_per_kb) or float(e)-delta > max(fees_per_kb): if float(e)+delta < min(fees_per_kb) or float(e)-delta > max(fees_per_kb):
raise AssertionError("Estimated fee (%f) out of range (%f,%f)"%(float(e), min_fee_kb, max_fee_kb)) raise AssertionError("Estimated fee (%f) out of range (%f,%f)"%(float(e), min_fee_kb, max_fee_kb))
# Finish by mining a normal-sized block: # Finish by mining a normal-sized block:
while len(nodes[0].getrawmempool()) > 0: while len(self.nodes[0].getrawmempool()) > 0:
nodes[0].setgenerate(True, 1) self.nodes[0].setgenerate(True, 1)
sync_blocks(nodes) self.sync_all()
final_estimates = [ nodes[0].estimatefee(i) for i in range(1,20) ] final_estimates = [ self.nodes[0].estimatefee(i) for i in range(1,20) ]
print("Final fee estimates: "+str([ str(e) for e in final_estimates])) print("Final fee estimates: "+str([ str(e) for e in final_estimates]))

View File

@ -21,22 +21,64 @@ from util import *
class BitcoinTestFramework(object): class BitcoinTestFramework(object):
# These may be over-ridden by subclasses: # These may be over-ridden by subclasses:
def run_test(self, nodes): def run_test(self):
for node in self.nodes:
assert_equal(node.getblockcount(), 200) assert_equal(node.getblockcount(), 200)
assert_equal(node.getbalance(), 25*50) assert_equal(node.getbalance(), 25*50)
def add_options(self, parser): def add_options(self, parser):
pass pass
def setup_chain(self, tmp_directory): def setup_chain(self):
print("Initializing test directory "+tmp_directory) print("Initializing test directory "+self.options.tmpdir)
initialize_chain(tmp_directory) initialize_chain(self.options.tmpdir)
def setup_network(self, tmp_directory): def setup_network(self, split = False):
nodes = start_nodes(2, tmp_directory) self.nodes = start_nodes(4, self.options.tmpdir)
connect_nodes(nodes[1], 0)
sync_blocks(nodes) # Connect the nodes as a "chain". This allows us
return nodes # to split the network between nodes 1 and 2 to get
# two halves that can work on competing chains.
# If we joined network halves, connect the nodes from the joint
# on outward. This ensures that chains are properly reorganised.
if not split:
connect_nodes(self.nodes[2], 1)
sync_blocks(self.nodes[1:2])
sync_mempools(self.nodes[1:2])
connect_nodes(self.nodes[1], 0)
connect_nodes(self.nodes[3], 2)
self.is_network_split = split
self.sync_all()
def split_network(self):
"""
Split the network of four nodes into nodes 0/1 and 2/3.
"""
assert not self.is_network_split
stop_nodes(self.nodes)
wait_bitcoinds()
self.setup_network(True)
def sync_all(self):
if self.is_network_split:
sync_blocks(self.nodes[:1])
sync_blocks(self.nodes[2:])
sync_mempools(self.nodes[:1])
sync_mempools(self.nodes[2:])
else:
sync_blocks(self.nodes)
sync_mempools(self.nodes)
def join_network(self):
"""
Join the (previously split) network halves together.
"""
assert self.is_network_split
stop_nodes(self.nodes)
wait_bitcoinds()
self.setup_network(False)
def main(self): def main(self):
import optparse import optparse
@ -56,15 +98,14 @@ class BitcoinTestFramework(object):
check_json_precision() check_json_precision()
success = False success = False
nodes = []
try: try:
if not os.path.isdir(self.options.tmpdir): if not os.path.isdir(self.options.tmpdir):
os.makedirs(self.options.tmpdir) os.makedirs(self.options.tmpdir)
self.setup_chain(self.options.tmpdir) self.setup_chain()
nodes = self.setup_network(self.options.tmpdir) self.setup_network()
self.run_test(nodes) self.run_test()
success = True success = True
@ -80,7 +121,7 @@ class BitcoinTestFramework(object):
if not self.options.nocleanup: if not self.options.nocleanup:
print("Cleaning up") print("Cleaning up")
stop_nodes(nodes) stop_nodes(self.nodes)
wait_bitcoinds() wait_bitcoinds()
shutil.rmtree(self.options.tmpdir) shutil.rmtree(self.options.tmpdir)