from enum import Enum
import functools
import subprocess
import os
class Network(Enum):
BCH = 0
NEX = 1
class Node(Enum):
NEXA = 1
BCHN = 3
class NodeFeature(Enum):
SPAWN_ROSTRUM = (0,)
TOKENS = (1,)
RPC_LOGLINE = (3,)
@functools.cache
def rostrum_network() -> Network:
output: str = ""
try:
args = [rostrum_path(), "--version"]
wrapper = process_wrapper()
if wrapper is not None:
args = [wrapper] + args
subprocess.run(
args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
)
except subprocess.CalledProcessError as e:
if e.returncode != 1:
raise
output = e.stderr.decode("utf-8")
if "nexa" in output:
return Network.NEX
if "bitcoin" in output:
return Network.BCH
raise Exception(f"Cannot tell network from following version info: '{output}'")
def node_network() -> Network:
if node() == Node.BCHN:
return Network.BCH
if node() == Node.NEXA:
return Network.NEX
raise NotImplementedError()
@functools.cache
def node() -> Node:
output: str = ""
args = [full_node_path(), "--version"]
wrapper = process_wrapper()
if wrapper is not None:
args = [wrapper] + args
result = subprocess.run(
args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
)
output = result.stdout.decode("utf-8")
if "Nexa" in output:
return Node.NEXA
if "Bitcoin Cash Node Daemon" in output:
return Node.BCHN
raise Exception(f"Cannot tell network from following version info: '{output}'")
@functools.cache
def network() -> Network:
if rostrum_network() != node_network():
raise Exception(
f"Rostrum is built for {rostrum_network()}, while node software is built for {node_network()}"
)
return rostrum_network()
def on_bch() -> bool:
return network() == Network.BCH
def on_nex() -> bool:
return network() == Network.NEX
def full_node_path() -> str:
if not "NODE_PATH" in os.environ:
raise Exception(
"Environment variable NODE_PATH is not set. Set NODE_PATH to the full path of the node software executable."
)
path = os.environ["NODE_PATH"]
if not os.path.exists(path):
raise Exception(
f"Path '{path}' in environment variable NODE_PATH does not exist"
)
return path
def rostrum_path() -> str:
if "ROSTRUM_PATH" in os.environ:
path = os.environ["ROSTRUM_PATH"]
if not os.path.exists(path):
raise Exception(
f"Path '{path}' set in environment variable ROSTRUM_PATH does not exist"
)
return path
path = os.path.join(os.path.dirname(full_node_path()), "rostrum")
if not os.path.exists(path):
raise Exception(
f"Path '{path}' does not exist. Set path with environment variable ROSTRUM_PATH"
)
return path
def process_wrapper() -> str | None:
if not "PROCESS_WRAPPER" in os.environ:
return None
path = os.environ["PROCESS_WRAPPER"]
if not os.path.exists(path):
raise Exception(
f"Path '{path}' set in environment variable PROCESS_WRAPPER does not exist"
)
return path
def node_supports(n: Node, feature: NodeFeature) -> bool:
if feature == NodeFeature.SPAWN_ROSTRUM and "NO_SPAWN_ROSTRUM" in os.environ:
return False
support_map = {
Node.NEXA: [
NodeFeature.SPAWN_ROSTRUM,
NodeFeature.TOKENS,
NodeFeature.RPC_LOGLINE,
],
Node.BCHN: [NodeFeature.TOKENS],
}
if not n in support_map:
raise NotImplementedError()
return feature in support_map[n]
def testing_websocket():
return "ROSTRUM_TEST_WEBSOCKET" in os.environ
def testing_http():
return "ROSTRUM_TEST_HTTP" in os.environ
def announce_enabled():
return "ROSTRUM_ANNOUNCE" in os.environ
def testing_tcp_ssl():
return "ROSTRUM_TEST_TCP_SSL" in os.environ
def testing_websocket_ssl():
return "ROSTRUM_TEST_WEBSOCKET_SSL" in os.environ
def testing_https():
return "ROSTRUM_TEST_HTTPS" in os.environ