Skip to content

Commit

Permalink
Initial implementation of new, more convenient Map wrapping HAMT (#1349)
Browse files Browse the repository at this point in the history
* Initial implementation of new, more convenient Map wrapping HAMT

* MapKey trait
  • Loading branch information
anorth authored Aug 8, 2023
1 parent f9b5760 commit 8882852
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 86 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 15 additions & 32 deletions actors/init/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,27 @@
// SPDX-License-Identifier: Apache-2.0, MIT

use cid::Cid;
use fil_actors_runtime::{
actor_error, make_empty_map, make_map_with_root_and_bitwidth, ActorError, AsActorError,
FIRST_NON_SINGLETON_ADDR,
};
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::tuple::*;
use fvm_shared::address::{Address, Protocol};
use fvm_shared::error::ExitCode;
use fvm_shared::{ActorID, HAMT_BIT_WIDTH};
use fvm_shared::ActorID;

use fil_actors_runtime::{actor_error, ActorError, Map2, DEFAULT_CONF, FIRST_NON_SINGLETON_ADDR};

/// State is reponsible for creating
#[derive(Serialize_tuple, Deserialize_tuple, Clone, Debug)]
pub struct State {
/// HAMT[Address]ActorID
pub address_map: Cid,
pub next_id: ActorID,
pub network_name: String,
}

pub type AddressMap<BS> = Map2<BS, Address, ActorID>;

impl State {
pub fn new<BS: Blockstore>(store: &BS, network_name: String) -> Result<Self, ActorError> {
let empty_map = make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH)
.flush()
.context_code(ExitCode::USR_ILLEGAL_STATE, "failed to create empty map")?;
Ok(Self { address_map: empty_map, next_id: FIRST_NON_SINGLETON_ADDR, network_name })
let empty = AddressMap::flush_empty(store, DEFAULT_CONF)?;
Ok(Self { address_map: empty, next_id: FIRST_NON_SINGLETON_ADDR, network_name })
}

/// Maps argument addresses to to a new or existing actor ID.
Expand All @@ -40,22 +37,16 @@ impl State {
robust_addr: &Address,
delegated_addr: Option<&Address>,
) -> Result<(ActorID, bool), ActorError> {
let mut map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH)
.context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load address map")?;
let mut map = AddressMap::load(store, &self.address_map, DEFAULT_CONF, "addresses")?;
let (id, existing) = if let Some(delegated_addr) = delegated_addr {
// If there's a delegated address, either recall the already-mapped actor ID or
// create and map a new one.
let delegated_key = delegated_addr.to_bytes().into();
if let Some(existing_id) = map
.get(&delegated_key)
.context_code(ExitCode::USR_ILLEGAL_STATE, "failed to lookup delegated address")?
{
if let Some(existing_id) = map.get(delegated_addr)? {
(*existing_id, true)
} else {
let new_id = self.next_id;
self.next_id += 1;
map.set(delegated_key, new_id)
.context_code(ExitCode::USR_ILLEGAL_STATE, "failed to map delegated address")?;
map.set(delegated_addr, new_id)?;
(new_id, false)
}
} else {
Expand All @@ -66,18 +57,15 @@ impl State {
};

// Map the robust address to the ID, failing if it's already mapped to anything.
let is_new = map
.set_if_absent(robust_addr.to_bytes().into(), id)
.context_code(ExitCode::USR_ILLEGAL_STATE, "failed to map robust address")?;
let is_new = map.set_if_absent(robust_addr, id)?;
if !is_new {
return Err(actor_error!(
forbidden,
"robust address {} is already allocated in the address map",
robust_addr
));
}
self.address_map =
map.flush().context_code(ExitCode::USR_ILLEGAL_STATE, "failed to store address map")?;
self.address_map = map.flush()?;
Ok((id, existing))
}

Expand All @@ -99,13 +87,8 @@ impl State {
if addr.protocol() == Protocol::ID {
return Ok(Some(*addr));
}

let map = make_map_with_root_and_bitwidth(&self.address_map, store, HAMT_BIT_WIDTH)
.context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load address map")?;

let found = map
.get(&addr.to_bytes())
.context_code(ExitCode::USR_ILLEGAL_STATE, "failed to get address entry")?;
let map = AddressMap::load(store, &self.address_map, DEFAULT_CONF, "addresses")?;
let found = map.get(addr)?;
Ok(found.copied().map(Address::new_id))
}
}
31 changes: 13 additions & 18 deletions actors/init/src/testing.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use std::collections::HashMap;

use fil_actors_runtime::{Map, MessageAccumulator, FIRST_NON_SINGLETON_ADDR};
use fvm_ipld_blockstore::Blockstore;
use fvm_shared::{
address::{Address, Protocol},
ActorID,
};

use fil_actors_runtime::{MessageAccumulator, DEFAULT_CONF, FIRST_NON_SINGLETON_ADDR};

use crate::state::AddressMap;
use crate::State;

pub struct StateSummary {
Expand All @@ -31,44 +33,37 @@ pub fn check_state_invariants<BS: Blockstore>(

let mut stable_address_by_id = HashMap::<ActorID, Address>::new();
let mut delegated_address_by_id = HashMap::<ActorID, Address>::new();
match Map::<_, ActorID>::load(&state.address_map, store) {

match AddressMap::load(store, &state.address_map, DEFAULT_CONF, "addresses") {
Ok(address_map) => {
let ret = address_map.for_each(|key, actor_id| {
let key_address = Address::from_bytes(key)?;

acc.require(
key_address.protocol() != Protocol::ID,
format!("key {key_address} is an ID address"),
);
acc.require(key.protocol() != Protocol::ID, format!("key {key} is an ID address"));
acc.require(
actor_id >= &FIRST_NON_SINGLETON_ADDR,
format!("unexpected singleton ID value {actor_id}"),
);

match key_address.protocol() {
match key.protocol() {
Protocol::ID => {
acc.add(format!("key {key_address} is an ID address"));
acc.add(format!("key {key} is an ID address"));
}
Protocol::Delegated => {
if let Some(duplicate) =
delegated_address_by_id.insert(*actor_id, key_address)
{
if let Some(duplicate) = delegated_address_by_id.insert(*actor_id, key) {
acc.add(format!(
"duplicate mapping to ID {actor_id}: {key_address} {duplicate}"
"duplicate mapping to ID {actor_id}: {key} {duplicate}"
));
}
}
_ => {
if let Some(duplicate) = stable_address_by_id.insert(*actor_id, key_address)
{
if let Some(duplicate) = stable_address_by_id.insert(*actor_id, key) {
acc.add(format!(
"duplicate mapping to ID {actor_id}: {key_address} {duplicate}"
"duplicate mapping to ID {actor_id}: {key} {duplicate}"
));
}
}
}

init_summary.ids_by_address.insert(key_address, *actor_id);
init_summary.ids_by_address.insert(key, *actor_id);

Ok(())
});
Expand Down
41 changes: 21 additions & 20 deletions runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,41 @@ edition = "2021"
repository = "https://github.com/filecoin-project/builtin-actors"

[dependencies]
fvm_ipld_hamt = { workspace = true }
fvm_ipld_amt = { workspace = true }
fvm_shared = { workspace = true }
num = { workspace = true }
num-traits = { workspace = true }
num-derive = { workspace = true }
serde = { workspace = true }
lazy_static = { workspace = true, optional = true }
unsigned-varint = { workspace = true }
anyhow = { workspace = true }
byteorder = { workspace = true }
castaway = { workspace = true }
cid = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
anyhow = { workspace = true }
fvm_sdk = { workspace = true, optional = true }
fvm_ipld_amt = { workspace = true }
fvm_ipld_bitfield = { workspace = true }
fvm_ipld_blockstore = { workspace = true }
fvm_ipld_encoding = { workspace = true }
fvm_ipld_bitfield = { workspace = true }
multihash = { workspace = true }
serde_repr = { workspace = true }
regex = { workspace = true }
fvm_ipld_hamt = { workspace = true }
fvm_sdk = { workspace = true, optional = true }
fvm_shared = { workspace = true }
integer-encoding = { workspace = true }
itertools = { workspace = true }
lazy_static = { workspace = true, optional = true }
log = { workspace = true }
multihash = { workspace = true }
num = { workspace = true }
num-derive = { workspace = true }
num-traits = { workspace = true }
paste = { workspace = true }
castaway = { workspace = true }
regex = { workspace = true }
serde = { workspace = true }
serde_repr = { workspace = true }
thiserror = { workspace = true }
unsigned-varint = { workspace = true }

# A fake-proofs dependency but... we can't select on that feature here because we enable it from
# build.rs.
sha2 = { workspace = true }

# test_util
rand = { workspace = true, optional = true }
hex = { workspace = true, optional = true }
blake2b_simd = { workspace = true, optional = true }
hex = { workspace = true, optional = true }
pretty_env_logger = { workspace = true, optional = true }
rand = { workspace = true, optional = true }

[dependencies.libsecp256k1]
workspace = true
Expand Down
25 changes: 9 additions & 16 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,36 @@
// Copyright 2019-2022 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use builtin::HAMT_BIT_WIDTH;
use cid::Cid;
use fvm_ipld_amt::Amt;
use fvm_ipld_blockstore::Blockstore;
#[cfg(not(feature = "fil-actor"))]
use fvm_ipld_hamt::Sha256;
use fvm_ipld_hamt::{BytesKey, Error as HamtError, Hamt};
use fvm_shared::address::Address;
use fvm_shared::bigint::BigInt;
pub use fvm_shared::BLOCKS_PER_EPOCH as EXPECTED_LEADERS_PER_EPOCH;
use serde::de::DeserializeOwned;
use serde::Serialize;
use unsigned_varint::decode::Error as UVarintError;
pub use {fvm_ipld_amt, fvm_ipld_hamt};

pub use self::actor_error::*;
pub use self::builtin::*;
pub use self::util::*;
use crate::runtime::Runtime;
use builtin::HAMT_BIT_WIDTH;
pub use dispatch::{dispatch, dispatch_default};
pub use {fvm_ipld_amt, fvm_ipld_hamt};

#[cfg(feature = "fil-actor")]
use crate::runtime::hash_algorithm::FvmHashSha256;
use crate::runtime::Runtime;

#[cfg(not(feature = "fil-actor"))]
use fvm_ipld_hamt::Sha256;
pub use self::actor_error::*;
pub use self::builtin::*;
pub use self::util::*;

pub mod actor_error;
pub mod builtin;
pub mod runtime;
pub mod util;

mod dispatch;
pub use dispatch::{dispatch, dispatch_default};
#[cfg(feature = "test_utils")]
pub mod test_utils;

Expand Down Expand Up @@ -112,12 +111,6 @@ pub trait Keyer {
fn key(&self) -> BytesKey;
}

impl Keyer for Address {
fn key(&self) -> BytesKey {
self.to_bytes().into()
}
}

impl Keyer for u64 {
fn key(&self) -> BytesKey {
u64_key(*self)
Expand Down
Loading

0 comments on commit 8882852

Please sign in to comment.