Skip to content

Commit

Permalink
Implement the get_tipset_cid
Browse files Browse the repository at this point in the history
  • Loading branch information
fridrik01 committed Jan 23, 2024
1 parent 18de5a0 commit 0e38cc8
Show file tree
Hide file tree
Showing 26 changed files with 186 additions and 71 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion fendermint/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ docker-build: docker-deps $(FENDERMINT_CODE)
# Build a bundle CAR; this is so we don't have to have a project reference,
# which means we are not tied to the release cycle of both FVM _and_ actors;
# so long as they work together.
actor-bundle: $(BUILTIN_ACTORS_BUNDLE)
actor-bundle: $(BUILTIN_ACTORS_BUNDLE) $(ACTORS_BUNDLE)

compile-abi: $(IPC_ACTORS_GEN)

Expand Down
2 changes: 1 addition & 1 deletion fendermint/actors/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ fn main() -> Result<(), Box<dyn Error>> {
.arg("--target=wasm32-unknown-unknown")
.arg("--profile=wasm")
.arg("--features=fil-actor")
.arg("--manifest-path=".to_owned() + manifest_path.to_str().unwrap())
.arg(format!("--manifest-path={}", manifest_path.display()))
.stdout(Stdio::piped())
.stderr(Stdio::piped())
// We are supposed to only generate artifacts under OUT_DIR,
Expand Down
2 changes: 2 additions & 0 deletions fendermint/actors/chainmetadata/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ cid = { workspace = true, default-features = false }
fil_actors_runtime = { workspace = true, features = ["fil-actor"] }
fvm_shared = { workspace = true }
fvm_ipld_encoding = { workspace = true }
fvm_ipld_blockstore = { workspace = true }
num-derive = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_tuple = { workspace = true }
num-traits = { workspace = true }
frc42_dispatch = { workspace = true }
anyhow = { workspace = true }

[features]
fil-actor = []
18 changes: 5 additions & 13 deletions fendermint/actors/chainmetadata/src/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use fil_actors_runtime::Array;
use fvm_shared::clock::ChainEpoch;
use fvm_shared::error::ExitCode;

use crate::shared::BLOCKHASHES_AMT_BITWIDTH;
use crate::{ConstructorParams, Method, PushBlockParams, State};

#[cfg(feature = "fil-actor")]
Expand All @@ -26,17 +25,9 @@ impl Actor {
fn constructor(rt: &impl Runtime, params: ConstructorParams) -> Result<(), ActorError> {
rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?;

let empty_arr_cid =
Array::<(), _>::new_with_bit_width(rt.store(), BLOCKHASHES_AMT_BITWIDTH)
.flush()
.map_err(|e| {
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to create empty AMT")
})?;

let state = State {
blockhashes: empty_arr_cid,
lookback_len: params.lookback_len,
};
let state = State::new(rt.store(), params.lookback_len).map_err(|e| {
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to create empty AMT")
})?;

rt.create(&state)?;

Expand All @@ -60,7 +51,8 @@ impl Actor {
.set(params.epoch as u64, params.block.to_string())
.unwrap();

// remove the oldest block if the AMT is full
// remove the oldest block if the AMT is full (note that this assume the
// for_each_while iterates in order, which it seems to do)
if blockhashes.count() > st.lookback_len {
let mut first_idx = 0;
blockhashes
Expand Down
27 changes: 24 additions & 3 deletions fendermint/actors/chainmetadata/src/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
// SPDX-License-Identifier: Apache-2.0, MIT

use cid::Cid;
use fil_actors_runtime::Array;
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::tuple::{Deserialize_tuple, Serialize_tuple};
use fvm_shared::{clock::ChainEpoch, METHOD_CONSTRUCTOR};
use num_derive::FromPrimitive;

// The state is a stores `blockhashes` in an AMT containing the blockhashes of the
// last `lookback_len` epochs
// The state stores the blockhashes of the last `lookback_len` epochs
#[derive(Serialize_tuple, Deserialize_tuple)]
pub struct State {
// the AMT root cid of blockhashes
Expand All @@ -17,6 +18,26 @@ pub struct State {
pub lookback_len: u64,
}

impl State {
pub fn new<BS: Blockstore>(store: &BS, lookback_len: u64) -> anyhow::Result<Self> {
let empty_blockhashes_cid =
match Array::<(), _>::new_with_bit_width(store, BLOCKHASHES_AMT_BITWIDTH).flush() {
Ok(cid) => cid,
Err(e) => {
return Err(anyhow::anyhow!(
"chainmetadata actor failed to create empty Amt: {}",
e
))
}
};

Ok(Self {
blockhashes: empty_blockhashes_cid,
lookback_len,
})
}
}

// the default lookback length is 256 epochs
pub const DEFAULT_LOOKBACK_LEN: u64 = 256;

Expand All @@ -38,7 +59,7 @@ pub struct PushBlockParams {
#[repr(u64)]
pub enum Method {
Constructor = METHOD_CONSTRUCTOR,
PushBlock = 2,
PushBlock = frc42_dispatch::method_hash!("PushBlock"),
LookbackLen = frc42_dispatch::method_hash!("LookbackLen"),
BlockCID = frc42_dispatch::method_hash!("BlockCID"),
}
2 changes: 1 addition & 1 deletion fendermint/app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub struct AppConfig<S: KVStore> {
#[derive(Clone)]
pub struct App<DB, SS, S, I>
where
SS: Blockstore + 'static,
SS: Blockstore + Clone + 'static,
S: KVStore,
{
/// Database backing all key-value operations.
Expand Down
2 changes: 1 addition & 1 deletion fendermint/app/src/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::sync::Arc;
/// Queries the LATEST COMMITTED parent finality from the storage
pub struct AppParentFinalityQuery<DB, SS, S, I>
where
SS: Blockstore + 'static,
SS: Blockstore + Clone + 'static,
S: KVStore,
{
/// The app to get state
Expand Down
2 changes: 1 addition & 1 deletion fendermint/testing/contract-test/src/ipc/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl<DB> RegistryCaller<DB> {
}
}

impl<DB: Blockstore> RegistryCaller<DB> {
impl<DB: Blockstore + Clone> RegistryCaller<DB> {
/// Create a new instance of the built-in subnet implemetation.
///
/// Returns the address of the deployed contract.
Expand Down
2 changes: 1 addition & 1 deletion fendermint/testing/contract-test/src/ipc/subnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl<DB> SubnetCaller<DB> {

type TryCallResult<T> = anyhow::Result<ContractResult<T, SubnetActorErrors>>;

impl<DB: Blockstore> SubnetCaller<DB> {
impl<DB: Blockstore + Clone> SubnetCaller<DB> {
/// Join a subnet as a validator.
pub fn join(
&self,
Expand Down
2 changes: 1 addition & 1 deletion fendermint/testing/contract-test/tests/staking/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ fn choose_account<'a>(
Ok(a)
}

fn get_actor_balance<DB: Blockstore>(
fn get_actor_balance<DB: Blockstore + Clone>(
exec_state: &mut FvmExecState<DB>,
addr: EthAddress,
) -> TokenAmount {
Expand Down
2 changes: 1 addition & 1 deletion fendermint/vm/interpreter/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ where
#[async_trait]
impl<I, DB> ExecInterpreter for ChainMessageInterpreter<I, DB>
where
DB: Blockstore + Clone + 'static + Send + Sync,
DB: Blockstore + Clone + 'static + Send + Sync + Clone,
I: ExecInterpreter<
Message = VerifiableMessage,
DeliverOutput = SignedMessageApplyRes,
Expand Down
2 changes: 1 addition & 1 deletion fendermint/vm/interpreter/src/fvm/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub struct FvmCheckRet {
#[async_trait]
impl<DB, TC> CheckInterpreter for FvmMessageInterpreter<DB, TC>
where
DB: Blockstore + 'static + Send + Sync,
DB: Blockstore + 'static + Send + Sync + Clone,
TC: Send + Sync + 'static,
{
// We simulate the full pending state so that client can call methods on
Expand Down
12 changes: 6 additions & 6 deletions fendermint/vm/interpreter/src/fvm/checkpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub fn maybe_create_checkpoint<DB>(
state: &mut FvmExecState<DB>,
) -> anyhow::Result<Option<(checkpoint::BottomUpCheckpoint, PowerUpdates)>>
where
DB: Blockstore + Sync + Send + 'static,
DB: Blockstore + Sync + Send + Clone + 'static,
{
// Epoch transitions for checkpointing.
let height: tendermint::block::Height = state
Expand Down Expand Up @@ -164,7 +164,7 @@ pub fn unsigned_checkpoints<DB>(
validator_key: PublicKey,
) -> anyhow::Result<Vec<getter::BottomUpCheckpoint>>
where
DB: Blockstore + Send + Sync + 'static,
DB: Blockstore + Send + Sync + Clone + 'static,
{
let mut unsigned_checkpoints = Vec::new();
let validator_addr = EthAddress::from(validator_key);
Expand Down Expand Up @@ -196,7 +196,7 @@ pub async fn broadcast_incomplete_signatures<C, DB>(
) -> anyhow::Result<()>
where
C: Client + Clone + Send + Sync + 'static,
DB: Blockstore + Send + Sync + 'static,
DB: Blockstore + Send + Sync + Clone + 'static,
{
// Make sure that these had time to be added to the ledger.
if let Some(highest) = incomplete_checkpoints
Expand Down Expand Up @@ -268,7 +268,7 @@ pub async fn broadcast_signature<C, DB>(
) -> anyhow::Result<()>
where
C: Client + Clone + Send + Sync + 'static,
DB: Blockstore + Send + Sync + 'static,
DB: Blockstore + Send + Sync + Clone + 'static,
{
let calldata = gateway
.add_checkpoint_signature_calldata(checkpoint, &power_table.0, validator, secret_key)
Expand All @@ -291,7 +291,7 @@ fn should_create_checkpoint<DB>(
height: Height,
) -> anyhow::Result<Option<checkpoint::SubnetID>>
where
DB: Blockstore,
DB: Blockstore + Clone,
{
if gateway.enabled(state)? {
let id = gateway.subnet_id(state)?;
Expand Down Expand Up @@ -336,7 +336,7 @@ fn ipc_power_table<DB>(
state: &mut FvmExecState<DB>,
) -> anyhow::Result<(ConfigurationNumber, PowerTable)>
where
DB: Blockstore + Sync + Send + 'static,
DB: Blockstore + Sync + Send + Clone + 'static,
{
let membership = gateway
.current_validator_set(state)
Expand Down
118 changes: 108 additions & 10 deletions fendermint/vm/interpreter/src/fvm/externs.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
// Copyright 2022-2024 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
use anyhow::anyhow;
use cid::Cid;
use fvm::externs::{Chain, Consensus, Externs, Rand};
use fendermint_actors::CHAINMETADATA_ACTOR_ID;
use fil_actors_runtime::Array;
use fvm::{
externs::{Chain, Consensus, Externs, Rand},
state_tree::StateTree,
};
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::CborStore;
use fvm_shared::clock::ChainEpoch;
use std::str::FromStr;

/// Dummy externs - these are related to Expected Consensus,
/// which I believe we have nothing to do with.
pub struct FendermintExterns;
use super::store::ReadOnlyBlockstore;

impl Rand for FendermintExterns {
pub struct FendermintExterns<DB>
where
DB: Blockstore + 'static,
{
blockstore: DB,
state_root: Cid,
}

impl<DB> FendermintExterns<DB>
where
DB: Blockstore + 'static,
{
pub fn new(blockstore: DB, state_root: Cid) -> Self {
Self {
blockstore,
state_root,
}
}
}

impl<DB> Rand for FendermintExterns<DB>
where
DB: Blockstore + 'static,
{
fn get_chain_randomness(&self, _round: ChainEpoch) -> anyhow::Result<[u8; 32]> {
todo!("might need randomness")
}
Expand All @@ -18,7 +48,10 @@ impl Rand for FendermintExterns {
}
}

impl Consensus for FendermintExterns {
impl<DB> Consensus for FendermintExterns<DB>
where
DB: Blockstore + 'static,
{
fn verify_consensus_fault(
&self,
_h1: &[u8],
Expand All @@ -29,10 +62,75 @@ impl Consensus for FendermintExterns {
}
}

impl Chain for FendermintExterns {
fn get_tipset_cid(&self, _epoch: ChainEpoch) -> anyhow::Result<Cid> {
unimplemented!("not expecting to use tipsets")
impl<DB> Chain for FendermintExterns<DB>
where
DB: Blockstore + Clone + 'static,
{
// for retreiving the tipset_cid, we load the chain metadata actor state
// at the given state_root and retrieve the blockhash for the given epoch
fn get_tipset_cid(&self, epoch: ChainEpoch) -> anyhow::Result<Cid> {
// create a read only state tree from the state root
let bstore = ReadOnlyBlockstore::new(&self.blockstore);
let state_tree = StateTree::new_from_root(&bstore, &self.state_root)?;

// get the chain metadata actor state cid
let actor_state_cid = match state_tree.get_actor(CHAINMETADATA_ACTOR_ID) {
Ok(Some(actor_state)) => actor_state.state,
Ok(None) => {
return Err(anyhow!(
"chain metadata actor id ({}) not found in state",
CHAINMETADATA_ACTOR_ID
));
}
Err(err) => {
return Err(anyhow!(
"failed to get chain metadata actor ({}) state, error: {}",
CHAINMETADATA_ACTOR_ID,
err
));
}
};

// get the chain metadata actor state from the blockstore
let actor_state: fendermint_actor_chainmetadata::State =
match state_tree.store().get_cbor(&actor_state_cid) {
Ok(Some(v)) => v,
Ok(None) => {
return Err(anyhow!(
"chain metadata actor ({}) state not found",
CHAINMETADATA_ACTOR_ID
));
}
Err(err) => {
return Err(anyhow!(
"failed to get chain metadata actor ({}) state, error: {}",
CHAINMETADATA_ACTOR_ID,
err
));
}
};

// load the blockhashe Array from the AMT root cid
let blockhashes = Array::load(&actor_state.blockhashes, &bstore)?;

// get the block hash at the given epoch
let blockhash: &String = match blockhashes.get(epoch as u64).unwrap() {
Some(v) => v,
None => {
return Ok(Cid::default());
}
};

// return the blockhash as a cid, or an error if the cid is invalid
match Cid::from_str(blockhash.as_str()) {
Ok(cid) => Ok(cid),
Err(_) => Err(anyhow!(
"failed to parse cid, blockhash: {}, epoch: {}",
blockhash,
epoch
)),
}
}
}

impl Externs for FendermintExterns {}
impl<DB> Externs for FendermintExterns<DB> where DB: Blockstore + Clone + 'static {}
Loading

0 comments on commit 0e38cc8

Please sign in to comment.