diff --git a/docs/conf.py b/docs/conf.py index 63763f7..5a9b910 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -15,7 +15,7 @@ import os import sys sys.path.insert(0, os.path.abspath('..')) -from server.version import VERSION +from server.controller import Controller # -- Project information ----------------------------------------------------- @@ -24,9 +24,9 @@ copyright = '2016-2018, Neil Booth' author = 'Neil Booth' # The short X.Y version -version = VERSION.split()[-1] -# The full version, including alpha/beta/rc tags -release = VERSION +version = Controller.short_version() +# The full version including branding +release = Controller.VERSION # -- General configuration --------------------------------------------------- diff --git a/lib/server_base.py b/lib/server_base.py index f6e1de2..9d2e000 100644 --- a/lib/server_base.py +++ b/lib/server_base.py @@ -53,7 +53,6 @@ class ServerBase(object): # First asyncio operation must be to set the event loop policy # as this replaces the event loop - self.logger.info('event loop policy: {}'.format(self.env.loop_policy)) asyncio.set_event_loop_policy(self.env.loop_policy) # Trigger this event to cleanly shutdown diff --git a/server/block_processor.py b/server/block_processor.py index cc31b64..4b3431f 100644 --- a/server/block_processor.py +++ b/server/block_processor.py @@ -18,7 +18,6 @@ from collections import defaultdict from functools import partial from server.daemon import DaemonError -from server.version import VERSION from lib.hash import hash_to_str from lib.util import chunks, formatted_time import server.db @@ -221,8 +220,8 @@ class BlockProcessor(server.db.DB): self.first_sync = False await self.controller.run_in_executor(self.flush, True) if self.utxo_db.for_sync: - self.logger.info('{} synced to height {:,d}' - .format(VERSION, self.height)) + self.logger.info(f'{self.controller.VERSION} synced to ' + f'height {height:,d}') self.open_dbs() self.caught_up_event.set() diff --git a/server/controller.py b/server/controller.py index 0c9bfbc..337be4c 100644 --- a/server/controller.py +++ b/server/controller.py @@ -46,10 +46,17 @@ class Controller(ServerBase): ''' CATCHING_UP, LISTENING, PAUSED, SHUTTING_DOWN = range(4) + PROTOCOL_MIN = '0.9' + PROTOCOL_MAX = '1.2' + VERSION = 'ElectrumX 1.4' def __init__(self, env): '''Initialize everything that doesn't require the event loop.''' super().__init__(env) + self.logger.info(f'software version: {self.VERSION}') + self.logger.info(f'supported protocol versions: ' + f'{self.PROTOCOL_MIN}-{self.PROTOCOL_MAX}') + self.logger.info(f'event loop policy: {env.loop_policy}') self.coin = env.coin self.servers = {} @@ -84,6 +91,34 @@ class Controller(ServerBase): self.mempool = MemPool(self.bp, self) self.peer_mgr = PeerManager(env, self) + @classmethod + def short_version(cls): + '''Return e.g. "1.2" for ElectrumX 1.2''' + return cls.VERSION.split()[-1] + + def server_features(self): + '''Return the server features dictionary.''' + return { + 'hosts': self.env.hosts_dict(), + 'pruning': None, + 'server_version': self.VERSION, + 'protocol_min': self.PROTOCOL_MIN, + 'protocol_max': self.PROTOCOL_MAX, + 'genesis_hash': self.coin.GENESIS_HASH, + 'hash_function': 'sha256', + } + + def server_version_args(self): + '''The arguments to a server.version RPC call to a peer.''' + return [self.VERSION, [self.PROTOCOL_MIN, self.PROTOCOL_MAX]] + + def protocol_tuple(self, client_protocol_str): + '''Given a client's protocol version string, return the negotiated + protocol version tuple, or None if unsupported. + ''' + return util.protocol_version(client_protocol_str, + self.PROTOCOL_MIN, self.PROTOCOL_MAX) + async def start_servers(self): '''Start the RPC server and schedule the external servers to be started once the block processor has caught up. diff --git a/server/db.py b/server/db.py index 32bcd49..e5091e8 100644 --- a/server/db.py +++ b/server/db.py @@ -20,7 +20,6 @@ from collections import namedtuple import lib.util as util from lib.hash import hash_to_str from server.storage import db_class -from server.version import VERSION, PROTOCOL_MIN, PROTOCOL_MAX UTXO = namedtuple("UTXO", "tx_num tx_pos tx_hash height value") @@ -131,9 +130,6 @@ class DB(object): self.read_history_state() - self.logger.info('software version: {}'.format(VERSION)) - self.logger.info('supported protocol versions: {}-{}' - .format(PROTOCOL_MIN, PROTOCOL_MAX)) self.logger.info('DB version: {:d}'.format(self.db_version)) self.logger.info('coin: {}'.format(self.coin.NAME)) self.logger.info('network: {}'.format(self.coin.NET)) diff --git a/server/env.py b/server/env.py index 1cc7e76..99293f8 100644 --- a/server/env.py +++ b/server/env.py @@ -16,7 +16,6 @@ from ipaddress import ip_address from lib.coins import Coin from lib.env_base import EnvBase import lib.util as lib_util -import server.version as version NetIdentity = namedtuple('NetIdentity', 'host tcp_port ssl_port nick_suffix') @@ -146,21 +145,10 @@ class Env(EnvBase): '_tor', ) - def server_features(self): - '''Return the server features dictionary.''' - hosts = {identity.host: {'tcp_port': identity.tcp_port, - 'ssl_port': identity.ssl_port} - for identity in self.identities} - - return { - 'hosts': hosts, - 'pruning': None, - 'server_version': version.VERSION, - 'protocol_min': version.PROTOCOL_MIN, - 'protocol_max': version.PROTOCOL_MAX, - 'genesis_hash': self.coin.GENESIS_HASH, - 'hash_function': 'sha256', - } + def hosts_dict(self): + return {identity.host: {'tcp_port': identity.tcp_port, + 'ssl_port': identity.ssl_port} + for identity in self.identities} def peer_discovery_enum(self): pd = self.default('PEER_DISCOVERY', 'on').strip().lower() diff --git a/server/peers.py b/server/peers.py index 5af6866..e57ace2 100644 --- a/server/peers.py +++ b/server/peers.py @@ -20,7 +20,6 @@ import aiorpcx from lib.peer import Peer from lib.util import ConnectionLogger -import server.version as version PEER_GOOD, PEER_STALE, PEER_NEVER, PEER_BAD = range(4) @@ -51,9 +50,9 @@ class PeerSession(aiorpcx.ClientSession): self.peer.ip_addr = address[0] # Send server.version first - args = [version.VERSION, [version.PROTOCOL_MIN, version.PROTOCOL_MAX]] - self.send_request('server.version', args, self.on_version, - timeout=self.timeout) + controller = self.peer_mgr.controller + self.send_request('server.version', controller.server_version_args(), + self.on_version, timeout=self.timeout) def _header_notification(self, header): pass @@ -226,7 +225,7 @@ class PeerManager(object): self.loop = controller.loop # Our clearnet and Tor Peers, if any - self.myselves = [Peer(ident.host, env.server_features(), 'env') + self.myselves = [Peer(ident.host, controller.server_features(), 'env') for ident in env.identities] self.retry_event = asyncio.Event() # Peers have one entry per hostname. Once connected, the diff --git a/server/session.py b/server/session.py index d42a582..e515299 100644 --- a/server/session.py +++ b/server/session.py @@ -17,7 +17,6 @@ from aiorpcx import ServerSession, JSONRPCAutoDetect, RPCError from lib.hash import sha256, hash_to_str import lib.util as util from server.daemon import DaemonError -import server.version as version BAD_REQUEST = 1 DAEMON_ERROR = 2 @@ -293,10 +292,6 @@ class ElectrumX(SessionBase): hashX = self.controller.scripthash_to_hashX(scripthash) return await self.hashX_subscribe(hashX, scripthash) - def server_features(self): - '''Returns a dictionary of server features.''' - return self.env.server_features() - def block_headers(self, start_height, count): '''Return count concatenated block headers as hex for the main chain; starting at start_height. @@ -336,11 +331,9 @@ class ElectrumX(SessionBase): minor, revision = divmod(minor, 10000) revision //= 100 daemon_version = '{:d}.{:d}.{:d}'.format(major, minor, revision) - server_version = version.VERSION.split()[-1] for pair in [ - ('$VERSION', version.VERSION), # legacy - ('$SERVER_VERSION', server_version), - ('$SERVER_SUBVERSION', version.VERSION), + ('$SERVER_VERSION', self.controller.short_version()), + ('$SERVER_SUBVERSION', self.controller.VERSION), ('$DAEMON_VERSION', daemon_version), ('$DAEMON_SUBVERSION', network_info['subversion']), ('$DONATION_ADDRESS', self.env.donation_address), @@ -398,8 +391,7 @@ class ElectrumX(SessionBase): # Find the highest common protocol version. Disconnect if # that protocol version in unsupported. - ptuple = util.protocol_version(protocol_version, version.PROTOCOL_MIN, - version.PROTOCOL_MAX) + ptuple = self.controller.protocol_tuple(protocol_version) # From protocol version 1.1, protocol_version cannot be omitted if ptuple is None or (ptuple >= (1, 1) and protocol_version is None): @@ -413,9 +405,9 @@ class ElectrumX(SessionBase): # The return value depends on the protocol version if ptuple < (1, 1): - return version.VERSION + return self.controller.VERSION else: - return (version.VERSION, self.protocol_version) + return (self.controller.VERSION, self.protocol_version) async def transaction_broadcast(self, raw_tx): '''Broadcast a raw transaction to the network. @@ -479,7 +471,7 @@ class ElectrumX(SessionBase): 'server.add_peer': self.add_peer, 'server.banner': self.banner, 'server.donation_address': self.donation_address, - 'server.features': self.server_features, + 'server.features': self.controller.server_features, 'server.peers.subscribe': self.peers_subscribe, 'server.version': self.server_version, } diff --git a/server/version.py b/server/version.py deleted file mode 100644 index 7868d2b..0000000 --- a/server/version.py +++ /dev/null @@ -1,5 +0,0 @@ -# Server name and protocol versions - -VERSION = 'ElectrumX 1.4' -PROTOCOL_MIN = '0.9' -PROTOCOL_MAX = '1.2' diff --git a/setup.py b/setup.py index ed88ac2..0bcf2b8 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,10 @@ import setuptools -from server.version import VERSION +from server.controller import Controller setuptools.setup( name='electrumx', - version=VERSION.split()[-1], + version=Controller.short_version(), scripts=['electrumx_server.py', 'electrumx_rpc.py'], python_requires='>=3.6', # via environment variables, in which case I've tested with 15.0.4