Skip to content
Snippets Groups Projects
Commit 64de6d51 authored by Dariusz Kędzierski's avatar Dariusz Kędzierski
Browse files

Merge branch 'dk-functional-example' of gitlab.syncad.com:hive/tests_api into dk-functional-example

parents eb34a1e4 9e33ffa6
No related tags found
1 merge request!122Runner for functional tests with example
Showing
with 1873 additions and 0 deletions
from .common import *
from .hive_node import *
from .hivemind import *
from .test_runner import *
# -*- coding: utf-8 -*-
import logging
from junit_xml import TestCase
import time
import traceback
import sys
DEFAULT_LOG_FORMAT = '%(asctime)-15s - %(name)s - %(levelname)s - %(message)s'
DEFAULT_LOG_LEVEL = logging.INFO
MODULE_NAME = "common"
logger = logging.getLogger(MODULE_NAME)
logger.setLevel(DEFAULT_LOG_LEVEL)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(DEFAULT_LOG_LEVEL)
ch.setFormatter(logging.Formatter(DEFAULT_LOG_FORMAT))
logger.addHandler(ch)
def send_rpc_query(target_node : str, payload : dict) -> dict:
from requests import post
from json import dumps
resp = post(target_node, data = dumps(payload))
if resp.status_code != 200:
print(resp.json())
raise Exception("{} returned non 200 code".format(payload["method"]))
return resp.json()
def get_random_id() -> str:
from uuid import uuid4
return str(uuid4())
def get_current_block_number(source_node) -> int:
payload = {
"jsonrpc" : "2.0",
"id" : get_random_id(),
"method" : "database_api.get_dynamic_global_properties",
"params" : {}
}
from requests import post
from json import dumps, loads
try:
resp = post(source_node, data = dumps(payload))
if resp.status_code != 200:
return -1
data = resp.json()["result"]
return int(data["head_block_number"])
except Exception as ex:
return -1
def wait_n_blocks(source_node : str, blocks : int, timeout : int = 60):
from time import sleep
starting_block = get_current_block_number(source_node)
cntr = 0
while starting_block == -1 and cntr < timeout:
starting_block = get_current_block_number(source_node)
sleep(1)
cntr += 1
if cntr >= timeout:
raise TimeoutError("Timeout in waiting for blocks. Hived is not running?")
current_block = starting_block
cntr = 0
while current_block - starting_block < blocks and cntr < timeout:
current_block = get_current_block_number(source_node)
sleep(1)
cntr += 1
if cntr >= timeout:
raise TimeoutError("Timeout in waiting for blocks. Hived is not running?")
def debug_generate_blocks(target_node : str, debug_key : str, count : int) -> dict:
if count < 0:
raise ValueError("Count must be a positive non-zero number")
payload = {
"jsonrpc": "2.0",
"id" : get_random_id(),
"method": "debug_node_api.debug_generate_blocks",
"params": {
"debug_key": debug_key,
"count": count,
"skip": 0,
"miss_blocks": 0,
"edit_if_needed": True
}
}
return send_rpc_query(target_node, payload)
def debug_generate_blocks_until(target_node : str, debug_key : str, timestamp : str, generate_sparsely : bool = True) -> dict:
payload = {
"jsonrpc": "2.0",
"id" : get_random_id(),
"method": "debug_node_api.debug_generate_blocks_until",
"params": {
"debug_key": debug_key,
"head_block_time": timestamp,
"generate_sparsely": generate_sparsely
}
}
return send_rpc_query(target_node, payload)
def debug_set_hardfork(target_node : str, hardfork_id : int) -> dict:
if hardfork_id < 0:
raise ValueError( "hardfork_id cannot be negative" )
payload = {
"jsonrpc": "2.0",
"id" : get_random_id(),
"method": "debug_node_api.debug_set_hardfork",
"params": {
"hardfork_id": hardfork_id
}
}
return send_rpc_query(target_node, payload)
def debug_has_hardfork(target_node : str, hardfork_id : int) -> dict:
payload = {
"jsonrpc": "2.0",
"id" : get_random_id(),
"method": "debug_node_api.debug_has_hardfork",
"params": {
"hardfork_id": hardfork_id
}
}
return send_rpc_query(target_node, payload)
def debug_get_witness_schedule(target_node : str) -> dict:
payload = {
"jsonrpc": "2.0",
"id" : get_random_id(),
"method": "debug_node_api.debug_get_witness_schedule",
"params": {}
}
return send_rpc_query(target_node, payload)
def debug_get_hardfork_property_object(target_node : str) -> dict:
payload = {
"jsonrpc": "2.0",
"id" : get_random_id(),
"method": "debug_node_api.debug_get_hardfork_property_object",
"params": {}
}
return send_rpc_query(target_node, payload)
def get_date_as_isostr(date):
return date.replace(microsecond=0).isoformat()
def get_isostr_start_end_date(now, start_date_delta, end_date_delta):
from datetime import timedelta
start_date = now + timedelta(days = start_date_delta)
end_date = start_date + timedelta(days = end_date_delta)
start_date = start_date.replace(microsecond=0).isoformat()
end_date = end_date.replace(microsecond=0).isoformat()
return start_date, end_date
def save_screen_cfg(cfg_file_name, log_file_path):
"""Creates a config file for screen command. In config file we configure logging path and interval.
Args:
cfg_file_name -- file name for screen config file,
log_file_path -- path to log file.
"""
with open(cfg_file_name, "w") as cfg:
cfg.write("logfile {0}\n".format(log_file_path))
cfg.write("deflog on\n")
cfg.write("logfile flush 1\n")
def save_pid_file(pid_file_name, exec_name, port, start_time):
"""Creates PID file which indicate running keosd or nodeos process.
Args:
pid_file_name -- file name for pid file,
exec_name -- name of the exectutable bound to this pid file,
port -- port number for running executable,
start_time -- execution start time.
"""
with open(pid_file_name, "w") as pid_file:
pid_file.write("{0}-{1}-{2}\n".format(exec_name, port, start_time))
def wait_for_string_in_file(log_file_name, string, timeout):
"""Blocks program execution until a given string is found in given file.
Args:
log_file_name -- path to scanned file,
string -- sting to be found,
timout -- block timeout in seconds, after this time exception will be raised.
"""
logger.info("Waiting for string \"{}\" in file {}".format(string, log_file_name))
step = 1
to_timeout = 0.
from time import sleep
from os.path import exists
while True:
sleep(step)
to_timeout = to_timeout + step
if timeout is not None and to_timeout >= timeout:
msg = "Timeout during wait for string {0}".format(string)
logger.error(msg)
raise TimeoutError(msg)
if exists(log_file_name):
with open(log_file_name, "r") as log_file:
leave = False
for line in log_file.readlines():
if string in line:
leave = True
break
if leave:
break
def get_last_line_of_file(file_name):
"""Reads and returns last line of given file.
Args:
file_name -- path to a file.
"""
last_line = ""
from os import SEEK_CUR, SEEK_END
with open(file_name, "r") as f:
f.seek(-2, SEEK_END)
while f.read(1) != b'\n':
f.seek(-2, SEEK_CUR)
last_line = f.readline().decode()
return last_line
def kill_process(pid_file_name, proc_name, ip_address, port):
"""Attempts to stop a process with given PID listening on port at ip_address. Process data is read from pid_file_name.
Args:
pid_file_name -- path to pid file,
proc_name -- executable name,
ip_address -- executable ip address,
port -- executable port number.
"""
logger.info("Terminating {0} process running on port {1}".format(proc_name, port))
pids = []
pid_name = None
try:
from os import popen, kill, remove
from os.path import exists
with open(pid_file_name, "r") as pid_file:
pid_name = pid_file.readline()
pid_name = pid_name.strip()
if pid_name is not None:
for line in popen("ps ax | grep " + proc_name + " | grep -v grep"):
if pid_name in line:
line = line.strip().split()
pids.append(line[0])
for pid in pids:
for line in popen("ps --no-header --ppid {0}".format(pid)):
line = line.strip().split()
kill(int(line[0]), 2)
kill(int(pid), 2)
if exists(pid_file_name):
remove(pid_file_name)
logger.info("Done...")
else:
logger.warning("No such process: {0}".format(pid_name))
except Exception as ex:
logger.error("Process {0} cannot be killed. Reason: {1}".format(proc_name, ex))
def detect_process_by_name(proc_name, exec_path, port):
"""Checks if process of given name runs on given ip_address and port.
Args:
proc_name -- process name,
exec_path -- path to executable,
port -- process port.
"""
pids = []
from os import popen
for line in popen("ps ax | grep " + proc_name + " | grep -v grep"):
if exec_path in line and str(port) in line:
line = line.strip().split()
pids.append(line[0])
if pids:
return True
return False
BLOCK_TYPE_HEADBLOCK = "within_reversible_block"
BLOCK_TYPE_IRREVERSIBLE = "within_irreversible_block"
def block_until_transaction_in_block(node_url, transaction_id, block_type = BLOCK_TYPE_HEADBLOCK, timeout = 60.):
logger.info("Block until transaction_id: {0} is {1}".format(transaction_id, block_type))
from time import sleep
from requests import post
step = 1.
timeout_cnt = 0.
while True:
query = {
"id" : "{}".format(get_random_id()),
"jsonrpc":"2.0",
"method":"transaction_status_api.find_transaction",
"params": {
"transaction_id": transaction_id
}
}
response = post(node_url, json=query)
transaction_status = response.get("status", None)
if transaction_status is not None:
if transaction_status == block_type:
logger.info("Transaction id: {0} is {1}".format(transaction_id, block_type))
return
logger.info("Transaction id: {0} not {1}".format(transaction_id, block_type))
sleep(step)
timeout_cnt = timeout_cnt + step
if timeout_cnt > timeout:
msg = "Timeout reached during block_until_transaction_in_block"
logger.error(msg)
raise TimeoutError(msg)
junit_test_cases = []
def junit_test_case(method):
def log_test_case(*args, **kw):
start_time = time.time()
error = None
try:
result = method(*args, **kw)
except:
e = sys.exc_info()
error = traceback.format_exception(e[0], e[1], e[2])
raise
finally:
end_time = time.time()
test_case = TestCase(method.__name__, method.__name__, end_time - start_time, '', '')
if error is not None:
test_case.add_failure_info(output = error)
junit_test_cases.append(test_case)
return log_test_case
#!/usr/bin/python3
import json
import logging
import sys
import os
import subprocess
import datetime
from .common import DEFAULT_LOG_FORMAT, DEFAULT_LOG_LEVEL
from threading import Lock
MODULE_NAME = "hive_node"
logger = logging.getLogger(MODULE_NAME)
logger.setLevel(DEFAULT_LOG_LEVEL)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(DEFAULT_LOG_LEVEL)
ch.setFormatter(logging.Formatter(DEFAULT_LOG_FORMAT))
logger.addHandler(ch)
from typing import NamedTuple
class HiveNode(object):
hived_binary = None
hived_process = None
hived_lock = Lock()
hived_data_dir = None
hived_args = list()
logger = logging.getLogger(MODULE_NAME + ".HiveNode")
def __init__(self, binary_path : str, working_dir : str, binary_args : list):
self.logger.info("New hive node")
if not os.path.exists(binary_path):
raise ValueError("Path to hived binary is not valid.")
if not os.path.isfile(binary_path):
raise ValueError("Path to hived binary must point to file")
self.hived_binary = binary_path
if not os.path.exists(working_dir):
raise ValueError("Path to data directory is not valid")
if not os.path.isdir(working_dir):
raise ValueError("Data directory is not valid directory")
self.hived_data_dir = working_dir
if binary_args:
self.hived_args.extend(binary_args)
def __enter__(self):
self.hived_lock.acquire()
from subprocess import Popen, PIPE
from time import sleep
hived_command = [
self.hived_binary,
"--data-dir={}".format(self.hived_data_dir)
]
hived_command.extend(self.hived_args)
self.hived_process = Popen(hived_command, stdout=PIPE, stderr=None)
self.hived_process.poll()
sleep(5)
if self.hived_process.returncode:
raise Exception("Error during starting node")
def get_output(self):
out, err = self.hived_process.communicate()
return out
def __exit__(self, exc, value, tb):
self.logger.info("Closing node")
from signal import SIGINT, SIGTERM
from time import sleep
if self.hived_process is not None:
self.hived_process.poll()
if self.hived_process.returncode != 0:
self.hived_process.send_signal(SIGINT)
sleep(7)
self.hived_process.poll()
if self.hived_process.returncode != 0:
self.hived_process.send_signal(SIGTERM)
sleep(7)
self.hived_process.poll()
if self.hived_process.returncode != 0:
raise Exception("Error during stopping node. Manual intervention required.")
self.hived_process = None
self.hived_lock.release()
class HiveNodeInScreen(object):
def __init__(self, hive_executable, working_dir, config_src_path, run_using_existing_data = False, node_is_steem = False):
self.logger = logging.getLogger(MODULE_NAME + ".HiveNodeInScreen")
self.logger.info("New hive node")
self.hive_executable = hive_executable
self.working_dir = working_dir
self.config_src_path = config_src_path
self.node_is_steem = node_is_steem
# usefull when we want to do a replay
if not run_using_existing_data:
from shutil import rmtree, copy
# remove old data from node
if os.path.exists(self.working_dir):
rmtree(self.working_dir)
os.makedirs(self.working_dir+"/blockchain")
# copy config file to working dir
copy(self.config_src_path, self.working_dir + "/config.ini")
self.hive_config = self.parse_node_config_file(self.working_dir + "/config.ini")
self.ip_address, self.port = self.hive_config["webserver-http-endpoint"][0].split(":")
self.ip_address = "http://{}".format(self.ip_address)
self.node_running = False
def get_from_config(self, key):
return self.hive_config.get(key, None)
def get_node_url(self):
return "{}:{}/".format(self.ip_address, self.port)
def is_running(self):
return self.node_running
def parse_node_config_file(self, config_file_name):
ret = dict()
lines = None
with open(config_file_name, "r") as f:
lines = f.readlines()
for line in lines:
proc_line = line.strip()
if proc_line:
if proc_line.startswith("#"):
continue
k, v = proc_line.split("=", 1)
k = k.strip()
v = v.strip()
if k in ret:
ret[k].append(v)
else:
ret[k] = [v]
return ret
def run_hive_node(self, additional_params = [], wait_for_blocks = True):
from .common import detect_process_by_name, save_screen_cfg, save_pid_file, wait_n_blocks, wait_for_string_in_file, kill_process
if detect_process_by_name("hived" if not self.node_is_steem else "steemd", self.hive_executable, self.port):
msg = "{0} process is running on {1}:{2}. Please terminate that process and try again.".format("hive", self.ip_address, self.port)
raise ProcessLookupError(msg)
self.logger.info("*** START NODE at {0}:{1} in {2}".format(self.ip_address, self.port, self.working_dir))
parameters = [
self.hive_executable,
"-d",
self.working_dir,
"--advanced-benchmark",
"--sps-remove-threshold",
"-1"
]
parameters = parameters + additional_params
self.pid_file_name = "{0}/run_hive-{1}.pid".format(self.working_dir, self.port)
current_time_str = datetime.datetime.now().strftime("%Y-%m-%d")
log_file_name = "{0}/{1}-{2}-{3}.log".format(self.working_dir, "hived", self.port, current_time_str)
screen_cfg_name = "{0}/hive_screen-{1}.cfg".format(self.working_dir, self.port)
save_screen_cfg(screen_cfg_name, log_file_name)
screen_params = [
"screen",
"-m",
"-d",
"-L",
"-c",
screen_cfg_name,
"-S",
"{0}-{1}-{2}".format("hived", self.port, current_time_str)
]
parameters = screen_params + parameters
self.logger.info("Running hived with command: {0}".format(" ".join(parameters)))
try:
subprocess.Popen(parameters)
save_pid_file(self.pid_file_name, "hived", self.port, current_time_str)
# we will allow for screen to setup and die maybe?
from time import sleep
sleep(5)
# now it should be dead
if not detect_process_by_name("hived" if not self.node_is_steem else "steemd", self.hive_executable, self.port):
msg = "{0} process is not running on {1}:{2}. Please check logs.".format("hive", self.ip_address, self.port)
raise ProcessLookupError(msg)
if "--replay-blockchain" in parameters:
wait_for_string_in_file(log_file_name, "start listening for ws requests", None)
else:
if wait_for_blocks:
wait_n_blocks("{}:{}".format(self.ip_address, self.port), 5)
else:
wait_for_string_in_file(log_file_name, "start listening for ws requests", 20.)
self.node_running = True
self.logger.info("Node at {0}:{1} in {2} is up and running...".format(self.ip_address, self.port, self.working_dir))
except Exception as ex:
self.logger.error("Exception during hived run: {0}".format(ex))
kill_process(self.pid_file_name, "hived" if not self.node_is_steem else "steemd", self.ip_address, self.port)
self.node_running = False
def stop_hive_node(self):
from .common import kill_process
self.logger.info("Stopping node at {0}:{1}".format(self.ip_address, self.port))
kill_process(self.pid_file_name, "hived" if not self.node_is_steem else "steemd", self.ip_address, self.port)
self.node_running = False
def __enter__(self):
self.run_hive_node()
def __exit__(self, exc, value, tb):
self.stop_hive_node()
if __name__ == "__main__":
KEEP_GOING = True
def sigint_handler(signum, frame):
logger.info("Shutting down...")
global KEEP_GOING
from time import sleep
KEEP_GOING = False
sleep(3)
sys.exit(0)
def main():
try:
import signal
signal.signal(signal.SIGINT, sigint_handler)
plugins = ["chain","p2p","webserver","json_rpc","debug_node"]
config = "# Simple config file\n" \
+ "shared-file-size = 1G\n" \
+ "enable-stale-production = true\n" \
+ "p2p-endpoint = 127.0.0.1:2001\n" \
+ "webserver-http-endpoint = 127.0.0.1:8095\n" \
+ "webserver-ws-endpoint = 127.0.0.1:8096\n" \
+ "plugin = witness debug_node {}\n".format(" ".join(plugins)) \
+ "plugin = database_api debug_node_api block_api\n" \
+ "witness = \"initminer\"\n" \
+ "private-key = 5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n\n" \
+ "required-participation = 0"
binary_path = "/home/dariusz-work/Builds/hive/programs/hived/hived"
work_dir = "/home/dariusz-work/hive-data"
print(config)
with open(work_dir + "/config.ini", "w") as conf_file:
conf_file.write(config)
node = HiveNode(binary_path, work_dir, [])
from time import sleep
from .common import wait_n_blocks, debug_generate_blocks
with node:
print("Waiting 10 blocks")
wait_n_blocks("http://127.0.0.1:8095", 10)
print("Done...")
print(debug_generate_blocks("http://127.0.0.1:8095", "5JHNbFNDg834SFj8CMArV6YW7td4zrPzXveqTfaShmYVuYNeK69", 100))
while(KEEP_GOING):
sleep(1)
except Exception as ex:
logger.exception("Exception: {}".format(ex))
sys.exit(1)
main()
#!/usr/bin/python3
import json
import logging
import sys
import os
import subprocess
import datetime
from .common import DEFAULT_LOG_FORMAT, DEFAULT_LOG_LEVEL
MODULE_NAME = "hivemind"
logger = logging.getLogger(MODULE_NAME)
logger.setLevel(DEFAULT_LOG_LEVEL)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(DEFAULT_LOG_LEVEL)
ch.setFormatter(logging.Formatter(DEFAULT_LOG_FORMAT))
logger.addHandler(ch)
class HivemindInScreen(object):
def __init__(self, hivemind_executable, mode, hivemind_port, database_url, working_dir = ".", hived_address="http://127.0.0.1", hived_port=8090):
self.logger = logging.getLogger(MODULE_NAME + ".HivemindInScreen")
self.logger.info("New hivemind instance")
self.hivemind_executable = hivemind_executable
assert mode in ['sync', 'server'], "Allowed modes are: `sync` and `server`"
self.hivemind_mode = mode
self.hivemind_port = hivemind_port
self.hived_address = hived_address
self.hived_port = hived_port
self.hivemind_database_url = database_url
self.working_dir = working_dir
self.hivemind_running = False
def get_address(self):
return "http://{}:{}/".format("127.0.0.1", self.hivemind_port)
def is_running(self):
return self.hivemind_running
def run_hivemind(self, params = []):
from .common import detect_process_by_name, save_screen_cfg, save_pid_file, wait_for_string_in_file, kill_process
from json import dumps
start_params = [
self.hivemind_executable,
self.hivemind_mode,
'--steemd-url',
dumps({"default":"{}:{}".format(self.hived_address, self.hived_port)}),
'--database-url',
self.hivemind_database_url,
'--http-server-port',
str(self.hivemind_port)
]
self.pid_file_name = "{0}/run_hivemind-{1}.pid".format(self.working_dir, self.hivemind_port)
current_time_str = datetime.datetime.now().strftime("%Y-%m-%d")
self.log_file_name = "{0}/{1}-{2}-{3}.log".format(self.working_dir, "hive", self.hivemind_port, current_time_str)
screen_cfg_name = "{0}/hive_screen-{1}.cfg".format(self.working_dir, self.hivemind_port)
start_params = start_params + params
save_screen_cfg(screen_cfg_name, self.log_file_name)
screen_params = [
"screen",
"-m",
"-d",
"-L",
"-c",
screen_cfg_name,
"-S",
"{0}-{1}-{2}".format("hive", self.hivemind_port, current_time_str)
]
start_params = screen_params + start_params
self.logger.info("Running hivemind with command: {0}".format(" ".join(start_params)))
try:
save_pid_file(self.pid_file_name, "hive", self.hivemind_port, current_time_str)
subprocess.Popen(start_params)
# we will allow for screen to setup and die maybe?
from time import sleep
sleep(5)
# now it should be dead
if not detect_process_by_name("hive", self.hivemind_executable, self.hivemind_port):
msg = "{0} process is not running on {1}:{2}. Please check logs.".format("hive", "http://0.0.0.0", self.hivemind_port)
raise ProcessLookupError(msg)
self.hivemind_running = True
self.logger.info("Hivemind at {0}:{1} in {2} is up and running...".format("http://0.0.0.0", self.hivemind_port, self.working_dir))
except Exception as ex:
self.logger.exception("Exception during hivemind run: {0}".format(ex))
kill_process(self.pid_file_name, "hive", "http://0.0.0.0", self.hivemind_port)
self.hivemind_running = False
def stop_hivemind(self):
from .common import kill_process
self.logger.info("Stopping hivemind at {0}:{1}".format("http://0.0.0.0", self.hivemind_port))
kill_process(self.pid_file_name, "hive", "http://0.0.0.0", self.hivemind_port)
self.node_running = False
def __enter__(self):
self.run_hivemind()
def __exit__(self, exc, value, tb):
self.stop_hivemind()
if __name__ == "__main__":
from time import sleep
def main():
hivemind = None
try:
hivemind = HivemindInScreen("hive", "sync", 8080, "postgresql://hive@localhost:5432/hive3", "/tmp")
hivemind.run_hivemind()
sleep(30)
if hivemind is not None and hivemind.is_running():
hivemind.stop_hivemind()
except Exception as ex:
if hivemind is not None and hivemind.is_running():
hivemind.stop_hivemind()
logger.exception("Exception: {}".format(ex))
sys.exit(1)
main()
\ No newline at end of file
# Appender definition json: {"appender", "stream", "file"} Can only specify a file OR a stream
log-appender = {"appender":"stderr","stream":"std_error"} {"appender":"p2p","file":"logs/p2p/p2p.log"}
# Logger definition json: {"name", "level", "appender"}
log-logger = {"name":"default","level":"all","appender":"stderr"} {"name":"p2p","level":"all","appender":"p2p"}
# Whether to print backtrace on SIGSEGV
backtrace = yes
# Plugin(s) to enable, may be specified multiple times
plugin = witness database_api account_by_key_api network_broadcast_api condenser_api block_api transaction_status_api debug_node_api market_history_api account_history_rocksdb account_history_api
# Disables automatic account history trimming
history-disable-pruning = 0
# The location of the rocksdb database for account history. By default it is $DATA_DIR/blockchain/account-history-rocksdb-storage
account-history-rocksdb-path = "blockchain/account-history-rocksdb-storage"
# Where to export data (NONE to discard)
block-data-export-file = NONE
# How often to print out block_log_info (default 1 day)
block-log-info-print-interval-seconds = 86400
# Whether to defer printing until block is irreversible
block-log-info-print-irreversible = 1
# Where to print (filename or special sink ILOG, STDOUT, STDERR)
block-log-info-print-file = ILOG
# the location of the chain shared memory files (absolute path or relative to application data dir)
shared-file-dir = "blockchain"
# Size of the shared memory file. Default: 54G. If running a full node, increase this value to 200G.
shared-file-size = 8G
# A 2 precision percentage (0-10000) that defines the threshold for when to autoscale the shared memory file. Setting this to 0 disables autoscaling. Recommended value for consensus node is 9500 (95%). Full node is 9900 (99%)
shared-file-full-threshold = 0
# A 2 precision percentage (0-10000) that defines how quickly to scale the shared memory file. When autoscaling occurs the file's size will be increased by this percent. Setting this to 0 disables autoscaling. Recommended value is between 1000-2000 (10-20%)
shared-file-scale-rate = 0
# Set the maximum size of cached feed for an account
follow-max-feed-size = 500
# Block time (in epoch seconds) when to start calculating feeds
follow-start-feeds = 0
# Track market history by grouping orders into buckets of equal size measured in seconds specified as a JSON array of numbers
market-history-bucket-size = [15,60,300,3600,86400]
# How far back in time to track history for each bucket size, measured in the number of buckets (default: 5760)
market-history-buckets-per-size = 5760
# The IP address and port of a remote peer to sync with.
p2p-seed-node = 127.0.0.1:2001
# Skip rejecting transactions when account has insufficient RCs. This is not recommended.
rc-skip-reject-not-enough-rc = 0
# Generate historical resource credits
rc-compute-historical-rc = 0
# Size to batch statsd messages.
statsd-batchsize = 1
# Block time (in epoch seconds) when to start calculating promoted content. Should be 1 week prior to current time.
tags-start-promoted = 0
# Skip updating tags on startup. Can safely be skipped when starting a previously running node. Should not be skipped when reindexing.
tags-skip-startup-update = 0
# Defines the number of blocks from the head block that transaction statuses will be tracked.
transaction-status-block-depth = 64000
# Defines the block number the transaction status plugin will begin tracking.
transaction-status-track-after-block = 0
# Local http endpoint for webserver requests.
webserver-http-endpoint = 127.0.0.1:8090
# Local websocket endpoint for webserver requests.
webserver-ws-endpoint = 127.0.0.1:8090
# Number of threads used to handle queries. Default: 32.
webserver-thread-pool-size = 32
# Enable block production, even if the chain is stale.
enable-stale-production = 0
# Percent of witnesses (0-99) that must be participating in order to produce blocks
required-participation = 0
# name of witness controlled by this node (e.g. initwitness )
witness = "initminer"
# WIF PRIVATE KEY to be used by one or more witnesses or miners
private-key = 5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n
# Skip enforcing bandwidth restrictions. Default is true in favor of rc_plugin.
witness-skip-enforce-bandwidth = 1
#!/usr/bin/python3
import sys
from .hivemind import *
from .hive_node import *
from .common import wait_for_string_in_file
from uuid import uuid4
from time import sleep
import logging
import os
LOG_LEVEL = logging.INFO
LOG_FORMAT = "%(asctime)-15s - %(name)s - %(levelname)s - %(message)s"
MAIN_LOG_PATH = "test_runner.log"
log_dir = os.environ.get("TEST_LOG_DIR", None)
if log_dir is not None:
MAIN_LOG_PATH = log_dir + "/" + MAIN_LOG_PATH
else:
MAIN_LOG_PATH = "./" + MAIN_LOG_PATH
MODULE_NAME = "test_runner"
logger = logging.getLogger(MODULE_NAME)
logger.setLevel(LOG_LEVEL)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(LOG_LEVEL)
ch.setFormatter(logging.Formatter(LOG_FORMAT))
fh = logging.FileHandler(MAIN_LOG_PATH)
fh.setLevel(LOG_LEVEL)
fh.setFormatter(logging.Formatter(LOG_FORMAT))
if not logger.hasHandlers():
logger.addHandler(ch)
logger.addHandler(fh)
try:
from beem import Hive
except Exception as ex:
logger.error("beem library is not installed.")
sys.exit(1)
class TestRunner(object):
def __init__(self, hived_path, wif, node_url, database_url, working_dir, config_path):
self.logger = logging.getLogger(MODULE_NAME + ".TestRunner")
self.hived_node = None
self.hived_node_client = None
self.hivemind_sync = None
self.hivemind_server = None
self.hived_keys = [wif]
self.hived_path = hived_path
self.hived_wif = wif
self.hived_node_url = node_url
self.hivemind_database_url = database_url
self.hived_working_dir = working_dir
self.hived_config_path = config_path
def on_before_hived_run(self):
pass
def on_before_hivemind_sync_run(self):
pass
def on_before_hivemind_server_run(self):
pass
def on_after_hivemind_server_run(self):
from time import sleep
sleep(60)
def run(self):
self.logger.info("Executing before hived run hook")
self.on_before_hived_run()
if self.hived_path:
self.logger.info("Running hived via {} in {} with config {}".format(self.hived_path,
self.hived_working_dir,
self.hived_config_path)
)
self.hived_node = HiveNodeInScreen(
self.hived_path,
self.hived_working_dir,
self.hived_config_path
)
if self.hived_node is not None:
self.hived_node.run_hive_node(["--enable-stale-production"])
try:
if self.hived_node.is_running():
self.hived_node_client = Hive(node = [self.hived_node_url], no_broadcast = False,
keys = self.hived_keys
)
self.logger.info("Chain prefix is: {}".format(self.hived_node_client.prefix))
self.logger.info("Chain ID is: {}".format(self.hived_node_client.get_config()["HIVE_CHAIN_ID"]))
self.logger.info("Executing before hivemind initial sync run hook")
self.on_before_hivemind_sync_run()
self.logger.info("Start hivemind instance and perform initial sync")
self.hivemind_sync = HivemindInScreen("hive", "sync", 8080, self.hivemind_database_url, "/tmp")
self.hivemind_sync.run_hivemind()
wait_for_string_in_file(self.hivemind_sync.log_file_name, "Initial sync complete", None)
self.logger.info("Initial sync complete, switching to live sync mode")
self.logger.info("Executing before hivemind server run hook")
self.on_before_hivemind_server_run()
self.logger.info("Start hivemind instance as server")
self.hivemind_server = HivemindInScreen("hive", "server", 8081, self.hivemind_database_url, "/tmp")
self.hivemind_server.run_hivemind()
self.logger.info("Executing after hivemind server run hook")
self.on_after_hivemind_server_run()
self.logger.info("Stopping hived and all hivemind instances")
if self.hivemind_server is not None:
self.hivemind_server.stop_hivemind()
if self.hivemind_sync is not None:
self.hivemind_sync.stop_hivemind()
if self.hived_node is not None:
self.hived_node.stop_hive_node()
return True
return False
except Exception as ex:
self.logger.exception("Exception: {}".format(ex))
if self.hivemind_server is not None:
self.hivemind_server.stop_hivemind()
if self.hivemind_sync is not None:
self.hivemind_sync.stop_hivemind()
if self.hived_node is not None:
self.hived_node.stop_hive_node()
raise ex
#!/usr/bin/python3
import sys
sys.path.append("../../")
import hive_utils
from uuid import uuid4
from time import sleep
import logging
import os
LOG_LEVEL = logging.INFO
LOG_FORMAT = "%(asctime)-15s - %(name)s - %(levelname)s - %(message)s"
MAIN_LOG_PATH = "functional_example.log"
log_dir = os.environ.get("TEST_LOG_DIR", None)
if log_dir is not None:
MAIN_LOG_PATH = log_dir + "/" + MAIN_LOG_PATH
else:
MAIN_LOG_PATH = "./" + MAIN_LOG_PATH
MODULE_NAME = "Functional-Example"
logger = logging.getLogger(MODULE_NAME)
logger.setLevel(LOG_LEVEL)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(LOG_LEVEL)
ch.setFormatter(logging.Formatter(LOG_FORMAT))
fh = logging.FileHandler(MAIN_LOG_PATH)
fh.setLevel(LOG_LEVEL)
fh.setFormatter(logging.Formatter(LOG_FORMAT))
if not logger.hasHandlers():
logger.addHandler(ch)
logger.addHandler(fh)
try:
from beem import Hive
except Exception as ex:
logger.error("beem library is not installed.")
sys.exit(1)
def create_accounts(node, creator, accounts):
""" Create accounts given as a list using `creator` as a creator account """
for account in accounts:
logger.info("Creating account: {}".format(account['name']))
node.create_account(account['name'],
owner_key=account['public_key'],
active_key=account['public_key'],
posting_key=account['public_key'],
memo_key=account['public_key'],
store_keys = False,
creator=creator,
asset='TESTS'
)
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def transfer_to_vesting(node, from_account, accounts, amount, asset):
""" Transfer assets to vesting from `from_account` to accounts given in list """
from beem.account import Account
for acnt in accounts:
logger.info("Transfer to vesting from {} to {} amount {} {}".format(
from_account, acnt['name'], amount, asset)
)
acc = Account(from_account, hive_instance=node)
acc.transfer_to_vesting(amount, to = acnt['name'], asset = asset)
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def transfer_assets_to_accounts(node, from_account, accounts, amount, asset, wif=None):
""" Transfer assets `from_account` to accounts given in list """
from beem.account import Account
for acnt in accounts:
logger.info("Transfer from {} to {} amount {} {}".format(from_account,
acnt['name'], amount, asset)
)
acc = Account(from_account, hive_instance=node)
acc.transfer(acnt['name'], amount, asset, memo = "initial transfer")
if wif is not None:
hive_utils.debug_generate_blocks(node.rpc.url, wif, 5)
else:
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def get_permlink(account):
""" Helper for permlink creation """
return "functional-example-title-{}".format(account)
def create_posts(node, accounts, wif=None):
""" Create example posts - one post for one account. Accounts given in list format """
logger.info("Creating posts...")
for acnt in accounts:
logger.info("New post ==> ({},{},{},{},{})".format(
"Hivepy example post title [{}]".format(acnt['name']),
"Hivepy example post body [{}]".format(acnt['name']),
acnt['name'],
get_permlink(acnt['name']),
"example"
))
node.post("Hivepy example post title [{}]".format(acnt['name']),
"Hivepy example post body [{}]".format(acnt['name']),
acnt['name'],
permlink = get_permlink(acnt['name']),
tags = "example")
if wif is not None:
hive_utils.debug_generate_blocks(node.rpc.url, wif, 5)
else:
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def print_balance(node, accounts):
""" Print balance for accounts given as a list """
from beem.account import Account
balances = []
balances_str = []
for acnt in accounts:
ret = Account(acnt['name'], hive_instance=node).json()
hbd = ret.get('hbd_balance', None)
if hbd is not None:
hbd = hbd.get('amount')
balances_str.append("{}:{}".format(acnt['name'], hbd))
balances.append(hbd)
logger.info("Balances ==> {}".format(",".join(balances_str)))
return balances
class HivemindFunctionalRunner(hive_utils.test_runner.TestRunner):
def __init__(self, creator, hived_path, wif, node_url, database_url, working_dir, config_path):
super().__init__(hived_path, wif, node_url, database_url, working_dir, config_path)
self.creator = creator
self.accounts = [
# place accounts here in the format: {'name' : name, 'private_key' : private-key, 'public_key' : public-key}
{"name" : "tester001", "private_key" : "5KQeu7SdzxT1DiUzv7jaqwkwv1V8Fi7N8NBZtHugWYXqVFH1AFa", "public_key" : "TST8VfiahQsfS1TLcnBfp4NNfdw67uWweYbbUXymbNiDXVDrzUs7J"},
{"name" : "tester002", "private_key" : "5KgfcV9bgEen3v9mxkoGw6Rhuf2giDRZTHZjzwisjkrpF4FUh3N", "public_key" : "TST5gQPYm5bs9dRPHpqBy6dU32M8FcoKYFdF4YWEChUarc9FdYHzn"},
{"name" : "tester003", "private_key" : "5Jz3fcrrgKMbL8ncpzTdQmdRVHdxMhi8qScoxSR3TnAFUcdyD5N", "public_key" : "TST57wy5bXyJ4Z337Bo6RbinR6NyTRJxzond5dmGsP4gZ51yN6Zom"},
{"name" : "tester004", "private_key" : "5KcmobLVMSAVzETrZxfEGG73Zvi5SKTgJuZXtNgU3az2VK3Krye", "public_key" : "TST8dPte853xAuLMDV7PTVmiNMRwP6itMyvSmaht7J5tVczkDLa5K"},
{"name" : "tester005", "private_key" : "5Hy4vEeYmBDvmXipe5JAFPhNwCnx7NfsfyiktBTBURn9Qt1ihcA", "public_key" : "TST7CP7FFjvG55AUeH8riYbfD8NxTTtFH32ekQV4YFXmV6gU8uAg3"}
]
keys = [account["private_key"] for account in self.accounts]
self.hived_keys.extend(keys)
def on_before_hived_run(self):
pass
def on_before_hivemind_sync_run(self):
# create accounts
create_accounts(self.hived_node_client, self.creator, self.accounts)
# tranfer to vesting
transfer_to_vesting(self.hived_node_client, self.creator, self.accounts, "300.000",
"TESTS"
)
# transfer assets to accounts
transfer_assets_to_accounts(self.hived_node_client, self.creator, self.accounts,
"400.000", "TESTS"
)
transfer_assets_to_accounts(self.hived_node_client, self.creator, self.accounts,
"400.000", "TBD"
)
logger.info("Balances for accounts after initial transfer")
print_balance(self.hived_node_client, self.accounts)
create_posts(self.hived_node_client, self.accounts)
def on_before_hivemind_server_run(self):
pass
def on_after_hivemind_server_run(self):
# create community
community_name = "hive-12345"
communities = [
{"name" : community_name, "private_key" : "5Hxtady4d9AcJoQNt8FsMcGiq7rKSapMx7WZyG3UWB7YPfsHRRX", "public_key" : "TST6MsTXRVf3bFS5KBUHxrMRLSz7ystTUrGADFw98jEBWcZ3btXqb"},
]
create_accounts(self.hived_node_client, "tester001", communities)
# validate if community exists
query = {
"jsonrpc": "2.0",
"id": 1,
"method": "bridge.list_communities",
"params": {
"sort" : "new"
}
}
ret = hive_utils.common.send_rpc_query(self.hivemind_server.get_address(), query)
assert 'error' not in ret, "Query returned error response"
assert 'result' in ret, "Expected result key in response"
result = ret['result']
assert len(result) == 1, "Expecting one item"
assert result['name'] == community_name, "Expected name `{}` got `{}`".format(community_name, result['name'])
if __name__ == '__main__':
logger.info("Performing tests...")
import argparse
parser = argparse.ArgumentParser(description="Usage: python3 exaple.py path/to/hived/executable")
parser.add_argument("hived_path", help = "Path to hived executable.")
parser.add_argument("database_url", help = "Path to database")
parser.add_argument("--creator", dest="creator", default="initminer", help = "Account to create test accounts with")
parser.add_argument("--wif", dest="wif", default="5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n", help="Private key for creator account")
parser.add_argument("--node-url", dest="node_url", default="http://127.0.0.1:8090", help="Url of working hive node")
parser.add_argument("--working-dir", dest="hived_working_dir", default="/tmp/hived-data/", help = "Path to hived working directory")
parser.add_argument("--config-path", dest="hived_config_path", default="../../hive_utils/resources/config.ini.in",help = "Path to source config.ini file")
args = parser.parse_args()
test_runner = HivemindFunctionalRunner(args.creator, args.hived_path, args.wif, args.node_url, args.database_url, args.hived_working_dir, args.hived_config_path)
test_runner.run()
#!/usr/bin/python3
import sys
sys.path.append("../../")
import hive_utils
from uuid import uuid4
from time import sleep
import logging
import os
LOG_LEVEL = logging.INFO
LOG_FORMAT = "%(asctime)-15s - %(name)s - %(levelname)s - %(message)s"
MAIN_LOG_PATH = "functional_example.log"
log_dir = os.environ.get("TEST_LOG_DIR", None)
if log_dir is not None:
MAIN_LOG_PATH = log_dir + "/" + MAIN_LOG_PATH
else:
MAIN_LOG_PATH = "./" + MAIN_LOG_PATH
MODULE_NAME = "Functional-Example"
logger = logging.getLogger(MODULE_NAME)
logger.setLevel(LOG_LEVEL)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(LOG_LEVEL)
ch.setFormatter(logging.Formatter(LOG_FORMAT))
fh = logging.FileHandler(MAIN_LOG_PATH)
fh.setLevel(LOG_LEVEL)
fh.setFormatter(logging.Formatter(LOG_FORMAT))
if not logger.hasHandlers():
logger.addHandler(ch)
logger.addHandler(fh)
try:
from beem import Hive
except Exception as ex:
logger.error("beem library is not installed.")
sys.exit(1)
def create_accounts(node, creator, accounts):
""" Create accounts given as a list using `creator` as a creator account """
for account in accounts:
logger.info("Creating account: {}".format(account['name']))
node.create_account(account['name'],
owner_key=account['public_key'],
active_key=account['public_key'],
posting_key=account['public_key'],
memo_key=account['public_key'],
store_keys = False,
creator=creator,
asset='TESTS'
)
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def transfer_to_vesting(node, from_account, accounts, amount, asset):
""" Transfer assets to vesting from `from_account` to accounts given in list """
from beem.account import Account
for acnt in accounts:
logger.info("Transfer to vesting from {} to {} amount {} {}".format(
from_account, acnt['name'], amount, asset)
)
acc = Account(from_account, hive_instance=node)
acc.transfer_to_vesting(amount, to = acnt['name'], asset = asset)
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def transfer_assets_to_accounts(node, from_account, accounts, amount, asset, wif=None):
""" Transfer assets `from_account` to accounts given in list """
from beem.account import Account
for acnt in accounts:
logger.info("Transfer from {} to {} amount {} {}".format(from_account,
acnt['name'], amount, asset)
)
acc = Account(from_account, hive_instance=node)
acc.transfer(acnt['name'], amount, asset, memo = "initial transfer")
if wif is not None:
hive_utils.debug_generate_blocks(node.rpc.url, wif, 5)
else:
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def get_permlink(account):
""" Helper for permlink creation """
return "functional-example-title-{}".format(account)
def create_posts(node, accounts, wif=None):
""" Create example posts - one post for one account. Accounts given in list format """
logger.info("Creating posts...")
for acnt in accounts:
logger.info("New post ==> ({},{},{},{},{})".format(
"Hivepy example post title [{}]".format(acnt['name']),
"Hivepy example post body [{}]".format(acnt['name']),
acnt['name'],
get_permlink(acnt['name']),
"example"
))
node.post("Hivepy example post title [{}]".format(acnt['name']),
"Hivepy example post body [{}]".format(acnt['name']),
acnt['name'],
permlink = get_permlink(acnt['name']),
tags = "example")
if wif is not None:
hive_utils.debug_generate_blocks(node.rpc.url, wif, 5)
else:
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def print_balance(node, accounts):
""" Print balance for accounts given as a list """
from beem.account import Account
balances = []
balances_str = []
for acnt in accounts:
ret = Account(acnt['name'], hive_instance=node).json()
hbd = ret.get('hbd_balance', None)
if hbd is not None:
hbd = hbd.get('amount')
balances_str.append("{}:{}".format(acnt['name'], hbd))
balances.append(hbd)
logger.info("Balances ==> {}".format(",".join(balances_str)))
return balances
class HivemindFunctionalRunner(hive_utils.test_runner.TestRunner):
def __init__(self, creator, hived_path, wif, node_url, database_url, working_dir, config_path):
super().__init__(hived_path, wif, node_url, database_url, working_dir, config_path)
self.creator = creator
self.accounts = [
# place accounts here in the format: {'name' : name, 'private_key' : private-key, 'public_key' : public-key}
{"name" : "tester001", "private_key" : "5KQeu7SdzxT1DiUzv7jaqwkwv1V8Fi7N8NBZtHugWYXqVFH1AFa", "public_key" : "TST8VfiahQsfS1TLcnBfp4NNfdw67uWweYbbUXymbNiDXVDrzUs7J"},
{"name" : "tester002", "private_key" : "5KgfcV9bgEen3v9mxkoGw6Rhuf2giDRZTHZjzwisjkrpF4FUh3N", "public_key" : "TST5gQPYm5bs9dRPHpqBy6dU32M8FcoKYFdF4YWEChUarc9FdYHzn"},
{"name" : "tester003", "private_key" : "5Jz3fcrrgKMbL8ncpzTdQmdRVHdxMhi8qScoxSR3TnAFUcdyD5N", "public_key" : "TST57wy5bXyJ4Z337Bo6RbinR6NyTRJxzond5dmGsP4gZ51yN6Zom"},
{"name" : "tester004", "private_key" : "5KcmobLVMSAVzETrZxfEGG73Zvi5SKTgJuZXtNgU3az2VK3Krye", "public_key" : "TST8dPte853xAuLMDV7PTVmiNMRwP6itMyvSmaht7J5tVczkDLa5K"},
{"name" : "tester005", "private_key" : "5Hy4vEeYmBDvmXipe5JAFPhNwCnx7NfsfyiktBTBURn9Qt1ihcA", "public_key" : "TST7CP7FFjvG55AUeH8riYbfD8NxTTtFH32ekQV4YFXmV6gU8uAg3"}
]
keys = [account["private_key"] for account in self.accounts]
self.hived_keys.extend(keys)
def on_before_hived_run(self):
pass
def on_before_hivemind_sync_run(self):
# create accounts
create_accounts(self.hived_node_client, self.creator, self.accounts)
# tranfer to vesting
transfer_to_vesting(self.hived_node_client, self.creator, self.accounts, "300.000",
"TESTS"
)
# transfer assets to accounts
transfer_assets_to_accounts(self.hived_node_client, self.creator, self.accounts,
"400.000", "TESTS"
)
transfer_assets_to_accounts(self.hived_node_client, self.creator, self.accounts,
"400.000", "TBD"
)
logger.info("Balances for accounts after initial transfer")
print_balance(self.hived_node_client, self.accounts)
create_posts(self.hived_node_client, self.accounts)
def on_before_hivemind_server_run(self):
pass
def on_after_hivemind_server_run(self):
from time import sleep
sleep(60)
if __name__ == '__main__':
logger.info("Performing tests...")
import argparse
parser = argparse.ArgumentParser(description="Usage: python3 exaple.py path/to/hived/executable")
parser.add_argument("hived_path", help = "Path to hived executable.")
parser.add_argument("database_url", help = "Path database.")
parser.add_argument("--creator", dest="creator", default="initminer", help = "Account to create test accounts with")
parser.add_argument("--wif", dest="wif", default="5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n", help="Private key for creator account")
parser.add_argument("--node-url", dest="node_url", default="http://127.0.0.1:8090", help="Url of working hive node")
parser.add_argument("--working-dir", dest="hived_working_dir", default="/tmp/hived-data/", help = "Path to hived working directory")
parser.add_argument("--config-path", dest="hived_config_path", default="../../hive_utils/resources/config.ini.in",help = "Path to source config.ini file")
args = parser.parse_args()
test_runner = HivemindFunctionalRunner(args.creator, args.hived_path, args.wif, args.node_url, args.database_url, args.hived_working_dir, args.hived_config_path)
test_runner.run()
1. `psql -U postgres`
2. `create database hive_test`
3. `python3 follow.py /path/to/testnet/hived/programs/hived/hived postgresql://hive@localhost:5432/hive_test`
4. Logs in /tmp
Run example:
```
$ python3 follow.py /home/dariusz-work/Builds/hive-testnet/programs/hived/hived postgresql://hive@localhost:5432/hive4
2020-10-12 19:36:43,630 - Functional-Follow - INFO - Performing tests...
2020-10-12 19:36:43,632 - test_runner.TestRunner - INFO - Executing before hived run hook
2020-10-12 19:36:43,632 - test_runner.TestRunner - INFO - Running hived via /home/dariusz-work/Builds/hive-testnet/programs/hived/hived in /tmp/hived-data/ with config ../../hive_utils/resources/config.ini.in
2020-10-12 19:36:43,632 - hive_node.HiveNodeInScreen - INFO - New hive node
2020-10-12 19:36:43,645 - hive_node.HiveNodeInScreen - INFO - *** START NODE at http://127.0.0.1:8090 in /tmp/hived-data/
2020-10-12 19:36:43,645 - hive_node.HiveNodeInScreen - INFO - Running hived with command: screen -m -d -L -c /tmp/hived-data//hive_screen-8090.cfg -S hived-8090-2020-10-12 /home/dariusz-work/Builds/hive-testnet/programs/hived/hived -d /tmp/hived-data/ --advanced-benchmark --sps-remove-threshold -1 --enable-stale-production
2020-10-12 19:37:03,706 - hive_node.HiveNodeInScreen - INFO - Node at http://127.0.0.1:8090 in /tmp/hived-data/ is up and running...
2020-10-12 19:37:04,320 - test_runner.TestRunner - INFO - Chain prefix is: TST
2020-10-12 19:37:04,320 - test_runner.TestRunner - INFO - Chain ID is: 18dcf0a285365fc58b71f18b3d3fec954aa0c141c44e4e5cb4cf777b9eab274e
2020-10-12 19:37:04,320 - test_runner.TestRunner - INFO - Executing before hivemind initial sync run hook
2020-10-12 19:37:04,320 - Functional-Follow - INFO - Creating account: tester001
2020-10-12 19:37:04,482 - Functional-Follow - INFO - Creating account: tester002
2020-10-12 19:37:04,613 - Functional-Follow - INFO - Creating account: tester003
2020-10-12 19:37:04,684 - Functional-Follow - INFO - Creating account: tester004
2020-10-12 19:37:04,755 - Functional-Follow - INFO - Creating account: tester005
2020-10-12 19:37:18,925 - Functional-Follow - INFO - Transfer to vesting from initminer to tester001 amount 300.000 TESTS
2020-10-12 19:37:19,063 - Functional-Follow - INFO - Transfer to vesting from initminer to tester002 amount 300.000 TESTS
2020-10-12 19:37:19,144 - Functional-Follow - INFO - Transfer to vesting from initminer to tester003 amount 300.000 TESTS
2020-10-12 19:37:19,224 - Functional-Follow - INFO - Transfer to vesting from initminer to tester004 amount 300.000 TESTS
2020-10-12 19:37:19,356 - Functional-Follow - INFO - Transfer to vesting from initminer to tester005 amount 300.000 TESTS
2020-10-12 19:37:34,528 - Functional-Follow - INFO - Transfer from initminer to tester001 amount 400.000 TESTS
2020-10-12 19:37:34,598 - Functional-Follow - INFO - Transfer from initminer to tester002 amount 400.000 TESTS
2020-10-12 19:37:34,669 - Functional-Follow - INFO - Transfer from initminer to tester003 amount 400.000 TESTS
2020-10-12 19:37:34,750 - Functional-Follow - INFO - Transfer from initminer to tester004 amount 400.000 TESTS
2020-10-12 19:37:34,882 - Functional-Follow - INFO - Transfer from initminer to tester005 amount 400.000 TESTS
2020-10-12 19:37:48,991 - Functional-Follow - INFO - Transfer from initminer to tester001 amount 400.000 TBD
2020-10-12 19:37:49,123 - Functional-Follow - INFO - Transfer from initminer to tester002 amount 400.000 TBD
2020-10-12 19:37:49,255 - Functional-Follow - INFO - Transfer from initminer to tester003 amount 400.000 TBD
2020-10-12 19:37:49,336 - Functional-Follow - INFO - Transfer from initminer to tester004 amount 400.000 TBD
2020-10-12 19:37:49,417 - Functional-Follow - INFO - Transfer from initminer to tester005 amount 400.000 TBD
2020-10-12 19:38:04,539 - Functional-Follow - INFO - Balances for accounts after initial transfer
2020-10-12 19:38:04,549 - Functional-Follow - INFO - Balances ==> tester001:400000,tester002:400000,tester003:400000,tester004:400000,tester005:400000
2020-10-12 19:38:04,549 - Functional-Follow - INFO - Creating posts...
2020-10-12 19:38:04,549 - Functional-Follow - INFO - New post ==> (Hivepy example post title [tester001],Hivepy example post body [tester001],tester001,functional-example-title-tester001,example)
2020-10-12 19:38:04,627 - Functional-Follow - INFO - New post ==> (Hivepy example post title [tester002],Hivepy example post body [tester002],tester002,functional-example-title-tester002,example)
2020-10-12 19:38:04,759 - Functional-Follow - INFO - New post ==> (Hivepy example post title [tester003],Hivepy example post body [tester003],tester003,functional-example-title-tester003,example)
2020-10-12 19:38:04,840 - Functional-Follow - INFO - New post ==> (Hivepy example post title [tester004],Hivepy example post body [tester004],tester004,functional-example-title-tester004,example)
2020-10-12 19:38:04,971 - Functional-Follow - INFO - New post ==> (Hivepy example post title [tester005],Hivepy example post body [tester005],tester005,functional-example-title-tester005,example)
2020-10-12 19:38:49,178 - test_runner.TestRunner - INFO - Start hivemind instance and perform initial sync
2020-10-12 19:38:49,178 - hivemind.HivemindInScreen - INFO - New hivemind instance
2020-10-12 19:38:49,178 - hivemind.HivemindInScreen - INFO - Running hivemind with command: screen -m -d -L -c /tmp/hive_screen-8080.cfg -S hive-8080-2020-10-12 hive sync --steemd-url {"default": "http://127.0.0.1:8090"} --database-url postgresql://hive@localhost:5432/hive4 --http-server-port 8080
2020-10-12 19:38:54,196 - hivemind.HivemindInScreen - INFO - Hivemind at http://0.0.0.0:8080 in /tmp is up and running...
2020-10-12 19:38:54,197 - common - INFO - Waiting for string "Initial sync complete" in file /tmp/hive-8080-2020-10-12.log
2020-10-12 19:38:55,198 - test_runner.TestRunner - INFO - Initial sync complete, switching to live sync mode
2020-10-12 19:38:55,198 - test_runner.TestRunner - INFO - Executing before hivemind server run hook
2020-10-12 19:38:55,198 - test_runner.TestRunner - INFO - Start hivemind instance as server
2020-10-12 19:38:55,198 - hivemind.HivemindInScreen - INFO - New hivemind instance
2020-10-12 19:38:55,198 - hivemind.HivemindInScreen - INFO - Running hivemind with command: screen -m -d -L -c /tmp/hive_screen-8081.cfg -S hive-8081-2020-10-12 hive server --steemd-url {"default": "http://127.0.0.1:8090"} --database-url postgresql://hive@localhost:5432/hive4 --http-server-port 8081
2020-10-12 19:39:00,212 - hivemind.HivemindInScreen - INFO - Hivemind at http://0.0.0.0:8081 in /tmp is up and running...
2020-10-12 19:39:00,212 - test_runner.TestRunner - INFO - Executing after hivemind server run hook
2020-10-12 19:39:30,360 - test_runner.TestRunner - INFO - Stopping hived and all hivemind instances
2020-10-12 19:39:30,360 - hivemind.HivemindInScreen - INFO - Stopping hivemind at http://0.0.0.0:8081
2020-10-12 19:39:30,360 - common - INFO - Terminating hive process running on port 8081
2020-10-12 19:39:30,381 - common - INFO - Done...
2020-10-12 19:39:30,381 - hivemind.HivemindInScreen - INFO - Stopping hivemind at http://0.0.0.0:8080
2020-10-12 19:39:30,381 - common - INFO - Terminating hive process running on port 8080
2020-10-12 19:39:30,403 - common - INFO - Done...
2020-10-12 19:39:30,403 - hive_node.HiveNodeInScreen - INFO - Stopping node at http://127.0.0.1:8090
2020-10-12 19:39:30,403 - common - INFO - Terminating hived process running on port 8090
2020-10-12 19:39:30,427 - common - INFO - Done...
```
\ No newline at end of file
#!/usr/bin/python3
import sys
sys.path.append("../../")
import hive_utils
from time import sleep
import logging
import os
LOG_LEVEL = logging.INFO
LOG_FORMAT = "%(asctime)-15s - %(name)s - %(levelname)s - %(message)s"
MAIN_LOG_PATH = "functional_follow.log"
log_dir = os.environ.get("TEST_LOG_DIR", None)
if log_dir is not None:
MAIN_LOG_PATH = log_dir + "/" + MAIN_LOG_PATH
else:
MAIN_LOG_PATH = "./" + MAIN_LOG_PATH
MODULE_NAME = "Functional-Follow"
logger = logging.getLogger(MODULE_NAME)
logger.setLevel(LOG_LEVEL)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(LOG_LEVEL)
ch.setFormatter(logging.Formatter(LOG_FORMAT))
fh = logging.FileHandler(MAIN_LOG_PATH)
fh.setLevel(LOG_LEVEL)
fh.setFormatter(logging.Formatter(LOG_FORMAT))
if not logger.hasHandlers():
logger.addHandler(ch)
logger.addHandler(fh)
try:
from beem import Hive
except Exception as ex:
logger.error("beem library is not installed.")
sys.exit(1)
def create_accounts(node, creator, accounts):
""" Create accounts given as a list using `creator` as a creator account """
for account in accounts:
logger.info("Creating account: {}".format(account['name']))
node.create_account(account['name'],
owner_key=account['public_key'],
active_key=account['public_key'],
posting_key=account['public_key'],
memo_key=account['public_key'],
store_keys = False,
creator=creator,
asset='TESTS'
)
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def transfer_to_vesting(node, from_account, accounts, amount, asset):
""" Transfer assets to vesting from `from_account` to accounts given in list """
from beem.account import Account
for acnt in accounts:
logger.info("Transfer to vesting from {} to {} amount {} {}".format(
from_account, acnt['name'], amount, asset)
)
acc = Account(from_account, hive_instance=node)
acc.transfer_to_vesting(amount, to = acnt['name'], asset = asset)
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def transfer_assets_to_accounts(node, from_account, accounts, amount, asset, wif=None):
""" Transfer assets `from_account` to accounts given in list """
from beem.account import Account
for acnt in accounts:
logger.info("Transfer from {} to {} amount {} {}".format(from_account,
acnt['name'], amount, asset)
)
acc = Account(from_account, hive_instance=node)
acc.transfer(acnt['name'], amount, asset, memo = "initial transfer")
if wif is not None:
hive_utils.debug_generate_blocks(node.rpc.url, wif, 5)
else:
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def get_permlink(account):
""" Helper for permlink creation """
return "functional-example-title-{}".format(account)
def create_posts(node, accounts, wif=None):
""" Create example posts - one post for one account. Accounts given in list format """
logger.info("Creating posts...")
for acnt in accounts:
logger.info("New post ==> ({},{},{},{},{})".format(
"Hivepy example post title [{}]".format(acnt['name']),
"Hivepy example post body [{}]".format(acnt['name']),
acnt['name'],
get_permlink(acnt['name']),
"example"
))
node.post("Hivepy example post title [{}]".format(acnt['name']),
"Hivepy example post body [{}]".format(acnt['name']),
acnt['name'],
permlink = get_permlink(acnt['name']),
tags = "example")
if wif is not None:
hive_utils.debug_generate_blocks(node.rpc.url, wif, 5)
else:
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def print_balance(node, accounts):
""" Print balance for accounts given as a list """
from beem.account import Account
balances = []
balances_str = []
for acnt in accounts:
ret = Account(acnt['name'], hive_instance=node).json()
hbd = ret.get('hbd_balance', None)
if hbd is not None:
hbd = hbd.get('amount')
balances_str.append("{}:{}".format(acnt['name'], hbd))
balances.append(hbd)
logger.info("Balances ==> {}".format(",".join(balances_str)))
return balances
class HivemindFunctionalRunner(hive_utils.test_runner.TestRunner):
def __init__(self, creator, hived_path, wif, node_url, database_url, working_dir, config_path):
super().__init__(hived_path, wif, node_url, database_url, working_dir, config_path)
self.creator = creator
self.accounts = [
# place accounts here in the format: {'name' : name, 'private_key' : private-key, 'public_key' : public-key}
{"name" : "tester001", "private_key" : "5KQeu7SdzxT1DiUzv7jaqwkwv1V8Fi7N8NBZtHugWYXqVFH1AFa", "public_key" : "TST8VfiahQsfS1TLcnBfp4NNfdw67uWweYbbUXymbNiDXVDrzUs7J"},
{"name" : "tester002", "private_key" : "5KgfcV9bgEen3v9mxkoGw6Rhuf2giDRZTHZjzwisjkrpF4FUh3N", "public_key" : "TST5gQPYm5bs9dRPHpqBy6dU32M8FcoKYFdF4YWEChUarc9FdYHzn"},
{"name" : "tester003", "private_key" : "5Jz3fcrrgKMbL8ncpzTdQmdRVHdxMhi8qScoxSR3TnAFUcdyD5N", "public_key" : "TST57wy5bXyJ4Z337Bo6RbinR6NyTRJxzond5dmGsP4gZ51yN6Zom"},
{"name" : "tester004", "private_key" : "5KcmobLVMSAVzETrZxfEGG73Zvi5SKTgJuZXtNgU3az2VK3Krye", "public_key" : "TST8dPte853xAuLMDV7PTVmiNMRwP6itMyvSmaht7J5tVczkDLa5K"},
{"name" : "tester005", "private_key" : "5Hy4vEeYmBDvmXipe5JAFPhNwCnx7NfsfyiktBTBURn9Qt1ihcA", "public_key" : "TST7CP7FFjvG55AUeH8riYbfD8NxTTtFH32ekQV4YFXmV6gU8uAg3"}
]
keys = [account["private_key"] for account in self.accounts]
self.hived_keys.extend(keys)
def on_before_hived_run(self):
pass
def on_before_hivemind_sync_run(self):
# create accounts
create_accounts(self.hived_node_client, self.creator, self.accounts)
# tranfer to vesting
transfer_to_vesting(self.hived_node_client, self.creator, self.accounts, "300.000",
"TESTS"
)
# transfer assets to accounts
transfer_assets_to_accounts(self.hived_node_client, self.creator, self.accounts,
"400.000", "TESTS"
)
transfer_assets_to_accounts(self.hived_node_client, self.creator, self.accounts,
"400.000", "TBD"
)
logger.info("Balances for accounts after initial transfer")
print_balance(self.hived_node_client, self.accounts)
create_posts(self.hived_node_client, self.accounts)
json_data = ["follow", {"follower":"tester001","following":["tester002","tester003"],"what":["blog"]}]
self.hived_node_client.custom_json("follow",json_data, required_posting_auths=["tester001"])
sleep(30)
def on_before_hivemind_server_run(self):
pass
def on_after_hivemind_server_run(self):
json_data = ["follow", {"follower":"tester001","following":["tester004","tester005"],"what":["blog"]}]
self.hived_node_client.custom_json("follow", json_data, required_posting_auths=["tester001"])
sleep(30)
if __name__ == '__main__':
logger.info("Performing tests...")
import argparse
parser = argparse.ArgumentParser(description="Usage: python3 exaple.py path/to/hived/executable")
parser.add_argument("hived_path", help = "Path to hived executable.")
parser.add_argument("database_url", help = "Path to database.")
parser.add_argument("--creator", dest="creator", default="initminer", help = "Account to create test accounts with")
parser.add_argument("--wif", dest="wif", default="5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n", help="Private key for creator account")
parser.add_argument("--node-url", dest="node_url", default="http://127.0.0.1:8090", help="Url of working hive node")
parser.add_argument("--working-dir", dest="hived_working_dir", default="/tmp/hived-data/", help = "Path to hived working directory")
parser.add_argument("--config-path", dest="hived_config_path", default="../../hive_utils/resources/config.ini.in",help = "Path to source config.ini file")
args = parser.parse_args()
test_runner = HivemindFunctionalRunner(args.creator, args.hived_path, args.wif, args.node_url, args.database_url, args.hived_working_dir, args.hived_config_path)
test_runner.run()
#!/usr/bin/python3
import sys
sys.path.append("../../")
import hive_utils
from uuid import uuid4
from time import sleep
import logging
import os
LOG_LEVEL = logging.INFO
LOG_FORMAT = "%(asctime)-15s - %(name)s - %(levelname)s - %(message)s"
MAIN_LOG_PATH = "functional_example.log"
log_dir = os.environ.get("TEST_LOG_DIR", None)
if log_dir is not None:
MAIN_LOG_PATH = log_dir + "/" + MAIN_LOG_PATH
else:
MAIN_LOG_PATH = "./" + MAIN_LOG_PATH
MODULE_NAME = "Functional-Example"
logger = logging.getLogger(MODULE_NAME)
logger.setLevel(LOG_LEVEL)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(LOG_LEVEL)
ch.setFormatter(logging.Formatter(LOG_FORMAT))
fh = logging.FileHandler(MAIN_LOG_PATH)
fh.setLevel(LOG_LEVEL)
fh.setFormatter(logging.Formatter(LOG_FORMAT))
if not logger.hasHandlers():
logger.addHandler(ch)
logger.addHandler(fh)
try:
from beem import Hive
except Exception as ex:
logger.error("beem library is not installed.")
sys.exit(1)
def create_accounts(node, creator, accounts):
""" Create accounts given as a list using `creator` as a creator account """
for account in accounts:
logger.info("Creating account: {}".format(account['name']))
node.create_account(account['name'],
owner_key=account['public_key'],
active_key=account['public_key'],
posting_key=account['public_key'],
memo_key=account['public_key'],
store_keys = False,
creator=creator,
asset='TESTS'
)
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def transfer_to_vesting(node, from_account, accounts, amount, asset):
""" Transfer assets to vesting from `from_account` to accounts given in list """
from beem.account import Account
for acnt in accounts:
logger.info("Transfer to vesting from {} to {} amount {} {}".format(
from_account, acnt['name'], amount, asset)
)
acc = Account(from_account, hive_instance=node)
acc.transfer_to_vesting(amount, to = acnt['name'], asset = asset)
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def transfer_assets_to_accounts(node, from_account, accounts, amount, asset, wif=None):
""" Transfer assets `from_account` to accounts given in list """
from beem.account import Account
for acnt in accounts:
logger.info("Transfer from {} to {} amount {} {}".format(from_account,
acnt['name'], amount, asset)
)
acc = Account(from_account, hive_instance=node)
acc.transfer(acnt['name'], amount, asset, memo = "initial transfer")
if wif is not None:
hive_utils.debug_generate_blocks(node.rpc.url, wif, 5)
else:
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def get_permlink(account):
""" Helper for permlink creation """
return "functional-example-title-{}".format(account)
def create_posts(node, accounts, wif=None):
""" Create example posts - one post for one account. Accounts given in list format """
logger.info("Creating posts...")
for acnt in accounts:
logger.info("New post ==> ({},{},{},{},{})".format(
"Hivepy example post title [{}]".format(acnt['name']),
"Hivepy example post body [{}]".format(acnt['name']),
acnt['name'],
get_permlink(acnt['name']),
"example"
))
node.post("Hivepy example post title [{}]".format(acnt['name']),
"Hivepy example post body [{}]".format(acnt['name']),
acnt['name'],
permlink = get_permlink(acnt['name']),
tags = "example")
if wif is not None:
hive_utils.debug_generate_blocks(node.rpc.url, wif, 5)
else:
hive_utils.common.wait_n_blocks(node.rpc.url, 5)
def print_balance(node, accounts):
""" Print balance for accounts given as a list """
from beem.account import Account
balances = []
balances_str = []
for acnt in accounts:
ret = Account(acnt['name'], hive_instance=node).json()
hbd = ret.get('hbd_balance', None)
if hbd is not None:
hbd = hbd.get('amount')
balances_str.append("{}:{}".format(acnt['name'], hbd))
balances.append(hbd)
logger.info("Balances ==> {}".format(",".join(balances_str)))
return balances
class HivemindFunctionalRunner(hive_utils.test_runner.TestRunner):
def __init__(self, creator, hived_path, wif, node_url, database_url, working_dir, config_path):
super().__init__(hived_path, wif, node_url, database_url, working_dir, config_path)
self.creator = creator
self.accounts = [
# place accounts here in the format: {'name' : name, 'private_key' : private-key, 'public_key' : public-key}
{"name" : "tester001", "private_key" : "5KQeu7SdzxT1DiUzv7jaqwkwv1V8Fi7N8NBZtHugWYXqVFH1AFa", "public_key" : "TST8VfiahQsfS1TLcnBfp4NNfdw67uWweYbbUXymbNiDXVDrzUs7J"},
{"name" : "tester002", "private_key" : "5KgfcV9bgEen3v9mxkoGw6Rhuf2giDRZTHZjzwisjkrpF4FUh3N", "public_key" : "TST5gQPYm5bs9dRPHpqBy6dU32M8FcoKYFdF4YWEChUarc9FdYHzn"},
{"name" : "tester003", "private_key" : "5Jz3fcrrgKMbL8ncpzTdQmdRVHdxMhi8qScoxSR3TnAFUcdyD5N", "public_key" : "TST57wy5bXyJ4Z337Bo6RbinR6NyTRJxzond5dmGsP4gZ51yN6Zom"},
{"name" : "tester004", "private_key" : "5KcmobLVMSAVzETrZxfEGG73Zvi5SKTgJuZXtNgU3az2VK3Krye", "public_key" : "TST8dPte853xAuLMDV7PTVmiNMRwP6itMyvSmaht7J5tVczkDLa5K"},
{"name" : "tester005", "private_key" : "5Hy4vEeYmBDvmXipe5JAFPhNwCnx7NfsfyiktBTBURn9Qt1ihcA", "public_key" : "TST7CP7FFjvG55AUeH8riYbfD8NxTTtFH32ekQV4YFXmV6gU8uAg3"}
]
keys = [account["private_key"] for account in self.accounts]
self.hived_keys.extend(keys)
def on_before_hived_run(self):
pass
def on_before_hivemind_sync_run(self):
# create accounts
create_accounts(self.hived_node_client, self.creator, self.accounts)
# tranfer to vesting
transfer_to_vesting(self.hived_node_client, self.creator, self.accounts, "300.000",
"TESTS"
)
# transfer assets to accounts
transfer_assets_to_accounts(self.hived_node_client, self.creator, self.accounts,
"400.000", "TESTS"
)
transfer_assets_to_accounts(self.hived_node_client, self.creator, self.accounts,
"400.000", "TBD"
)
logger.info("Balances for accounts after initial transfer")
print_balance(self.hived_node_client, self.accounts)
create_posts(self.hived_node_client, self.accounts)
def on_before_hivemind_server_run(self):
pass
def on_after_hivemind_server_run(self):
from time import sleep
counter = 0
while counter < 20:
#ask for headblocks
ret = self.hived_node_client.rpc.get_dynamic_global_properties()
logger.info("Head block from Hived: {}".format(ret['head_block_number']))
query = {
"jsonrpc":"2.0",
"method":"hive.db_head_state",
"id":1
}
ret = hive_utils.common.send_rpc_query(self.hivemind_server.get_address(), query)
logger.info("Head block from hivemind: {}".format(ret['result']['db_head_block']))
sleep(3)
counter += 1
if __name__ == '__main__':
logger.info("Performing tests...")
import argparse
parser = argparse.ArgumentParser(description="Usage: python3 exaple.py path/to/hived/executable")
parser.add_argument("hived_path", help = "Path to hived executable.")
parser.add_argument("database_url", help = "Path to database.")
parser.add_argument("--creator", dest="creator", default="initminer", help = "Account to create test accounts with")
parser.add_argument("--wif", dest="wif", default="5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n", help="Private key for creator account")
parser.add_argument("--node-url", dest="node_url", default="http://127.0.0.1:8090", help="Url of working hive node")
parser.add_argument("--working-dir", dest="hived_working_dir", default="/tmp/hived-data/", help = "Path to hived working directory")
parser.add_argument("--config-path", dest="hived_config_path", default="../../hive_utils/resources/config.ini.in",help = "Path to source config.ini file")
args = parser.parse_args()
test_runner = HivemindFunctionalRunner(args.creator, args.hived_path, args.wif, args.node_url, args.database_url, args.hived_working_dir, args.hived_config_path)
test_runner.run()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment