commit 28e7842f4d9d65ffeedad5867fbc51a0ae882635
parent c85851f37080ef0a529befcbabbe94814de9cbd0
Author: nolash <dev@holbrook.no>
Date: Sun, 29 Aug 2021 10:15:37 +0200
More docs, include backend 'abstract' interface
Diffstat:
6 files changed, 210 insertions(+), 11 deletions(-)
diff --git a/chainqueue/backend.py 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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