Skip to content
This repository has been archived by the owner on Nov 11, 2022. It is now read-only.

Atomic execution protocol and primitives #13

Merged
merged 25 commits into from
Jul 28, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f7709ab
wip: locking primitives
adlrocha Jul 15, 2022
085ea15
wip: sca functions almost done
adlrocha Jul 19, 2022
2a5a89c
wip: writing unit tests
adlrocha Jul 20, 2022
4deb68a
atomic execution functions and unit tests
adlrocha Jul 21, 2022
5ca0e7e
added comments and use right generic types in atomic for next PR
adlrocha Jul 21, 2022
9881884
Update actors/hierarchical_sca/src/atomic/mod.rs
adlrocha Jul 25, 2022
39e2f60
renamed locked and lockable states
adlrocha Jul 25, 2022
1f0c0b0
addressed some comments and remove exec when finalized
adlrocha Jul 26, 2022
60a775a
Add TAddress
aakoshh Jul 26, 2022
bb604c4
Use TAddress
aakoshh Jul 26, 2022
d47c955
Merge remote-tracking branch 'origin/feat/atomic-exec' into taddress
aakoshh Jul 26, 2022
64b7d21
Make Hierarchical generic
aakoshh Jul 26, 2022
91cb133
Note about (not) doing typed comparison with the caller
aakoshh Jul 26, 2022
a24c90e
minor fix
adlrocha Jul 26, 2022
7d1fda9
improved propagate_exec
adlrocha Jul 26, 2022
92a8144
use HashSet instead of HashMap
adlrocha Jul 27, 2022
17a4ea9
compute exec CID before ID translation
adlrocha Jul 27, 2022
77d6eeb
add actor_primtives crate and take exec to its own module
adlrocha Jul 27, 2022
448d866
LockedOutput type alias
adlrocha Jul 27, 2022
9df2e56
Merge remote-tracking branch 'origin/feat/atomic-exec' into taddress
aakoshh Jul 27, 2022
ca61bf2
Fix warnings and compile errors in other crates.
aakoshh Jul 27, 2022
6151d78
Fix tests by adding a TAddressKey type.
aakoshh Jul 27, 2022
afc0e42
Turn caller into TAddress<ID>
aakoshh Jul 27, 2022
2817835
Merge pull request #16 from adlrocha/taddress
aakoshh Jul 27, 2022
f64a126
Remove out_status
aakoshh Jul 27, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 79 additions & 89 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 0 additions & 10 deletions actors/hierarchical_sca/src/atomic.rs

This file was deleted.

160 changes: 160 additions & 0 deletions actors/hierarchical_sca/src/atomic/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
use cid::multihash::Code::Blake2b256;
use cid::multihash::MultihashDigest;
use cid::Cid;
use fil_actors_runtime::cbor;
use fvm_ipld_encoding::{serde_bytes, tuple::*, Cbor, RawBytes, DAG_CBOR};
use fvm_shared::MethodNum;
use serde::de::DeserializeOwned;
use serde::ser::Serialize;

use crate::tcid::{TCid, THamt};

/// MethodNum to lock some state in an actor
/// This methods are only supported in actors
/// that support atomic executions.
pub const METHOD_LOCK: MethodNum = 2;
/// MethodNum used to trigger the merge of an input with
/// other input locked states.
pub const METHOD_MERGE: MethodNum = 3;
/// MethodNum called to signal the abortion of an atomic execution
/// and the unlock of all locked states in the actor for the execution
pub const METHOD_ABORT: MethodNum = 4;
/// MethodNum to trigger the merge of the output of an execution
/// into the state of an actor, and the unlock of all locked states.
pub const METHOD_UNLOCK: MethodNum = 5;

/// Trait that determines the functions that need to be implemented by
/// a state object to be lockable and be used in an atomic execution.
///
/// Different strategies may be used to merge different locked state to
/// prepare the actor state for the execution, and for the merging of the
/// output of the execution to the original state of the actor.
pub trait MergeableState<S: Serialize + DeserializeOwned> {
/// Merge a locked state (not necessarily the output) to the current state.
fn merge(&mut self, other: Self) -> anyhow::Result<()>;
/// Merge the output of an execution to the current state.
fn merge_output(&mut self, other: Self) -> anyhow::Result<()>;
}

/// Trait that specifies the interface of an actor state able to support
/// atomic executions.
pub trait LockableActorState<T>
where
T: Serialize + DeserializeOwned + MergeableState<T>,
{
/// Map with all the locked state in the actor uniquely identified through
/// their Cid.
fn locked_map_cid(&self) -> TCid<THamt<Cid, LockableState<T>>>;
/// Returns the output state of an execution from the current state
/// of the actor according to the input parameters.
fn output(&self, params: LockParams) -> LockableState<T>;
}

/// Return type for all actor functions.
///
/// It returns an option for developers to optionally choose if
/// to return an output in the function.
type ActorResult = anyhow::Result<Option<RawBytes>>;

/// Trait for an actor able to support an atomic execution.
///
/// The functions of this trait represent the set of methods that
/// and actor support atomic executions needs to implement. Correspondingly,
/// it follows the same return convention used for every FVM actor method.
pub trait LockableActor<T, S>
where
T: Serialize + DeserializeOwned + MergeableState<T>,
S: Serialize + DeserializeOwned + LockableActorState<T>,
{
/// Locks the state to perform the execution determined by the locking params.
fn lock(params: LockParams) -> ActorResult;
/// Merges some state to the current state of the actor to prepare for the execution
/// of the protocol.
fn merge(params: MergeParams<T>) -> ActorResult;
/// Merges the output state of an execution to the actor and unlocks the state
/// involved in the execution.
fn unlock(params: UnlockParams) -> ActorResult;
/// Aborts the execution and unlocks the locked state.
fn abort(params: LockParams) -> ActorResult;
/// Returns the lockable state of the actor.
fn state(params: LockParams) -> S;
}

/// Serialized representation of the locked state of an actor.
#[derive(Debug, PartialEq, Eq, Clone, Serialize_tuple, Deserialize_tuple, Default)]
pub struct SerializedState {
#[serde(with = "serde_bytes")]
ser: Vec<u8>,
}
impl SerializedState {
// TODO: This is used for testing purposes in order to have all the
// SCA functions running. In the next iteration we will implement proper
// primitives to get from/to a MergeableState to SerializedState using
// code-gen and generics.
pub fn new(ser: Vec<u8>) -> Self {
SerializedState { ser }
}
pub fn cid(&self) -> Cid {
Cid::new_v1(DAG_CBOR, Blake2b256.digest(self.ser.as_slice()))
}
}

/// Parameters used to lock certain state of an actor for its use in an atomic
/// execution
///
/// Different locking strategies may be implemented in the actor according to the
/// method and parameters used in the atomic execution. This parameters gives
/// information to the actor about the execution to be performed and thus the state
/// that needs to be locked.
#[derive(Debug, Eq, PartialEq, Serialize_tuple, Deserialize_tuple)]
pub struct LockParams {
pub method: MethodNum,
pub params: RawBytes,
}
impl Cbor for LockParams {}
impl LockParams {
pub fn new(method: MethodNum, params: RawBytes) -> Self {
LockParams { method, params }
}
}

/// Parameters used to specify the input state to merge to the current
/// state of an actor to perform the atomic execution.
#[derive(Serialize_tuple, Deserialize_tuple)]
pub struct MergeParams<T>
where
T: Serialize + DeserializeOwned + MergeableState<T>,
{
state: T,
}
impl<T: Serialize + DeserializeOwned + MergeableState<T>> Cbor for MergeParams<T> {}

/// Unlock parameters that pass the output of the execution as the serialized
/// output state of the execution, along with the lock parameters that determines
/// the type of execution being performed and thus the merging strategy that needs
/// to be followed by the actor.
#[derive(Debug, Eq, PartialEq, Serialize_tuple, Deserialize_tuple)]
pub struct UnlockParams {
pub params: LockParams,
pub state: SerializedState, // FIXME: This is a locked state for the output. We may be able to use generics here.
}
impl Cbor for UnlockParams {}
impl UnlockParams {
pub fn new(params: LockParams, state: SerializedState) -> Self {
UnlockParams { params, state }
}
pub fn from_raw_bytes(ser: &RawBytes) -> anyhow::Result<Self> {
Ok(cbor::deserialize_params(ser)?)
}
}

/// State of an actor including a lock to support atomic executions.
#[derive(Serialize_tuple, Deserialize_tuple)]
pub struct LockableState<T>
where
T: Serialize + DeserializeOwned + MergeableState<T>,
{
lock: bool,
state: T,
}
impl<T: Serialize + DeserializeOwned + MergeableState<T>> Cbor for LockableState<T> {}
32 changes: 16 additions & 16 deletions actors/hierarchical_sca/src/cross.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ pub struct StorableMsg {
}
impl Cbor for StorableMsg {}

impl Default for StorableMsg {
fn default() -> Self {
Self {
from: Address::new_id(0),
to: Address::new_id(0),
method: 0,
params: RawBytes::default(),
value: TokenAmount::from(0),
nonce: 0,
}
}
}

#[derive(PartialEq, Eq)]
pub enum HCMsgType {
Unknown = 0,
Expand All @@ -57,14 +70,7 @@ impl StorableMsg {
sig_addr,
)?;
let from = Address::new_hierarchical(sub_id, &BURNT_FUNDS_ACTOR_ADDR)?;
Ok(Self {
from: from,
to: to,
method: METHOD_SEND,
params: RawBytes::default(),
value: value,
nonce: nonce,
})
Ok(Self { from, to, method: METHOD_SEND, params: RawBytes::default(), value, nonce })
}

pub fn new_fund_msg(
Expand All @@ -80,14 +86,8 @@ impl StorableMsg {
sig_addr,
)?;
let to = Address::new_hierarchical(sub_id, sig_addr)?;
Ok(Self {
from: from,
to: to,
method: METHOD_SEND,
params: RawBytes::default(),
value: value,
nonce: 0,
})
// the nonce and the rest of message fields are set when the message is committed.
Ok(Self { from, to, method: METHOD_SEND, value, ..Default::default() })
}

pub fn hc_type(&self) -> anyhow::Result<HCMsgType> {
Expand Down
Loading