massa_async_pool/lib.rs
1//! Copyright (c) 2022 MASSA LABS <info@massa.net>
2
3//! # General description
4//!
5//! This crate implements a consensual/deterministic pool of asynchronous messages (`AsyncPool`) within the context of autonomous smart contracts.
6//!
7//! `AsyncPool` is used in conjunction with `FinalLedger` within the `FinalState`, but also as a speculative copy for speculative execution.
8//!
9//! ## Goal
10//!
11//! Allow a smart contract to send a message to trigger another smart contract's handler asynchronously.
12//!
13//! Note that all the "coins" mentioned here are SCE coins.
14//!
15//! ## Message format
16//!
17//! ```json
18//! {
19//! "sender": "xxxx", // address that sent the message and spent fee + coins on emission
20//! "slot": {"period": 123455, "thread": 11}, // slot at which the message was emitted
21//! "emission_index": 212, // index of the message emitted in this slot
22//! "destination": "xxxx", // target address
23//! "function": "handle_message", // name of the function to call in the target SC
24//! "validity_start": {"period": 123456, "thread": 12}, // the message can be handled starting from the validity_start slot (included)
25//! "validity_end": {"period": 123457, "thread": 16}, // the message can be handled until the validity_end slot (excluded)
26//! "max_gas": 12334, // max gas available when the handler is called
27//! "coins": "1111.11", // amount of coins to transfer to the destination address when calling its handler
28//! "function_params": { ... any object ... } // parameters to call the function
29//! }
30//! ```
31//!
32//! ## How to send a message during bytecode execution
33//!
34//! * messages are sent using an ABI: `send_message(target_address, function, validity_start, validity_end, max_gas, fee, coins, function_params) -> Result<(), ABIReturnError>`.
35//! * when called, this ABI does this:
36//! * it consumes `compute_gas_cost_of_message_storage(context.current_slot, validity_end_slot)` of gas in the current execution. This allows making the message emission more gas-consuming when it requires storing the message in queue for longer
37//! * it consumes `fee + coins` coins from the sender
38//! * it generates an `AsyncMessage` and stores it in an asynchronous pool
39//!
40//! Note that `fee + coins` coins are burned when sending the message.
41//!
42//! ## How is the `AsyncPool` handled
43//! ```md
44//! * In the AsyncPool, Messages are kept sorted by `priority = AsyncMessageId(rev(Ratio(msg.fee, max(msg.max_gas,1))), rev(msg.slot), rev(msg.emission_index))`
45//! From execution component version 2 (MIP-0002), batch selection and overflow eviction
46//! re-rank with `fee / max(max_gas + async_msg_cst_gas_cost, 1)` so priority matches the
47//! gas actually charged when taking a batch.
48//!
49//! * when an AsyncMessage is added to the AsyncPool:
50//! * if the AsyncPool length has exceeded config.max_async_pool_length:
51//! * remove the lowest-priority message and reimburse "coins" to the message sender
52//!
53//! * At every slot S :
54//! * expired messages are deleted, and "coins" are credited back to the message sender
55//! * messages that are valid at slot S (in terms of validity_start, validity end) are popped in highest-to-lowest priority order until they accumulate max_async_gas_per_slot. For each selected message M in decreasing priority order:
56//! * make sure that M.target_address exists and has a method called M.target_handler with the right signature, otherwise fail the execution
57//! * credit target_address with M.coins
58//! * run the target handler function with M.payload as parameter and the context:
59//! * max_gas = M.max_gas
60//! * fee = M.fee
61//! * slot = S
62//! * call_stack = [M.target_address, M.sender_address]
63//! * on any failure, cancel all the effects of execution and credit M.coins back to the sender
64//! * if there is a block at slot S, the execution of the block happens here
65//!
66//! ## How to receive a message (inside the smart contract)
67//!
68//! * define a public exported handler function taking 1 parameter
69//! * this function will be called when a message is processed with the right `destination` and `handler`
70//! ```
71//!
72//! # Architecture
73//!
74//! ## message.rs
75//! Defines `AsyncMessage` that represents an asynchronous message.
76//!
77//! ## pool.rs
78//! Defines the `AsyncPool` that manipulates a list of `AsyncMessages` sorted by priority.
79//!
80//! ## changes.rs
81//! Represents and manipulates changes (message additions/deletions) in the `AsyncPool`.
82//!
83//! ## bootstrap.rs
84//! Provides serializable structures and tools for bootstrapping the asynchronous pool.
85//!
86//! ## Test exports
87//!
88//! When the crate feature `test-exports` is enabled, tooling useful for test-exports purposes is exported.
89//! See `test_exports/mod.rs` for details.
90
91mod changes;
92mod config;
93mod pool;
94
95pub use changes::AsyncPoolChanges;
96pub use config::AsyncPoolConfig;
97pub use pool::{AsyncPool, AsyncPoolDeserializer, AsyncPoolSerializer};
98
99#[cfg(test)]
100mod tests;
101
102#[cfg(feature = "test-exports")]
103pub mod test_exports;