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 actions).
14//! 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 [ActionConfirmation](action_queue::ActionConfirmation)
17//! 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,
22//!    an 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"
24//!    did not 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 guarantees
27//! 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 [Action](chain_types::actions::Action)
31//! enum with parameters that already closely resemble the required on-chain transaction input for that action to be
32//! 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 [Actions](chain_types::actions::Action) submitted
40//! 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)
42//!    via a [PayloadGenerator](payload::PayloadGenerator<T>)
43//! 2. submit the [TypedTransaction](chain_types::TypedTransaction) on-chain via a [TransactionExecutor](action_queue::TransactionExecutor)
44//! 3. generate an [IndexerExpectation](action_state::IndexerExpectation) from the submitted action
45//! 4. submit the [IndexerExpectation](action_state::IndexerExpectation) in an [ActionState](action_state::ActionState) implementation
46//! 5. wait for expectation to be resolved (or timeout, see [ActionQueueConfig](action_queue::ActionQueueConfig)
47//!    and resolve the action submitter's [PendingAction](action_queue::PendingAction).
48//!
49//! In other words, the [ActionQueue](action_queue::ActionQueue) takes care of two important mappings:
50//! 1. [Action](chain_types::actions::Action) to [TypedTransaction](chain_types::TypedTransaction)
51//! 2. [Action](chain_types::actions::Action) to [IndexerExpectation](action_state::IndexerExpectation)
52//!
53//! The first one makes it possible for an action to make it on-chain, the second one allows to
54//! be informed of the action's result and effect.
55//!
56//! See the [action_queue] module for details.
57//!
58//! ### On-chain expectations after an action is submitted
59//! The [action_state] module defines the [ActionState](action_state::ActionState) trait which is responsible
60//! for registering [IndexerExpectation](action_state::IndexerExpectation) done by the [ActionQueue](action_queue::ActionQueue).
61//! The implementor of the [ActionState](action_state::ActionState) should be monitoring the state of the block chain (reading
62//! event logs contained inside newly mined blocks) and with each on-chain event that matches a registered expectation,
63//! mark it as resolved to allow the bound action to be confirmed in the [ActionQueue](action_queue::ActionQueue).
64//! Therefore, the two components which interact with an [ActionState](action_state::ActionState) implementation
65//! are the [ActionQueue](action_queue::ActionQueue) and the on-chain Indexer (see `chain-indexer` crate for details).
66//!
67//! There's an exception on construction of an expectation for the `withdraw` [NodeAction](node::NodeActions):
68//! Since the current implementation of the Indexer does not track native token transfers, but only smart contract
69//! events, it is impossible to detect the native token's transfer confirmation.
70//! Since the `withdraw` action is not very common, its confirmation is tracked via direct polling of the RPC
71//! provider until the transaction is confirmed. The confirmation horizon is set in the [TransactionExecutor](action_queue::TransactionExecutor).
72//!
73//! See the [action_state] module for details.
74//!
75//! ## Payload generators
76//! As described above, the [ActionQueue](action_queue::ActionQueue) needs a [PayloadGenerator](payload::PayloadGenerator<T>)
77//! implementation to be able to translate the [Action](chain_types::actions::Action) into the [TypedTransaction](chain_types::TypedTransaction).
78//! There are currently two possible ways of constructing the action's transaction:
79//! - via plain EIP1559 payload
80//! - via EIP1559 payload containing a SAFE transaction that embeds the actual payload
81//!
82//! The former one is implemented via [BasicPayloadGenerator](payload::BasicPayloadGenerator), the latter
83//! is implemented in [SafePayloadGenerator](payload::SafePayloadGenerator).
84//!
85//! In most situations, the transaction payload an action translates to, typically constitutes a smart contract call
86//! of one of the HOPR smart contracts deployed on-chain.
87//!
88//! See the [payload] module for details.
89use hopr_crypto_types::prelude::*;
90use hopr_primitive_types::prelude::*;
91
92use crate::action_queue::ActionSender;
93
94pub mod action_queue;
95pub mod action_state;
96pub mod channels;
97/// Contains all errors used in this crate.
98pub mod errors;
99pub mod node;
100pub mod payload;
101pub mod redeem;
102
103/// Contains all actions that a node can execute on-chain.
104#[derive(Debug, Clone)]
105pub struct ChainActions<Db>
106where
107    Db: Clone + std::fmt::Debug,
108{
109    me: Address,
110    chain_key: ChainKeypair,
111    db: Db,
112    tx_sender: ActionSender,
113}
114
115impl<Db> ChainActions<Db>
116where
117    Db: Clone + std::fmt::Debug,
118{
119    /// Creates new instance.
120    pub fn new(me: &ChainKeypair, db: Db, tx_sender: ActionSender) -> Self {
121        Self {
122            me: me.public().to_address(),
123            chain_key: me.clone(),
124            db,
125            tx_sender,
126        }
127    }
128
129    /// On-chain address of this node
130    pub fn self_address(&self) -> Address {
131        self.me
132    }
133}