Changeset View
Changeset View
Standalone View
Standalone View
test/functional/mining_getblocktemplate_longpoll.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2014-2019 The Bitcoin Core developers | # Copyright (c) 2014-2019 The Bitcoin Core developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT 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. | ||||
"""Test longpolling with getblocktemplate.""" | """Test longpolling with getblocktemplate.""" | ||||
import random | import random | ||||
import threading | import threading | ||||
from decimal import Decimal | from decimal import Decimal | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.util import get_rpc_proxy | from test_framework.util import get_rpc_proxy | ||||
from test_framework.wallet import MiniWallet | from test_framework.wallet import MiniWallet | ||||
class LongpollThread(threading.Thread): | class LongpollThread(threading.Thread): | ||||
def __init__(self, node): | def __init__(self, node): | ||||
threading.Thread.__init__(self) | threading.Thread.__init__(self) | ||||
# query current longpollid | # query current longpollid | ||||
templat = node.getblocktemplate() | templat = node.getblocktemplate() | ||||
self.longpollid = templat['longpollid'] | self.longpollid = templat["longpollid"] | ||||
# create a new connection to the node, we can't use the same | # create a new connection to the node, we can't use the same | ||||
# connection from two threads | # connection from two threads | ||||
self.node = get_rpc_proxy( | self.node = get_rpc_proxy( | ||||
node.url, 1, timeout=600, coveragedir=node.coverage_dir) | node.url, 1, timeout=600, coveragedir=node.coverage_dir | ||||
) | |||||
def run(self): | def run(self): | ||||
self.node.getblocktemplate({'longpollid': self.longpollid}) | self.node.getblocktemplate({"longpollid": self.longpollid}) | ||||
class GetBlockTemplateLPTest(BitcoinTestFramework): | class GetBlockTemplateLPTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 2 | self.num_nodes = 2 | ||||
self.supports_cli = False | self.supports_cli = False | ||||
def run_test(self): | def run_test(self): | ||||
self.log.info( | self.log.info( | ||||
"Warning: this test will take about 70 seconds in the best case. Be patient.") | "Warning: this test will take about 70 seconds in the best case. Be" | ||||
" patient." | |||||
) | |||||
self.log.info( | self.log.info( | ||||
"Test that longpollid doesn't change between successive getblocktemplate() invocations if nothing else happens") | "Test that longpollid doesn't change between successive getblocktemplate()" | ||||
" invocations if nothing else happens" | |||||
) | |||||
self.generate(self.nodes[0], 10) | self.generate(self.nodes[0], 10) | ||||
templat = self.nodes[0].getblocktemplate() | templat = self.nodes[0].getblocktemplate() | ||||
longpollid = templat['longpollid'] | longpollid = templat["longpollid"] | ||||
# longpollid should not change between successive invocations if | # longpollid should not change between successive invocations if | ||||
# nothing else happens | # nothing else happens | ||||
templat2 = self.nodes[0].getblocktemplate() | templat2 = self.nodes[0].getblocktemplate() | ||||
assert templat2['longpollid'] == longpollid | assert templat2["longpollid"] == longpollid | ||||
self.log.info("Test that longpoll waits if we do nothing") | self.log.info("Test that longpoll waits if we do nothing") | ||||
thr = LongpollThread(self.nodes[0]) | thr = LongpollThread(self.nodes[0]) | ||||
thr.start() | thr.start() | ||||
# check that thread still lives | # check that thread still lives | ||||
# wait 5 seconds or until thread exits | # wait 5 seconds or until thread exits | ||||
thr.join(5) | thr.join(5) | ||||
assert thr.is_alive() | assert thr.is_alive() | ||||
miniwallets = [MiniWallet(node) for node in self.nodes] | miniwallets = [MiniWallet(node) for node in self.nodes] | ||||
self.log.info( | self.log.info( | ||||
"Test that longpoll will terminate if another node generates a block") | "Test that longpoll will terminate if another node generates a block" | ||||
) | |||||
# generate a block on another node | # generate a block on another node | ||||
self.generate(miniwallets[1], 1) | self.generate(miniwallets[1], 1) | ||||
# check that thread will exit now that new transaction entered mempool | # check that thread will exit now that new transaction entered mempool | ||||
# wait 5 seconds or until thread exits | # wait 5 seconds or until thread exits | ||||
thr.join(5) | thr.join(5) | ||||
assert not thr.is_alive() | assert not thr.is_alive() | ||||
self.log.info( | self.log.info( | ||||
"Test that longpoll will terminate if we generate a block ourselves") | "Test that longpoll will terminate if we generate a block ourselves" | ||||
) | |||||
thr = LongpollThread(self.nodes[0]) | thr = LongpollThread(self.nodes[0]) | ||||
thr.start() | thr.start() | ||||
# generate a block on own node | # generate a block on own node | ||||
self.generate(miniwallets[0], 1) | self.generate(miniwallets[0], 1) | ||||
# wait 5 seconds or until thread exits | # wait 5 seconds or until thread exits | ||||
thr.join(5) | thr.join(5) | ||||
assert not thr.is_alive() | assert not thr.is_alive() | ||||
# Add enough mature utxos to the wallets, so that all txs spend | # Add enough mature utxos to the wallets, so that all txs spend | ||||
# confirmed coins | # confirmed coins | ||||
self.generate(self.nodes[0], 100) | self.generate(self.nodes[0], 100) | ||||
self.log.info( | self.log.info( | ||||
"Test that introducing a new transaction into the mempool will terminate the longpoll") | "Test that introducing a new transaction into the mempool will terminate" | ||||
" the longpoll" | |||||
) | |||||
thr = LongpollThread(self.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 | ||||
min_relay_fee = self.nodes[0].getnetworkinfo()["relayfee"] | min_relay_fee = self.nodes[0].getnetworkinfo()["relayfee"] | ||||
fee_rate = min_relay_fee + Decimal('0.10') * random.randint(0, 20) | fee_rate = min_relay_fee + Decimal("0.10") * random.randint(0, 20) | ||||
miniwallets[0].send_self_transfer(from_node=random.choice(self.nodes), | miniwallets[0].send_self_transfer( | ||||
fee_rate=fee_rate) | from_node=random.choice(self.nodes), fee_rate=fee_rate | ||||
) | |||||
# after one minute, every 10 seconds the mempool is probed, so in 80 | # after one minute, every 10 seconds the mempool is probed, so in 80 | ||||
# seconds it should have returned | # seconds it should have returned | ||||
thr.join(60 + 20) | thr.join(60 + 20) | ||||
assert not thr.is_alive() | assert not thr.is_alive() | ||||
if __name__ == '__main__': | if __name__ == "__main__": | ||||
GetBlockTemplateLPTest().main() | GetBlockTemplateLPTest().main() |