2021-06-01 12:43:12 +02:00
|
|
|
# standard imports
|
|
|
|
import enum
|
|
|
|
|
|
|
|
|
|
|
|
@enum.unique
|
|
|
|
class StatusBits(enum.IntEnum):
|
|
|
|
"""Individual bit flags that are combined to define the state and legacy of a queued transaction
|
|
|
|
|
|
|
|
"""
|
2021-08-27 09:44:12 +02:00
|
|
|
QUEUED = 0x01
|
|
|
|
"""Transaction should be sent to network"""
|
|
|
|
RESERVED = 0x02
|
|
|
|
"""Transaction is currently being handled by a thread"""
|
|
|
|
IN_NETWORK = 0x08
|
|
|
|
"""Transaction is in network"""
|
2021-06-01 12:43:12 +02:00
|
|
|
|
2021-08-27 09:44:12 +02:00
|
|
|
DEFERRED = 0x10
|
|
|
|
"""An attempt to send the transaction to network has failed"""
|
|
|
|
GAS_ISSUES = 0x20
|
|
|
|
"""Transaction is pending sender account fee funding"""
|
|
|
|
|
|
|
|
LOCAL_ERROR = 0x100
|
|
|
|
"""Errors that originate internally from the component"""
|
|
|
|
NODE_ERROR = 0x200
|
|
|
|
"""Errors originating in the node (e.g. invalid transaction wire format input...)"""
|
|
|
|
NETWORK_ERROR = 0x400
|
|
|
|
"""Errors that originate from the network (REVERT)"""
|
|
|
|
UNKNOWN_ERROR = 0x800
|
|
|
|
"""Unclassified errors (that should not occur)"""
|
|
|
|
|
|
|
|
FINAL = 0x1000
|
|
|
|
"""Transaction processing has completed"""
|
|
|
|
OBSOLETE = 0x2000
|
|
|
|
"""Transaction has been replaced by a different transaction (normally with higher fee)"""
|
|
|
|
MANUAL = 0x8000
|
|
|
|
"""Transaction processing has been manually overridden"""
|
2021-06-01 12:43:12 +02:00
|
|
|
|
|
|
|
|
|
|
|
@enum.unique
|
|
|
|
class StatusEnum(enum.IntEnum):
|
2021-08-26 10:05:56 +02:00
|
|
|
"""Semantic states intended for human consumption
|
2021-06-01 12:43:12 +02:00
|
|
|
"""
|
|
|
|
PENDING = 0
|
2021-08-27 09:44:12 +02:00
|
|
|
"""Transaction has been added but no processing has been performed"""
|
2021-06-01 12:43:12 +02:00
|
|
|
|
|
|
|
SENDFAIL = StatusBits.DEFERRED | StatusBits.LOCAL_ERROR
|
2021-08-27 09:44:12 +02:00
|
|
|
"""Temporary error occurred when sending transaction to node"""
|
2021-06-01 12:43:12 +02:00
|
|
|
RETRY = StatusBits.QUEUED | StatusBits.DEFERRED
|
2021-08-27 09:44:12 +02:00
|
|
|
"""Transaction is ready to retry send to the network"""
|
2021-06-01 12:43:12 +02:00
|
|
|
READYSEND = StatusBits.QUEUED
|
2021-08-27 09:44:12 +02:00
|
|
|
"""Transaction is ready to be sent to network for first time"""
|
2021-08-26 10:05:56 +02:00
|
|
|
RESERVED = StatusBits.RESERVED
|
2021-08-27 09:44:12 +02:00
|
|
|
"""Transaction is currently being handled by a thread"""
|
2021-06-01 12:43:12 +02:00
|
|
|
|
|
|
|
OBSOLETED = StatusBits.OBSOLETE | StatusBits.IN_NETWORK
|
2021-08-27 09:44:12 +02:00
|
|
|
"""Transaction already in network has been replaced by a different transaction"""
|
2021-06-01 12:43:12 +02:00
|
|
|
|
|
|
|
WAITFORGAS = StatusBits.GAS_ISSUES
|
2021-08-27 09:44:12 +02:00
|
|
|
"""Transaction is pending sender account fee funding"""
|
2021-06-01 12:43:12 +02:00
|
|
|
|
|
|
|
SENT = StatusBits.IN_NETWORK
|
2021-08-27 09:44:12 +02:00
|
|
|
"""Transaction is in network"""
|
2021-06-01 12:43:12 +02:00
|
|
|
FUBAR = StatusBits.FINAL | StatusBits.UNKNOWN_ERROR
|
2021-08-27 09:44:12 +02:00
|
|
|
"""Transaction processing encountered an unknown error and will not be processed further"""
|
2021-06-01 12:43:12 +02:00
|
|
|
CANCELLED = StatusBits.IN_NETWORK | StatusBits.FINAL | StatusBits.OBSOLETE
|
2021-08-27 09:44:12 +02:00
|
|
|
"""A superseding transaction was confirmed by the network, and the current transaction will not be processed further"""
|
2021-06-01 12:43:12 +02:00
|
|
|
OVERRIDDEN = StatusBits.FINAL | StatusBits.OBSOLETE | StatusBits.MANUAL
|
2021-08-27 09:44:12 +02:00
|
|
|
"""A superseding transaction was manually added. The current transaction will not be sent to network nor processed further"""
|
2021-06-01 12:43:12 +02:00
|
|
|
|
|
|
|
REJECTED = StatusBits.NODE_ERROR | StatusBits.FINAL
|
2021-08-27 09:44:12 +02:00
|
|
|
"""A permanent error occurred when attempting to send transaction to node"""
|
2021-06-01 12:43:12 +02:00
|
|
|
REVERTED = StatusBits.IN_NETWORK | StatusBits.FINAL | StatusBits.NETWORK_ERROR
|
2021-08-27 09:44:12 +02:00
|
|
|
"""Execution of transaction on network completed and was unsuccessful."""
|
2021-06-01 12:43:12 +02:00
|
|
|
SUCCESS = StatusBits.IN_NETWORK | StatusBits.FINAL
|
2021-08-27 09:44:12 +02:00
|
|
|
"""Execution of transaction on network completed and was successful"""
|
2021-06-01 12:43:12 +02:00
|
|
|
|
|
|
|
|
|
|
|
def status_str(v, bits_only=False):
|
|
|
|
"""Render a human-readable string describing the status
|
|
|
|
|
|
|
|
If the bit field exactly matches a StatusEnum value, the StatusEnum label will be returned.
|
|
|
|
|
|
|
|
If a StatusEnum cannot be matched, the string will be postfixed with "*", unless explicitly instructed to return bit field labels only.
|
|
|
|
|
|
|
|
:param v: Status bit field
|
|
|
|
:type v: number
|
|
|
|
:param bits_only: Only render individual bit labels.
|
|
|
|
:type bits_only: bool
|
|
|
|
:returns: Status string
|
|
|
|
:rtype: str
|
|
|
|
"""
|
|
|
|
s = ''
|
|
|
|
if not bits_only:
|
|
|
|
try:
|
|
|
|
s = StatusEnum(v).name
|
|
|
|
return s
|
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if v == 0:
|
|
|
|
return 'NONE'
|
|
|
|
|
|
|
|
for i in range(16):
|
|
|
|
b = (1 << i)
|
|
|
|
if (b & 0xffff) & v:
|
|
|
|
n = StatusBits(b).name
|
|
|
|
if len(s) > 0:
|
|
|
|
s += ','
|
|
|
|
s += n
|
|
|
|
if not bits_only:
|
|
|
|
s += '*'
|
|
|
|
return s
|
|
|
|
|
|
|
|
|
|
|
|
def status_bytes(status=0):
|
2021-08-26 10:05:56 +02:00
|
|
|
"""Serialize a status bit field integer value to bytes.
|
|
|
|
|
|
|
|
if invoked without an argument, it will return the serialization of an empty state.
|
|
|
|
|
|
|
|
:param status: Status bit field
|
|
|
|
:type status: number
|
|
|
|
:returns: Serialized value
|
|
|
|
:rtype: bytes
|
|
|
|
"""
|
2021-06-01 12:43:12 +02:00
|
|
|
return status.to_bytes(8, byteorder='big')
|
|
|
|
|
|
|
|
|
|
|
|
def all_errors():
|
|
|
|
"""Bit mask of all error states
|
|
|
|
|
|
|
|
:returns: Error flags
|
|
|
|
:rtype: number
|
|
|
|
"""
|
|
|
|
return StatusBits.LOCAL_ERROR | StatusBits.NODE_ERROR | StatusBits.NETWORK_ERROR | StatusBits.UNKNOWN_ERROR
|
|
|
|
|
2021-08-26 10:05:56 +02:00
|
|
|
errors = all_errors()
|
2021-06-01 12:43:12 +02:00
|
|
|
|
|
|
|
def is_error_status(v):
|
|
|
|
"""Check if value is an error state
|
|
|
|
|
|
|
|
:param v: Status bit field
|
|
|
|
:type v: number
|
|
|
|
:returns: True if error
|
|
|
|
:rtype: bool
|
|
|
|
"""
|
|
|
|
return bool(v & all_errors())
|
|
|
|
|
|
|
|
|
|
|
|
__ignore_manual_value = ~StatusBits.MANUAL
|
|
|
|
def ignore_manual(v):
|
2021-08-26 10:05:56 +02:00
|
|
|
"""Reset the MANUAL bit from the given status
|
|
|
|
|
|
|
|
:param v: Status bit field
|
|
|
|
:type v: number
|
|
|
|
:returns: Input state with manual bit removed
|
|
|
|
:rtype: number
|
|
|
|
"""
|
2021-06-01 12:43:12 +02:00
|
|
|
return v & __ignore_manual_value
|
|
|
|
|
|
|
|
|
|
|
|
def is_nascent(v):
|
2021-08-26 10:05:56 +02:00
|
|
|
"""Check if state is the empty state
|
|
|
|
|
|
|
|
:param v: Status bit field
|
|
|
|
:type v: number
|
|
|
|
:returns: True if empty
|
|
|
|
:rtype: bool
|
|
|
|
"""
|
2021-06-01 12:43:12 +02:00
|
|
|
return ignore_manual(v) == StatusEnum.PENDING
|
|
|
|
|
|
|
|
|
|
|
|
def dead():
|
|
|
|
"""Bit mask defining whether a transaction is still likely to be processed on the network.
|
|
|
|
|
|
|
|
:returns: Bit mask
|
|
|
|
:rtype: number
|
|
|
|
"""
|
|
|
|
return StatusBits.FINAL | StatusBits.OBSOLETE
|
|
|
|
|
|
|
|
|
|
|
|
def is_alive(v):
|
|
|
|
"""Check if transaction is still likely to be processed on the network.
|
|
|
|
|
|
|
|
The contingency of "likely" refers to the case a transaction has been obsoleted after sent to the network, but the network still confirms the obsoleted transaction. The return value of this method will not change as a result of this, BUT the state itself will (as the FINAL bit will be set).
|
|
|
|
|
2021-08-26 10:05:56 +02:00
|
|
|
:param v: Status bit field
|
|
|
|
:type v: number
|
|
|
|
:returns: True if status is not yet finalized
|
|
|
|
:rtype: bool
|
2021-06-01 12:43:12 +02:00
|
|
|
"""
|
|
|
|
return bool(v & dead() == 0)
|