mirror of
https://github.com/dashpay/dash.git
synced 2024-12-28 21:42:47 +01:00
7897338918
TestNode is a class responsible for all state related to a bitcoind node under test. It stores local state, is responsible for tracking the bitcoind process and delegates unrecognised messages to the RPC connection. This commit changes start_nodes and stop_nodes to start and stop the bitcoind nodes in parallel, making test setup and teardown much faster.
135 lines
5.3 KiB
Python
Executable File
135 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (c) 2017 The Bitcoin Core developers
|
|
# Distributed under the MIT software license, see the accompanying
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
"""Class for bitcoind node under test"""
|
|
|
|
import errno
|
|
import http.client
|
|
import logging
|
|
import os
|
|
import subprocess
|
|
import time
|
|
|
|
from .util import (
|
|
assert_equal,
|
|
get_rpc_proxy,
|
|
rpc_url,
|
|
)
|
|
from .authproxy import JSONRPCException
|
|
|
|
class TestNode():
|
|
"""A class for representing a bitcoind node under test.
|
|
|
|
This class contains:
|
|
|
|
- state about the node (whether it's running, etc)
|
|
- a Python subprocess.Popen object representing the running process
|
|
- an RPC connection to the node
|
|
|
|
To make things easier for the test writer, a bit of magic is happening under the covers.
|
|
Any unrecognised messages will be dispatched to the RPC connection."""
|
|
|
|
def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir):
|
|
self.index = i
|
|
self.datadir = os.path.join(dirname, "node" + str(i))
|
|
self.rpchost = rpchost
|
|
self.rpc_timeout = timewait
|
|
if binary is None:
|
|
self.binary = os.getenv("BITCOIND", "bitcoind")
|
|
else:
|
|
self.binary = binary
|
|
self.stderr = stderr
|
|
self.coverage_dir = coverage_dir
|
|
# Most callers will just need to add extra args to the standard list below. For those callers that need more flexibity, they can just set the args property directly.
|
|
self.extra_args = extra_args
|
|
self.args = [self.binary, "-datadir=" + self.datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i]
|
|
|
|
self.running = False
|
|
self.process = None
|
|
self.rpc_connected = False
|
|
self.rpc = None
|
|
self.url = None
|
|
self.log = logging.getLogger('TestFramework.node%d' % i)
|
|
|
|
def __getattr__(self, *args, **kwargs):
|
|
"""Dispatches any unrecognised messages to the RPC connection."""
|
|
assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection"
|
|
return self.rpc.__getattr__(*args, **kwargs)
|
|
|
|
def start(self):
|
|
"""Start the node."""
|
|
self.process = subprocess.Popen(self.args + self.extra_args, stderr=self.stderr)
|
|
self.running = True
|
|
self.log.debug("bitcoind started, waiting for RPC to come up")
|
|
|
|
def wait_for_rpc_connection(self):
|
|
"""Sets up an RPC connection to the bitcoind process. Returns False if unable to connect."""
|
|
|
|
# Wait for up to 10 seconds for the RPC server to respond
|
|
for _ in range(40):
|
|
assert not self.process.poll(), "bitcoind exited with status %i during initialization" % self.process.returncode
|
|
try:
|
|
self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, coveragedir=self.coverage_dir)
|
|
self.rpc.getblockcount()
|
|
# If the call to getblockcount() succeeds then the RPC connection is up
|
|
self.rpc_connected = True
|
|
self.url = self.rpc.url
|
|
self.log.debug("RPC successfully started")
|
|
return
|
|
except IOError as e:
|
|
if e.errno != errno.ECONNREFUSED: # Port not yet open?
|
|
raise # unknown IO error
|
|
except JSONRPCException as e: # Initialization phase
|
|
if e.error['code'] != -28: # RPC in warmup?
|
|
raise # unknown JSON RPC exception
|
|
except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting
|
|
if "No RPC credentials" not in str(e):
|
|
raise
|
|
time.sleep(0.25)
|
|
raise AssertionError("Unable to connect to bitcoind")
|
|
|
|
def get_wallet_rpc(self, wallet_name):
|
|
assert self.rpc_connected
|
|
assert self.rpc
|
|
wallet_path = "wallet/%s" % wallet_name
|
|
return self.rpc / wallet_path
|
|
|
|
def stop_node(self):
|
|
"""Stop the node."""
|
|
if not self.running:
|
|
return
|
|
self.log.debug("Stopping node")
|
|
try:
|
|
self.stop()
|
|
except http.client.CannotSendRequest:
|
|
self.log.exception("Unable to stop node.")
|
|
|
|
def is_node_stopped(self):
|
|
"""Checks whether the node has stopped.
|
|
|
|
Returns True if the node has stopped. False otherwise.
|
|
This method is responsible for freeing resources (self.process)."""
|
|
if not self.running:
|
|
return True
|
|
return_code = self.process.poll()
|
|
if return_code is not None:
|
|
# process has stopped. Assert that it didn't return an error code.
|
|
assert_equal(return_code, 0)
|
|
self.running = False
|
|
self.process = None
|
|
self.log.debug("Node stopped")
|
|
return True
|
|
return False
|
|
|
|
def node_encrypt_wallet(self, passphrase):
|
|
""""Encrypts the wallet.
|
|
|
|
This causes bitcoind to shutdown, so this method takes
|
|
care of cleaning up resources."""
|
|
self.encryptwallet(passphrase)
|
|
while not self.is_node_stopped():
|
|
time.sleep(0.1)
|
|
self.rpc = None
|
|
self.rpc_connected = False
|