hopr_chain_actions/lib.rs
1//! Contains high-level traits that translate to on-chain transaction interactions
2//!
3//! ## Actions
4//! The main concept is an "action", which a node can perform and results into an on-chain
5//! operation. These actions are all on the external interface of this crate which is represented
6//! by the [ChainActions] type.
7//! There are 3 classes of actions implemented in submodules of this crate:
8//! - [channel actions](channels)
9//! - [ticket redeem actions](redeem)
10//! - [node actions](node)
11//!
12//! Each action is represented by a method (or methods) that are imported into the [ChainActions] type
13//! through a trait from the respective module (e.g. [ChannelActions](channels::ChannelActions) trait for channel
14//! actions). Each action will eventually translate to an on-chain transaction.
15//! An action will always return a [PendingAction](action_queue::PendingAction) future. This
16//! future can be awaited or not, depending if the caller wishes to obtain the
17//! [ActionConfirmation](action_queue::ActionConfirmation) of the submitted action.
18//! If the action's caller wishes to await the confirmation, this process can end in one of 3 possible states:
19//!
20//! 1. the action gets confirmed, meaning it has been successfully executed.
21//! 2. awaiting the confirmation returns an error, which typically means a failure during action prerequisite checks, an
22//! invalid state to execute the action or invalid arguments given to the action
23//! 3. awaiting the confirmation times out, meaning the execution failed on-chain and the "action expectations" did not
24//! yield (see below for details on how "action expectations work").
25//!
26//! Not awaiting the returned [PendingAction](action_queue::PendingAction) future does not give the caller any
27//! guarantees on how the action has executed, although this might be perfectly fine for certain cases (fire & forget).
28//!
29//! ## How are actions executed and make it on-chain ?
30//! Call to any [ChainAction's](ChainActions<Db>) method is eventually represented as an
31//! [Action](chain_types::actions::Action) enum with parameters that already closely resemble the required on-chain
32//! transaction input for that action to be The [Action](chain_types::actions::Action) enum instance is then passed via
33//! an [ActionSender] into the [ActionQueue](action_queue::ActionQueue).
34//! The [ActionQueue](action_queue::ActionQueue) takes care of ensuring the FIFO order of the
35//! actions which is driven by a standalone [action loop](`action_queue::ActionQueue::start()`) and must be instantiated
36//! before [ChainActions], so that it can provide it with an [ActionSender].
37//!
38//! ### Queueing of actions
39//! The [ActionQueue](action_queue::ActionQueue) operates a MPSC queue, which picks up the
40//! [Actions](chain_types::actions::Action) submitted to it one by one. With each such action it will:
41//! 1. transform [Action](chain_types::actions::Action) into a [TypedTransaction](chain_types::TypedTransaction) via a
42//! [PayloadGenerator](payload::PayloadGenerator<T>)
43//! 2. submit the [TypedTransaction](chain_types::TypedTransaction) on-chain via a
44//! [TransactionExecutor](action_queue::TransactionExecutor)
45//! 3. generate an [IndexerExpectation](action_state::IndexerExpectation) from the submitted action
46//! 4. submit the [IndexerExpectation](action_state::IndexerExpectation) in an [ActionState](action_state::ActionState)
47//! implementation
48//! 5. wait for expectation to be resolved (or timeout, see [ActionQueueConfig](action_queue::ActionQueueConfig) and
49//! resolve the action submitter's [PendingAction](action_queue::PendingAction).
50//!
51//! In other words, the [ActionQueue](action_queue::ActionQueue) takes care of two important mappings:
52//! 1. [Action](chain_types::actions::Action) to [TypedTransaction](chain_types::TypedTransaction)
53//! 2. [Action](chain_types::actions::Action) to [IndexerExpectation](action_state::IndexerExpectation)
54//!
55//! The first one makes it possible for an action to make it on-chain, the second one allows to
56//! be informed of the action's result and effect.
57//!
58//! See the [action_queue] module for details.
59//!
60//! ### On-chain expectations after an action is submitted
61//! The [action_state] module defines the [ActionState](action_state::ActionState) trait which is responsible
62//! for registering [IndexerExpectation](action_state::IndexerExpectation) done by the
63//! [ActionQueue](action_queue::ActionQueue). The implementor of the [ActionState](action_state::ActionState) should be
64//! monitoring the state of the block chain (reading event logs contained inside newly mined blocks) and with each
65//! on-chain event that matches a registered expectation, mark it as resolved to allow the bound action to be confirmed
66//! in the [ActionQueue](action_queue::ActionQueue). Therefore, the two components which interact with an
67//! [ActionState](action_state::ActionState) implementation are the [ActionQueue](action_queue::ActionQueue) and the
68//! on-chain Indexer (see `chain-indexer` crate for details).
69//!
70//! There's an exception on construction of an expectation for the `withdraw` [NodeAction](node::NodeActions):
71//! Since the current implementation of the Indexer does not track native token transfers, but only smart contract
72//! events, it is impossible to detect the native token's transfer confirmation.
73//! Since the `withdraw` action is not very common, its confirmation is tracked via direct polling of the RPC
74//! provider until the transaction is confirmed. The confirmation horizon is set in the
75//! [TransactionExecutor](action_queue::TransactionExecutor).
76//!
77//! See the [action_state] module for details.
78//!
79//! ## Payload generators
80//! As described above, the [ActionQueue](action_queue::ActionQueue) needs a
81//! [PayloadGenerator](payload::PayloadGenerator<T>) implementation to be able to translate the
82//! [Action](chain_types::actions::Action) into the [TypedTransaction](chain_types::TypedTransaction).
83//! There are currently two possible ways of constructing the action's transaction:
84//! - via plain EIP1559 payload
85//! - via EIP1559 payload containing a SAFE transaction that embeds the actual payload
86//!
87//! The former one is implemented via [BasicPayloadGenerator](payload::BasicPayloadGenerator), the latter
88//! is implemented in [SafePayloadGenerator](payload::SafePayloadGenerator).
89//!
90//! In most situations, the transaction payload an action translates to, typically constitutes a smart contract call
91//! of one of the HOPR smart contracts deployed on-chain.
92//!
93//! See the [payload] module for details.
94use hopr_crypto_types::prelude::*;
95use hopr_primitive_types::prelude::*;
96
97use crate::action_queue::ActionSender;
98
99pub mod action_queue;
100pub mod action_state;
101pub mod channels;
102/// Contains all errors used in this crate.
103pub mod errors;
104pub mod node;
105pub mod payload;
106pub mod redeem;
107
108/// Contains all actions that a node can execute on-chain.
109#[derive(Debug, Clone)]
110pub struct ChainActions<Db>
111where
112 Db: Clone + std::fmt::Debug,
113{
114 me: Address,
115 chain_key: ChainKeypair,
116 db: Db,
117 tx_sender: ActionSender,
118}
119
120impl<Db> ChainActions<Db>
121where
122 Db: Clone + std::fmt::Debug,
123{
124 /// Creates new instance.
125 pub fn new(me: &ChainKeypair, db: Db, tx_sender: ActionSender) -> Self {
126 Self {
127 me: me.public().to_address(),
128 chain_key: me.clone(),
129 db,
130 tx_sender,
131 }
132 }
133
134 /// On-chain address of this node
135 pub fn self_address(&self) -> Address {
136 self.me
137 }
138}