More docs, include backend 'abstract' interface
This commit is contained in:
parent
c85851f370
commit
28e7842f4d
140
chainqueue/backend.py
Normal file
140
chainqueue/backend.py
Normal file
@ -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()
|
@ -10,7 +10,6 @@ from sqlalchemy.exc import (
|
|||||||
from chainlib.error import (
|
from chainlib.error import (
|
||||||
RPCException,
|
RPCException,
|
||||||
RPCNonceException,
|
RPCNonceException,
|
||||||
DefaultErrorParser,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
@ -29,12 +28,12 @@ from chainqueue.sql.state import (
|
|||||||
set_rejected,
|
set_rejected,
|
||||||
)
|
)
|
||||||
from chainqueue.sql.tx import cache_tx_dict
|
from chainqueue.sql.tx import cache_tx_dict
|
||||||
from chainqueue.encode import TxHexNormalizer
|
from chainqueue.backend import Backend
|
||||||
|
|
||||||
logg = logging.getLogger(__name__)
|
logg = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SQLBackend:
|
class SQLBackend(Backend):
|
||||||
"""SQL flavor of the chainqueue backend implementation.
|
"""SQL flavor of the chainqueue backend implementation.
|
||||||
|
|
||||||
:param conn_spec: Backend-dependent connection specification string. See chainqueue.db.models.base.SessionBase.connect
|
: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
|
:type pool_size: int
|
||||||
:param debug: Activate SQL engine level debug. See chainqueue.db.models.base.SessionBase.connect
|
:param debug: Activate SQL engine level debug. See chainqueue.db.models.base.SessionBase.connect
|
||||||
:type debug: bool
|
: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):
|
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)
|
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):
|
def create(self, chain_spec, nonce, holder_address, tx_hash, signed_tx, obsolete_predecessors=True, session=None):
|
||||||
|
6
doc/infotex/exec.texi
Normal file
6
doc/infotex/exec.texi
Normal file
@ -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}.
|
@ -4,3 +4,5 @@
|
|||||||
|
|
||||||
@include tx.texi
|
@include tx.texi
|
||||||
@include state.texi
|
@include state.texi
|
||||||
|
@include stack.texi
|
||||||
|
@include exec.texi
|
||||||
|
58
doc/infotex/stack.texi
Normal file
58
doc/infotex/stack.texi
Normal file
@ -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
|
@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = chainqueue
|
name = chainqueue
|
||||||
version = 0.0.4a8
|
version = 0.0.5a1
|
||||||
description = Generic blockchain transaction queue control
|
description = Generic blockchain transaction queue control
|
||||||
author = Louis Holbrook
|
author = Louis Holbrook
|
||||||
author_email = dev@holbrook.no
|
author_email = dev@holbrook.no
|
||||||
|
Loading…
Reference in New Issue
Block a user