From 28e7842f4d9d65ffeedad5867fbc51a0ae882635 Mon Sep 17 00:00:00 2001 From: nolash Date: Sun, 29 Aug 2021 10:15:37 +0200 Subject: [PATCH] More docs, include backend 'abstract' interface --- chainqueue/backend.py | 140 ++++++++++++++++++++++++++++++++++++++ chainqueue/sql/backend.py | 13 +--- doc/infotex/exec.texi | 6 ++ doc/infotex/index.texi | 2 + doc/infotex/stack.texi | 58 ++++++++++++++++ setup.cfg | 2 +- 6 files changed, 210 insertions(+), 11 deletions(-) create mode 100644 chainqueue/backend.py create mode 100644 doc/infotex/exec.texi create mode 100644 doc/infotex/stack.texi diff --git a/chainqueue/backend.py b/chainqueue/backend.py new file mode 100644 index 0000000..fbf163a --- /dev/null +++ b/chainqueue/backend.py @@ -0,0 +1,140 @@ +# external imports +from chainlib.error import DefaultErrorParser + +# local imports +from chainqueue.encode import TxHexNormalizer + + +class Backend: + """Base constructor for backend implementation. + + :param tx_normalizer: Transaction data normalizer + :type tx_normalizer: Object implementing chainqueue.encode.TxHexNormalizer interface + :param error_parser: Error parser to use for RPC calls within the backend component + :type error_parser: Object implementing chainlib.error.DefaultErrorParser + :param debug: Activate backend debugging + :type debug: bool + """ + def __init__(self, tx_normalizer=None, error_parser=None, debug=False): + if error_parser == None: + error_parser = DefaultErrorParser() + self.error_parser = error_parser + if tx_normalizer == None: + tx_normalizer = TxHexNormalizer() + self.tx_normalizer = tx_normalizer + self.debug = debug + + + def create(self, chain_spec, nonce, holder_address, tx_hash, signed_tx, obsolete_predecessors=True, session=None): + """Create a new transaction record in backend. + + The nonce field is provided as a convenience to avoid needless resources spent on decoding the transaction data to retrieve it. + + :param chain_spec: Chain spec to add record for + :type chain_spec: chainlib.chain.ChainSpec + :param nonce: Transaction nonce + :type nonce: int + :param holder_address: Address of transaction sender + :type holder_address: str + :param tx_hash: Transaction hash + :type tx_hash: str + :param signed_tx: Signed transaction data + :type signed_tx: str + :param obsolete_predecessors: If set, will mark older transactions with same nonce from holder_address as obsolete + :type obsolete_predecessors: bool + :param session: Sqlalchemy database session + :type session: sqlalchemy.orm.Session + :rtype: int + :returns: 0 if successfully added + """ + raise NotImplementedError() + + + def cache(self, tx, session=None): + """Create a new cache record for existing outgoing transaction in backend. + + :param tx: Transaction dict representation + :type tx: dict + :param session: Sqlalchemy database session + :type session: sqlalchemy.orm.Session + :rtype: int + :returns: 0 if successful + """ + raise NotImplementedError() + + + def get_otx(self, chain_spec, tx_hash, session=None): + """Retrieve a single otx summary dictionary by transaction hash. + + :param chain_spec: Chain spec context to look up transaction with + :type chain_spec: chainlib.chain.ChainSpec + :param tx_hash: Transaction hash + :type tx_hash: str + :param session: Sqlalchemy database session + :type session: sqlalchemy.orm.Session + :rtype: dict + :returns: otx record summary + """ + raise NotImplementedError() + + + def get(self, chain_spec, decoder, session=None, requeue=False, *args, **kwargs): + """Gets transaction lists based on given criteria. + + :param chain_spec: Chain spec context to look up transactions for + :type chain_spec: chainlib.chain.ChainSpec + :param decoder: Decoder instance to parse values from serialized transaction data in record + :type decoder: Function taking serialized tx as parameter + :param session: Sqlalchemy database session + :type session: sqlalchemy.orm.Session + :param status: Only match transaction that have the given bits set + :type status: int + :param not_status: Only match transactions that have none of the given bits set + :type not_status: int + :param recipient: Only match transactions that has the given address as recipient + :type recipient: str + :param before: Only match tranaactions that were last checked before the given time + :type before: datetime.datetime + :param limit: Return at most given number of transaction. If 0, will return all matched transactions. + :type limit: int + :rtype: dict + :returns: key value pairs of transaction hash and signed transaction data for all matching transactions + """ + raise NotImplementedError() + + + def dispatch(self, chain_spec, rpc, tx_hash, payload, session=None): + """Send a single queued transaction. + + :param chain_spec: Chain spec context for network send + :type chain_spec: chainlib.chain.ChainSpec + :param rpc: RPC connection to use for send + :type rpc: chainlib.connection.RPCConnection + :param tx_hash: Transaction hash of transaction to send + :type tx_hash: str + :param payload: Prepared RPC query to send + :type payload: any + :param session: Sqlalchemy database session + :type session: sqlalchemy.orm.Session + :rtype: int + :returns: 0 if no error + """ + raise NotImplementedError() + + + def create_session(self, session=None): + """Create or pass on a new backend connection session. + + :param session: Use existing session + :type session: varies + """ + raise NotImplementedError() + + + def release_session(self, session): + """Release resources held by session. + + :param session: Session to release + :type session: varies + """ + raise NotImplementedError() diff --git a/chainqueue/sql/backend.py b/chainqueue/sql/backend.py index 8960eae..3c9063c 100644 --- a/chainqueue/sql/backend.py +++ b/chainqueue/sql/backend.py @@ -10,7 +10,6 @@ from sqlalchemy.exc import ( from chainlib.error import ( RPCException, RPCNonceException, - DefaultErrorParser, ) # local imports @@ -29,12 +28,12 @@ from chainqueue.sql.state import ( set_rejected, ) from chainqueue.sql.tx import cache_tx_dict -from chainqueue.encode import TxHexNormalizer +from chainqueue.backend import Backend logg = logging.getLogger(__name__) -class SQLBackend: +class SQLBackend(Backend): """SQL flavor of the chainqueue backend implementation. :param conn_spec: Backend-dependent connection specification string. See chainqueue.db.models.base.SessionBase.connect @@ -47,16 +46,10 @@ class SQLBackend: :type pool_size: int :param debug: Activate SQL engine level debug. See chainqueue.db.models.base.SessionBase.connect :type debug: bool - :todo: define a backend abstract interface class """ def __init__(self, conn_spec, tx_normalizer=None, error_parser=None, pool_size=0, debug=False, *args, **kwargs): + super(SQLBackend, self).__init__(tx_normalizer=tx_normalizer, error_parser=error_parser, debug=debug) SessionBase.connect(conn_spec, pool_size=pool_size, debug=debug) - if error_parser == None: - error_parser = DefaultErrorParser() - self.error_parser = error_parser - if tx_normalizer == None: - tx_normalizer = TxHexNormalizer() - self.tx_normalizer = tx_normalizer def create(self, chain_spec, nonce, holder_address, tx_hash, signed_tx, obsolete_predecessors=True, session=None): diff --git a/doc/infotex/exec.texi b/doc/infotex/exec.texi new file mode 100644 index 0000000..fa38e1b --- /dev/null +++ b/doc/infotex/exec.texi @@ -0,0 +1,6 @@ +@node chainqueue-executable +@section Executables + +Chainqueue only provides a single executable. This executable lists items in queue based on given criteria. The available criteria more or less map to the arguments offered by @code{chainqueue.backend.Backend.get}. + +When installing @code{chainqueue} as a python package, the list tool will be available in @code{PATH} as the command @file{chainqueue-list}. diff --git a/doc/infotex/index.texi b/doc/infotex/index.texi index 473573b..e9def74 100644 --- a/doc/infotex/index.texi +++ b/doc/infotex/index.texi @@ -4,3 +4,5 @@ @include tx.texi @include state.texi +@include stack.texi +@include exec.texi diff --git a/doc/infotex/stack.texi b/doc/infotex/stack.texi new file mode 100644 index 0000000..34664c0 --- /dev/null +++ b/doc/infotex/stack.texi @@ -0,0 +1,58 @@ +@node chainqueue-architecture +@section State storage + +Chainqueue enables separate implementations of the state storage layer backend. + +Included in the package are state storage for sql using the SQLAlchemy dependency, as well as state using native filesystem backend (without any additional dependencies). + +The backend interface is defined in the @code{chainqueue.backend} module. It provides the following methods: + +@itemize +@item Create a new queue item +@item Add cached data to queue item +@item Get a single queue item +@item Get multiple queue items based on given criteria +@item Dispatch a queue item to the network +@item Connect / disconnect to backend +@end itemize + + +@subsection SQL backend + +This backend is Contained in the module @code{chainqueue.sql.backend.SQLBackend}. It translates high-level calls to invididual method calls in the query, state and tx submodules of @code{chainqueue.sql.backend}. + +The @code{SQLBackend} object should provide all methods required to make practical use of the chainqueue package. However, all other modules in @code{chainqueue.sql} are also intended for public consumption. + +The @code{chainqueue.sql.backend} in turn calls methods in the SQLAlchemy database model layer in @code{chainqueue.db.models}. The consumer is not intended to interface directly with @code{chainqueue.db.models}. + + +@subsection Filesystem backend + +The filesystem state storage is provided by the @code{chainqueue.fs} module. is at the moment missing the @code{chainqueue.backend.Backend} implementation. Please refer to the code in @file{tests/tests_fs*.py} to learn how to use in its current state. + + +@section Adapters + +The adapter layer enables chain specific code to be combined with an arbitrary storage backend. Typically, chain specific code is required to: + +@itemize +@item Translate transaction wire format to generic transaction representation +@item Send transactions to the network +@end itemize + +The adapter consumes a backend, and delegates calls to it as required. + +Since adapters are chain specific, @code{chainqueue} only provides a base class that must be extended the chain implementer code. Specifically, the methods to extend are: + +@table @code +@item Add +Add a transaction to the queue +@item Upcoming +Get queued transactions ready to be sent to network +@item Dispatch +Send a queued transaction to the network +@item Translate +Decode details of a transaction +@item Create_session, release_session +Session management to control queue state integrity +@end table diff --git a/setup.cfg b/setup.cfg index 2cc1095..dd72634 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = chainqueue -version = 0.0.4a8 +version = 0.0.5a1 description = Generic blockchain transaction queue control author = Louis Holbrook author_email = dev@holbrook.no