interface: extend client to be able to support a range of protocols
This commit is contained in:
@@ -546,7 +546,7 @@ class ElectrumQmlApplication(QGuiApplication):
|
|||||||
self.context.setContextProperty('QRIP', self.qr_ip_h)
|
self.context.setContextProperty('QRIP', self.qr_ip_h)
|
||||||
self.context.setContextProperty('BUILD', {
|
self.context.setContextProperty('BUILD', {
|
||||||
'electrum_version': version.ELECTRUM_VERSION,
|
'electrum_version': version.ELECTRUM_VERSION,
|
||||||
'protocol_version': version.PROTOCOL_VERSION,
|
'protocol_version': f"[{version.PROTOCOL_VERSION_MIN}, {version.PROTOCOL_VERSION_MAX}]",
|
||||||
'qt_version': QT_VERSION_STR,
|
'qt_version': QT_VERSION_STR,
|
||||||
'pyqt_version': PYQT_VERSION_STR
|
'pyqt_version': PYQT_VERSION_STR
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ import certifi
|
|||||||
from .util import (ignore_exceptions, log_exceptions, bfh, ESocksProxy,
|
from .util import (ignore_exceptions, log_exceptions, bfh, ESocksProxy,
|
||||||
is_integer, is_non_negative_integer, is_hash256_str, is_hex_str,
|
is_integer, is_non_negative_integer, is_hash256_str, is_hex_str,
|
||||||
is_int_or_float, is_non_negative_int_or_float, OldTaskGroup,
|
is_int_or_float, is_non_negative_int_or_float, OldTaskGroup,
|
||||||
send_exception_to_crash_reporter, error_text_str_to_safe_str)
|
send_exception_to_crash_reporter, error_text_str_to_safe_str, versiontuple)
|
||||||
from . import util
|
from . import util
|
||||||
from . import x509
|
from . import x509
|
||||||
from . import pem
|
from . import pem
|
||||||
@@ -139,6 +139,18 @@ def assert_list_or_tuple(val: Any) -> None:
|
|||||||
raise RequestCorrupted(f'{val!r} should be a list or tuple')
|
raise RequestCorrupted(f'{val!r} should be a list or tuple')
|
||||||
|
|
||||||
|
|
||||||
|
def protocol_tuple(s: Any) -> tuple[int, ...]:
|
||||||
|
"""Converts a protocol version number, such as "1.0" to a tuple (1, 0).
|
||||||
|
|
||||||
|
If the version number is bad, (0, ) indicating version 0 is returned.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
assert isinstance(s, str)
|
||||||
|
return versiontuple(s)
|
||||||
|
except Exception:
|
||||||
|
return (0, )
|
||||||
|
|
||||||
|
|
||||||
class ChainResolutionMode(enum.Enum):
|
class ChainResolutionMode(enum.Enum):
|
||||||
CATCHUP = enum.auto()
|
CATCHUP = enum.auto()
|
||||||
BACKWARD = enum.auto()
|
BACKWARD = enum.auto()
|
||||||
@@ -574,6 +586,8 @@ class Interface(Logger):
|
|||||||
|
|
||||||
self.fee_estimates_eta = {} # type: Dict[int, int]
|
self.fee_estimates_eta = {} # type: Dict[int, int]
|
||||||
|
|
||||||
|
self.active_protocol_tuple = (0,) # type: Optional[tuple[int, ...]]
|
||||||
|
|
||||||
# Dump network messages (only for this interface). Set at runtime from the console.
|
# Dump network messages (only for this interface). Set at runtime from the console.
|
||||||
self.debug = False
|
self.debug = False
|
||||||
|
|
||||||
@@ -964,15 +978,19 @@ class Interface(Logger):
|
|||||||
start = time.perf_counter()
|
start = time.perf_counter()
|
||||||
self.session = session # type: NotificationSession
|
self.session = session # type: NotificationSession
|
||||||
self.session.set_default_timeout(self.network.get_network_timeout_seconds(NetworkTimeout.Generic))
|
self.session.set_default_timeout(self.network.get_network_timeout_seconds(NetworkTimeout.Generic))
|
||||||
|
client_prange = [version.PROTOCOL_VERSION_MIN, version.PROTOCOL_VERSION_MAX]
|
||||||
try:
|
try:
|
||||||
ver = await session.send_request('server.version', [self.client_name(), version.PROTOCOL_VERSION])
|
ver = await session.send_request('server.version', [self.client_name(), client_prange])
|
||||||
except aiorpcx.jsonrpc.RPCError as e:
|
except aiorpcx.jsonrpc.RPCError as e:
|
||||||
raise GracefulDisconnect(e) # probably 'unsupported protocol version'
|
raise GracefulDisconnect(e) # probably 'unsupported protocol version'
|
||||||
if exit_early:
|
if exit_early:
|
||||||
return
|
return
|
||||||
if ver[1] != version.PROTOCOL_VERSION:
|
self.active_protocol_tuple = protocol_tuple(ver[1])
|
||||||
|
client_pmin = protocol_tuple(client_prange[0])
|
||||||
|
client_pmax = protocol_tuple(client_prange[1])
|
||||||
|
if not (client_pmin <= self.active_protocol_tuple <= client_pmax):
|
||||||
raise GracefulDisconnect(f'server violated protocol-version-negotiation. '
|
raise GracefulDisconnect(f'server violated protocol-version-negotiation. '
|
||||||
f'we asked for {version.PROTOCOL_VERSION!r}, they sent {ver[1]!r}')
|
f'we asked for {client_prange!r}, they sent {ver[1]!r}')
|
||||||
if not self.network.check_interface_against_healthy_spread_of_connected_servers(self):
|
if not self.network.check_interface_against_healthy_spread_of_connected_servers(self):
|
||||||
raise GracefulDisconnect(f'too many connected servers already '
|
raise GracefulDisconnect(f'too many connected servers already '
|
||||||
f'in bucket {self.bucket_based_on_ipaddress()}')
|
f'in bucket {self.bucket_based_on_ipaddress()}')
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ from .interface import (
|
|||||||
Interface, PREFERRED_NETWORK_PROTOCOL, RequestTimedOut, NetworkTimeout, BUCKET_NAME_OF_ONION_SERVERS,
|
Interface, PREFERRED_NETWORK_PROTOCOL, RequestTimedOut, NetworkTimeout, BUCKET_NAME_OF_ONION_SERVERS,
|
||||||
NetworkException, RequestCorrupted, ServerAddr, TxBroadcastError,
|
NetworkException, RequestCorrupted, ServerAddr, TxBroadcastError,
|
||||||
)
|
)
|
||||||
from .version import PROTOCOL_VERSION
|
from .version import PROTOCOL_VERSION_MIN
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
from .logging import get_logger, Logger
|
from .logging import get_logger, Logger
|
||||||
from .fee_policy import FeeHistogram, FeeTimeEstimates, FEE_ETA_TARGETS
|
from .fee_policy import FeeHistogram, FeeTimeEstimates, FEE_ETA_TARGETS
|
||||||
@@ -118,7 +118,7 @@ def parse_servers(result: Sequence[Tuple[str, str, List[str]]]) -> Dict[str, dic
|
|||||||
def filter_version(servers):
|
def filter_version(servers):
|
||||||
def is_recent(version):
|
def is_recent(version):
|
||||||
try:
|
try:
|
||||||
return util.versiontuple(version) >= util.versiontuple(PROTOCOL_VERSION)
|
return util.versiontuple(version) >= util.versiontuple(PROTOCOL_VERSION_MIN)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False
|
return False
|
||||||
return {k: v for k, v in servers.items() if is_recent(v.get('version'))}
|
return {k: v for k, v in servers.items() if is_recent(v.get('version'))}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
ELECTRUM_VERSION = '4.6.2' # version of the client package
|
ELECTRUM_VERSION = '4.6.2' # version of the client package
|
||||||
|
|
||||||
PROTOCOL_VERSION = '1.4' # protocol version requested
|
PROTOCOL_VERSION_MIN = '1.4' # electrum protocol
|
||||||
|
PROTOCOL_VERSION_MAX = '1.4'
|
||||||
|
|
||||||
# The hash of the mnemonic seed must begin with this
|
# The hash of the mnemonic seed must begin with this
|
||||||
SEED_PREFIX = '01' # Standard wallet
|
SEED_PREFIX = '01' # Standard wallet
|
||||||
|
|||||||
Reference in New Issue
Block a user