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

Commit

Permalink
Merge pull request #15 from adlrocha/taddress
Browse files Browse the repository at this point in the history
TAddress<Hierarchical<_>>
  • Loading branch information
adlrocha authored Jul 26, 2022
2 parents 1f0c0b0 + 91cb133 commit 12d67b9
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 37 deletions.
58 changes: 36 additions & 22 deletions actors/hierarchical_sca/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ use fvm_ipld_blockstore::{Blockstore, MemoryBlockstore};
use fvm_ipld_encoding::repr::*;
use fvm_ipld_encoding::{tuple::*, Cbor};
use fvm_shared::address::{Address, SubnetID};
use std::convert::TryFrom;
use std::{collections::HashMap, str::FromStr};

use crate::taddress::{Hierarchical, TAddress, ID};
use crate::tcid::{TAmt, TCid, THamt, TLink};
use crate::{atomic, StorableMsg};

Expand Down Expand Up @@ -43,6 +45,9 @@ impl Cbor for AtomicExec {}
/// we use their string format (thus this type).
type StringifiedAddr = String;

/// A hierarchical address resolved to an ID.
pub type HierarchicalId = TAddress<Hierarchical<ID>>;

impl AtomicExec {
pub fn new(params: AtomicExecParams) -> Self {
AtomicExec {
Expand Down Expand Up @@ -84,15 +89,22 @@ pub struct SubmitExecParams {
}
impl Cbor for SubmitExecParams {}

/// Parameters to uniquely initiate an atomic execution.
#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)]
pub struct AtomicExecParamsRaw {
pub msgs: Vec<StorableMsg>,
pub inputs: HashMap<StringifiedAddr, LockedStateInfo>,
}
impl Cbor for AtomicExecParamsRaw {}

/// Parameters to uniquely identify and describe an atomic execution.
///
/// The unique ID of an execution is determined by the CID of its parameters.
#[derive(Clone, Debug, PartialEq, Eq, Serialize_tuple, Deserialize_tuple)]
pub struct AtomicExecParams {
pub msgs: Vec<StorableMsg>,
pub inputs: HashMap<StringifiedAddr, LockedStateInfo>,
pub inputs: HashMap<HierarchicalId, LockedStateInfo>,
}
impl Cbor for AtomicExecParams {}

/// Output of the initialization of an atomic execution.
// FIXME: Can we probably return the CID directly without
Expand Down Expand Up @@ -134,18 +146,18 @@ impl AtomicExecParamsMeta {
}
}

impl AtomicExecParams {
impl AtomicExecParamsRaw {
/// translate input addresses into ID address in the current subnet.
/// The parameters of the atomic execution include non-ID addresses (i.e. keys)
/// and they need to be translated to their corresponding ID addresses in the
/// current subnet.
pub fn input_into_ids<BS, RT>(&mut self, rt: &mut RT) -> anyhow::Result<()>
pub fn input_into_ids<BS, RT>(self, rt: &mut RT) -> anyhow::Result<AtomicExecParams>
where
BS: Blockstore,
RT: Runtime<BS>,
{
let mut out = HashMap::<StringifiedAddr, LockedStateInfo>::new();
for (key, val) in self.inputs.iter() {
let mut out = HashMap::new();
for (key, val) in self.inputs.into_iter() {
let addr = Address::from_str(&key)?;
let sn = addr.subnet()?;
let addr = addr.raw_addr()?;
Expand All @@ -155,12 +167,14 @@ impl AtomicExecParams {
};
// Update with id_addr and subnet
let sn_addr = Address::new_hierarchical(&sn, &id_addr)?;
out.insert(sn_addr.to_string(), (*val).clone());
let addr = TAddress::try_from(sn_addr)?;
out.insert(addr, val);
}
self.inputs = out;
Ok(())
Ok(AtomicExecParams { msgs: self.msgs, inputs: out })
}
}

impl AtomicExecParams {
/// Computes the CID for the atomic execution parameters. The input parameters
/// for the execution determines the CID used to uniquely identify the execution.
pub fn cid(&self) -> anyhow::Result<Cid> {
Expand All @@ -173,8 +187,7 @@ impl AtomicExecParams {

for (k, v) in self.inputs.iter() {
meta.inputs_cid.update(&store, |input_map| {
let addr = Address::from_str(k)?;
input_map.set(addr.to_bytes().into(), v.clone()).map_err(|e| {
input_map.set(k.to_bytes().into(), v.clone()).map_err(|e| {
e.downcast_wrap(format!("failed to set input map to compute exec cid"))
})?;
Ok(())
Expand All @@ -190,19 +203,17 @@ impl AtomicExecParams {
/// Computes the common parent for the inputs of the atomic execution.
pub fn is_common_parent(
curr: &SubnetID,
inputs: &HashMap<StringifiedAddr, LockedStateInfo>,
inputs: &HashMap<HierarchicalId, LockedStateInfo>,
) -> anyhow::Result<bool> {
if inputs.len() == 0 {
return Err(anyhow!("wrong length! no inputs in hashmap"));
}

let ks: Vec<&String> = inputs.keys().collect();
let addr = Address::from_str(ks[0].as_str())?;
let mut cp = addr.subnet()?;
let ks: Vec<_> = inputs.keys().collect();
let mut cp = ks[0].subnet();

for k in ks.iter() {
let addr = Address::from_str(k.as_str())?;
let sn = addr.subnet()?;
let sn = k.subnet();
cp = match cp.common_parent(&sn) {
Some((_, s)) => s,
None => continue,
Expand All @@ -215,16 +226,19 @@ pub fn is_common_parent(
/// Check if the address is involved in the execution
pub fn is_addr_in_exec(
caller: &Address,
inputs: &HashMap<StringifiedAddr, LockedStateInfo>,
inputs: &HashMap<HierarchicalId, LockedStateInfo>,
) -> anyhow::Result<bool> {
let ks: Vec<&String> = inputs.keys().collect();
let ks: Vec<_> = inputs.clone().into_keys().collect();

for k in ks.iter() {
let addr = Address::from_str(k.as_str())?;
let addr = addr.raw_addr()?;
let addr = k.raw_addr();

// XXX: Throwing away the typing information so we can compare with `caller`.
// Ideally we should receive a `TAddress<ID>` so we know these are okay to compare.
let addr = addr.addr();

// if the raw address is equal to caller
if caller == &addr {
if caller == addr {
return Ok(true);
}
}
Expand Down
8 changes: 4 additions & 4 deletions actors/hierarchical_sca/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use cid::Cid;
use exec::{
is_addr_in_exec, is_common_parent, AtomicExec, AtomicExecParams, ExecStatus, LockedOutput,
is_addr_in_exec, is_common_parent, AtomicExec, AtomicExecParamsRaw, ExecStatus, LockedOutput,
SubmitExecParams, SubmitOutput,
};
use fil_actors_runtime::runtime::{ActorCode, Runtime};
Expand Down Expand Up @@ -38,6 +38,7 @@ pub mod exec;
pub mod ext;
mod state;
pub mod subnet;
pub mod taddress;
pub mod tcid;
mod types;

Expand Down Expand Up @@ -641,7 +642,7 @@ impl Actor {
/// and that its semantics and inputs are correct.
fn init_atomic_exec<BS, RT>(
rt: &mut RT,
params: AtomicExecParams,
params: AtomicExecParamsRaw,
) -> Result<LockedOutput, ActorError>
where
BS: Blockstore,
Expand All @@ -650,8 +651,7 @@ impl Actor {
rt.validate_immediate_caller_type(CALLER_TYPES_SIGNABLE.iter())?;

// translate inputs into id addresses for the subnet.
let mut params = params;
params.input_into_ids(rt).map_err(|e| {
let params = params.input_into_ids(rt).map_err(|e| {
e.downcast_default(
ExitCode::USR_ILLEGAL_ARGUMENT,
"error translating execution input addresses to IDs",
Expand Down
5 changes: 2 additions & 3 deletions actors/hierarchical_sca/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,11 +532,10 @@ impl State {
curr_epoch: ChainEpoch,
abort: bool,
) -> anyhow::Result<()> {
let ks: Vec<String> = exec.params().inputs.clone().into_keys().collect();
let ks: Vec<_> = exec.params().inputs.clone().into_keys().collect();
let mut visited = HashMap::<SubnetID, bool>::new();
for k in ks.iter() {
let addr = Address::from_str(k.as_str())?;
let sn = addr.subnet()?;
let sn = k.subnet();
match visited.get(&sn) {
Some(_) => {
continue;
Expand Down
138 changes: 138 additions & 0 deletions actors/hierarchical_sca/src/taddress.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use std::{convert::TryFrom, fmt::Display, marker::PhantomData};

use serde::de::Error;

use fvm_ipld_encoding::Cbor;
use fvm_shared::address::{Address, Payload, SubnetID};

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct TAddress<T> {
addr: Address,
_phantom: PhantomData<T>,
}

impl<T> TAddress<T> {
pub fn to_bytes(&self) -> Vec<u8> {
self.addr.to_bytes()
}

/// The untyped `Address` representation.
pub fn addr(&self) -> &Address {
&self.addr
}
}

trait RawAddress {
fn is_compatible(addr: Address) -> bool;
}

/// Define a unit struct for address types that can be used as a generic parameter.
macro_rules! raw_address_types {
($($typ:ident),+) => {
$(
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct $typ;

impl RawAddress for $typ {
fn is_compatible(addr: Address) -> bool {
match addr.payload() {
Payload::$typ(_) => true,
_ => false
}
}
}
)*
};
}

// Based on `Payload` variants.
raw_address_types! {
ID,
Secp256k1,
Actor,
BLS
}

/// For `Hierarchical` address type that doesn't say what kind it wraps.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct AnyRawAddr;

impl RawAddress for AnyRawAddr {
fn is_compatible(addr: Address) -> bool {
match addr.payload() {
Payload::Hierarchical(_) => false,
_ => true,
}
}
}

/// A `Hierarchical` is generic in what it wraps, which could be any raw address type, but *not* another `Hierarchical`.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Hierarchical<A> {
_phantom: PhantomData<A>,
}

impl<T> Into<Address> for TAddress<T> {
fn into(self) -> Address {
self.addr
}
}

impl<A: RawAddress> TryFrom<Address> for TAddress<Hierarchical<A>> {
type Error = fvm_shared::address::Error;

fn try_from(value: Address) -> Result<Self, Self::Error> {
let sub = value.subnet()?;
let raw = value.raw_addr()?;
if !A::is_compatible(raw) {
return Err(fvm_shared::address::Error::InvalidPayload);
}
let addr = Address::new_hierarchical(&sub, &raw)?;
Ok(Self { addr, _phantom: PhantomData })
}
}

impl<A> TAddress<Hierarchical<A>> {
pub fn subnet(&self) -> SubnetID {
self.addr.subnet().unwrap()
}

pub fn raw_addr(&self) -> TAddress<A> {
TAddress { addr: self.addr.raw_addr().unwrap(), _phantom: PhantomData }
}
}

/// Serializes exactly as its underlying `Address`.
impl<T> serde::Serialize for TAddress<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.addr.serialize(serializer)
}
}

/// Deserializes exactly as its underlying `Address` but might be rejected if it's not the expected type.
impl<'d, T> serde::Deserialize<'d> for TAddress<T>
where
Self: TryFrom<Address>,
<Self as TryFrom<Address>>::Error: Display,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'d>,
{
let raw = Address::deserialize(deserializer)?;
match Self::try_from(raw) {
Ok(addr) => Ok(addr),
Err(e) => Err(D::Error::custom(format!("wrong address type: {}", e))),
}
}
}

impl<T> Cbor for TAddress<T>
where
Self: TryFrom<Address>,
<Self as TryFrom<Address>>::Error: Display,
{
}
3 changes: 1 addition & 2 deletions actors/hierarchical_sca/tests/harness/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::borrow::Borrow;
use std::str::FromStr;

use anyhow::anyhow;
use cid::multihash::Code;
Expand Down Expand Up @@ -698,7 +697,7 @@ impl Harness {
panic!("execution should have been cleaned when finalized");
}
for (k, _) in exec_params.inputs.iter() {
let sn = Address::from_str(k).unwrap().subnet().unwrap();
let sn = k.subnet();
let sub = st.get_subnet(rt.store(), &sn).unwrap().unwrap();
let crossmsgs = sub.top_down_msgs.load(rt.store()).unwrap();
let msg = get_topdown_msg(&crossmsgs, 0).unwrap().unwrap();
Expand Down
14 changes: 8 additions & 6 deletions actors/hierarchical_sca/tests/sca_actor_test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use cid::multihash::Code;
use cid::multihash::MultihashDigest;
use cid::Cid;
use fil_actor_hierarchical_sca::exec::HierarchicalId;
use fil_actors_runtime::runtime::Runtime;
use fil_actors_runtime::BURNT_FUNDS_ACTOR_ADDR;
use fvm_ipld_encoding::RawBytes;
Expand All @@ -12,6 +13,7 @@ use fvm_shared::clock::ChainEpoch;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::ExitCode;
use std::collections::HashMap;
use std::convert::TryInto;
use std::str::FromStr;

use fil_actor_hierarchical_sca::atomic::SerializedState;
Expand Down Expand Up @@ -863,15 +865,15 @@ fn gen_locked_state(
sn2: &SubnetID,
caller: &Address,
other: &Address,
) -> HashMap<String, LockedStateInfo> {
) -> HashMap<HierarchicalId, LockedStateInfo> {
let lock_cid1 = Cid::new_v1(DAG_CBOR, Code::Blake2b256.digest(b"test1"));
let lock_cid2 = Cid::new_v1(DAG_CBOR, Code::Blake2b256.digest(b"test2"));
let addr1 = Address::new_hierarchical(sn1, caller).unwrap();
let addr2 = Address::new_hierarchical(sn2, other).unwrap();
let addr1 = Address::new_hierarchical(sn1, caller).unwrap().try_into().unwrap();
let addr2 = Address::new_hierarchical(sn2, other).unwrap().try_into().unwrap();
let act1 = Address::new_id(900);
let act2 = Address::new_id(901);
let mut m = HashMap::<String, LockedStateInfo>::new();
m.insert(addr1.to_string(), LockedStateInfo { cid: lock_cid1, actor: act1 });
m.insert(addr2.to_string(), LockedStateInfo { cid: lock_cid2, actor: act2 });
let mut m = HashMap::new();
m.insert(addr1, LockedStateInfo { cid: lock_cid1, actor: act1 });
m.insert(addr2, LockedStateInfo { cid: lock_cid2, actor: act2 });
m
}

0 comments on commit 12d67b9

Please sign in to comment.