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

FM-252: Adopt next configuration at the end of the checkpoint period #272

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 13 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
.PHONY: all build test lint license check-fmt check-clippy actor-bundle

BUILTIN_ACTORS_TAG:=v11.0.0
BUILTIN_ACTORS_DIR:=../builtin-actors
BUILTIN_ACTORS_CODE:=$(shell find $(BUILTIN_ACTORS_DIR) -type f -name "*.rs" | grep -v target)
BUILTIN_ACTORS_BUNDLE:=$(shell pwd)/$(BUILTIN_ACTORS_DIR)/output/bundle.car

IPC_ACTORS_TAG:=origin/dev
IPC_ACTORS_DIR:=$(shell pwd)/../ipc-solidity-actors
IPC_ACTORS_CODE:=$(shell find $(IPC_ACTORS_DIR) -type f -name "*.sol")
IPC_ACTORS_BUILD:=fendermint/vm/ipc_actors/build.rs
IPC_ACTORS_OUT:=$(IPC_ACTORS_DIR)/out
IPC_ACTORS_ABI:=$(IPC_ACTORS_OUT)/.compile.abi

FENDERMINT_CODE:=$(shell find . -type f \( -name "*.rs" -o -name "Cargo.toml" \) | grep -v target)
BUILTIN_ACTORS_TAG ?= v11.0.0
BUILTIN_ACTORS_DIR := ../builtin-actors
BUILTIN_ACTORS_CODE := $(shell find $(BUILTIN_ACTORS_DIR) -type f -name "*.rs" | grep -v target)
BUILTIN_ACTORS_BUNDLE := $(shell pwd)/$(BUILTIN_ACTORS_DIR)/output/bundle.car

IPC_ACTORS_TAG ?= origin/dev
IPC_ACTORS_DIR := $(shell pwd)/../ipc-solidity-actors
IPC_ACTORS_CODE := $(shell find $(IPC_ACTORS_DIR) -type f -name "*.sol")
IPC_ACTORS_BUILD := fendermint/vm/ipc_actors/build.rs
IPC_ACTORS_OUT := $(IPC_ACTORS_DIR)/out
IPC_ACTORS_ABI := $(IPC_ACTORS_OUT)/.compile.abi

FENDERMINT_CODE := $(shell find . -type f \( -name "*.rs" -o -name "Cargo.toml" \) | grep -v target)

# Override PROFILE env var to choose between `local | ci`
PROFILE?=local
Expand Down
1 change: 1 addition & 0 deletions fendermint/vm/actor_interface/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ license.workspace = true
anyhow = { workspace = true }
ethers = { workspace = true }
lazy_static = { workspace = true }
libsecp256k1 = { workspace = true }
paste = { workspace = true }
serde = { workspace = true }
serde_tuple = { workspace = true }
Expand Down
49 changes: 48 additions & 1 deletion fendermint/vm/actor_interface/src/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,11 @@ pub mod gateway {
use ethers::contract::{EthAbiCodec, EthAbiType};
use ethers::core::types::{H160, U256};
use fendermint_vm_genesis::ipc::GatewayParams;
use fvm_shared::address::Payload;
use fendermint_vm_genesis::ValidatorKey;
use fendermint_vm_ipc_actors::gateway_manager_facet::Membership;
use fvm_shared::address::{Payload, Protocol};
use fvm_shared::econ::TokenAmount;
use libsecp256k1::PublicKey;

use crate::eam::{self, EthAddress};

Expand Down Expand Up @@ -126,6 +129,50 @@ pub mod gateway {
}
}

/// Tendermint compatible validator set.
pub struct ValidatorSet {
pub configuration_number: u64,
/// Validators with their voting power.
pub validators: Vec<(ValidatorKey, u64)>,
}

impl TryFrom<Membership> for ValidatorSet {
type Error = anyhow::Error;

fn try_from(value: Membership) -> Result<Self, Self::Error> {
let mut validators = Vec::new();
let max_weight = U256::from(u64::MAX);

for v in value.validators {
if v.addr.addr_type != Protocol::Secp256k1 as u8 {
// We can't use delegated addresses for validation.
// However, when we saved the membership, there is a `worker_addr` field
// that should have contained a public key type address. If not, ignore.
continue;
}

// Tendermint limits the weights to u64, so anything above that is wasted.
let w = if v.weight > max_weight {
u64::MAX
} else {
v.weight.as_u64()
};

let pk: PublicKey =
todo!("oops, we can't restore a public key from an f1 type Address :O");

let vk = ValidatorKey(pk);

validators.push((vk, w));
}

Ok(Self {
configuration_number: value.configuration_number,
validators,
})
}
}

fn tokens_to_u256(value: TokenAmount) -> U256 {
// XXX: Ignoring any error resulting from larger fee than what fits into U256. This is in genesis after all.
U256::from_big_endian(&value.atto().to_bytes_be().1)
Expand Down
46 changes: 30 additions & 16 deletions fendermint/vm/interpreter/src/fvm/exec.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// Copyright 2022-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

use anyhow::Context;
use async_trait::async_trait;
use std::collections::HashMap;
use tendermint_rpc::endpoint::validators;
use tendermint_rpc::Paging;

use fendermint_vm_actor_interface::{cron, system};
use fvm::executor::ApplyRet;
Expand All @@ -12,7 +15,10 @@ use tendermint_rpc::Client;

use crate::ExecInterpreter;

use super::{state::FvmExecState, FvmMessage, FvmMessageInterpreter};
use super::{
state::{ipc::Configuration, FvmExecState},
FvmMessage, FvmMessageInterpreter,
};

/// The return value extended with some things from the message that
/// might not be available to the caller, because of the message lookups
Expand Down Expand Up @@ -119,27 +125,35 @@ where
Ok((state, ret))
}

async fn end(&self, state: Self::State) -> anyhow::Result<(Self::State, Self::EndOutput)> {
// TODO #252: Epoch transitions for checkpointing.
// TODO #254: Construct checkpoint.
// TODO #255: Broadcast signature, if validating.

#[cfg(test)]
{
use anyhow::Context;
use tendermint_rpc::endpoint::validators;
use tendermint_rpc::Paging;

async fn end(&self, mut state: Self::State) -> anyhow::Result<(Self::State, Self::EndOutput)> {
// Epoch transitions for checkpointing.
if self.gateway.enabled(&mut state)? && !self.gateway.is_root(&mut state)? {
let height: tendermint::block::Height = state
.block_height()
.try_into()
.context("block height is not u64")?;

let validators: validators::Response =
self._client.validators(height, Paging::All).await?;
let checkpoint_period = self.gateway.bottom_up_check_period(&mut state)?;

if height.value() % checkpoint_period == 0 {
// TODO #254: Put the next configuration number for the parent to expect in the checkpoint.
let _next_config_number =
match self.gateway.activate_next_configuration(&mut state)? {
Configuration::Unchanged(cnr) => cnr,
Configuration::Activated(membership) => {
let current_validators: validators::Response =
self._client.validators(height, Paging::All).await?;

let _current_validators = current_validators.validators;

// TODO #252: Work out the difference between the current and the next validators.
membership.configuration_number
}
};

// This is the power table.
let _validators = validators.validators;
// TODO #254: Construct checkpoint.
// TODO #255: Broadcast signature, if validating.
}
}

Ok((state, ()))
Expand Down
5 changes: 5 additions & 0 deletions fendermint/vm/interpreter/src/fvm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub use genesis::FvmGenesisOutput;
use libsecp256k1::{PublicKey, SecretKey};
pub use query::FvmQueryRet;

use self::state::ipc::GatewayCaller;

pub type FvmMessage = fvm_shared::message::Message;

/// Interpreter working on already verified unsigned messages.
Expand All @@ -40,6 +42,8 @@ pub struct FvmMessageInterpreter<DB, C> {
/// Indicate whether transactions should be fully executed during the checks performed
/// when they are added to the mempool, or just the most basic ones are performed.
exec_in_check: bool,
/// Helper to call the IPC Gateway contract.
gateway: GatewayCaller,
_phantom_db: PhantomData<DB>,
}

Expand All @@ -61,6 +65,7 @@ impl<DB, C> FvmMessageInterpreter<DB, C> {
gas_overestimation_rate,
gas_search_step,
exec_in_check,
gateway: GatewayCaller::default(),
_phantom_db: PhantomData,
}
}
Expand Down
7 changes: 6 additions & 1 deletion fendermint/vm/interpreter/src/fvm/state/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use fvm::{
call_manager::DefaultCallManager,
engine::MultiEngine,
executor::{ApplyFailure, ApplyKind, ApplyRet, DefaultExecutor, Executor},
machine::{DefaultMachine, Machine, NetworkConfig},
machine::{DefaultMachine, Machine, Manifest, NetworkConfig},
state_tree::StateTree,
DefaultKernel,
};
Expand Down Expand Up @@ -138,6 +138,11 @@ where
self.executor.state_tree_mut()
}

/// Built-in actor manifest to inspect code CIDs.
pub fn builtin_actors(&self) -> &Manifest {
self.executor.builtin_actors()
}

/// Collect all the event emitters' delegated addresses, for those who have any.
fn emitter_delegated_addresses(&self, apply_ret: &ApplyRet) -> anyhow::Result<ActorAddressMap> {
let emitter_ids = apply_ret
Expand Down
1 change: 1 addition & 0 deletions fendermint/vm/interpreter/src/fvm/state/fevm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub type MockContractCall<T> = ethers::prelude::ContractCall<MockProvider, T>;
///
/// let _period: u64 = caller.call(&mut state, |c| c.bottom_up_check_period()).unwrap();
/// ```
#[derive(Clone)]
pub struct ContractCaller<C> {
addr: Address,
contract: C,
Expand Down
55 changes: 53 additions & 2 deletions fendermint/vm/interpreter/src/fvm/state/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,79 @@ use super::{
fevm::{ContractCaller, MockProvider},
FvmExecState,
};
use fendermint_vm_actor_interface::{eam::EthAddress, ipc};
use fendermint_vm_actor_interface::{eam::EthAddress, ipc::GATEWAY_ACTOR_ID};
use fendermint_vm_ipc_actors::gateway_getter_facet::GatewayGetterFacet;
use fendermint_vm_ipc_actors::gateway_manager_facet::{GatewayManagerFacet, Membership};
use fvm_ipld_blockstore::Blockstore;

/// Result of attempting to activate the next configuration.
pub enum Configuration {
/// No change - return the current configuration number.
Unchanged(u64),
/// Activated the next configuration.
Activated(Membership),
}

#[derive(Clone)]
pub struct GatewayCaller {
getter: ContractCaller<GatewayGetterFacet<MockProvider>>,
manager: ContractCaller<GatewayManagerFacet<MockProvider>>,
}

impl GatewayCaller {
pub fn new() -> Self {
let addr = EthAddress::from_id(ipc::GATEWAY_ACTOR_ID);
let addr = EthAddress::from_id(GATEWAY_ACTOR_ID);
Self {
getter: ContractCaller::new(addr, GatewayGetterFacet::new),
manager: ContractCaller::new(addr, GatewayManagerFacet::new),
}
}

/// Check that IPC is configured in this deployment.
pub fn enabled<DB: Blockstore>(&self, state: &mut FvmExecState<DB>) -> anyhow::Result<bool> {
match state.state_tree_mut().get_actor(GATEWAY_ACTOR_ID)? {
None => Ok(false),
Some(a) => Ok(!state.builtin_actors().is_placeholder_actor(&a.code)),
}
}

/// Return true if the current subnet is the root subnet.
pub fn is_root<DB: Blockstore>(&self, state: &mut FvmExecState<DB>) -> anyhow::Result<bool> {
let subnet_id = self.getter.call(state, |c| c.get_network_name())?;
Ok(subnet_id.route.is_empty())
}

/// Fetch the period at which this subnet should checkpoint itself to the parent.
pub fn bottom_up_check_period<DB: Blockstore>(
&self,
state: &mut FvmExecState<DB>,
) -> anyhow::Result<u64> {
self.getter.call(state, |c| c.bottom_up_check_period())
}

/// Activate the next available configuration at the end of the checkpoint period
/// and return the new validator set. If there was no change, return nothing.
pub fn activate_next_configuration<DB: Blockstore>(
&self,
state: &mut FvmExecState<DB>,
) -> anyhow::Result<Configuration> {
let current_cnr = self
.getter
.call(state, |c| c.get_current_configuration_number())?;

// In theory we could just call the transition, but currently it emits an event even if there is no change.
let next_cnr = self
.getter
.call(state, |c| c.get_last_configuration_number())?;

if current_cnr == next_cnr {
return Ok(Configuration::Unchanged(current_cnr));
}

let membership = self.manager.call(state, |c| c.update_membership())?;

Ok(Configuration::Activated(membership))
}
}

impl Default for GatewayCaller {
Expand Down
2 changes: 1 addition & 1 deletion fendermint/vm/ipc_actors/src/gateway_diamond.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ pub mod gateway_diamond {
__abi,
);
#[rustfmt::skip]
const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R6\x15`\x87W`\0\x805`\x01`\x01`\xE0\x1B\x03\x19\x16\x80\x82R\x7F\x80n\x0C\xBB\x9F\xCE)k\xBC3jH\xF4+\xF1\xDB\xC6\x97\"\xD1\x8D\x90\xD6\xFEp[u\x82\xC2\xBBK\xD2` R`@\x82 T`\x01`\x01`\xA0\x1B\x03\x16\x90\x81\x15`oWP\x81\x80\x916\x82\x807\x816\x91Z\xF4=\x82\x80>\x15`kW=\x90\xF3[=\x90\xFD[`$\x90`@Q\x90c\n\x82\xDDs`\xE3\x1B\x82R`\x04\x82\x01R\xFD[`\0\x805`\x01`\x01`\xE0\x1B\x03\x19\x16\x80\x82R\x7F\x80n\x0C\xBB\x9F\xCE)k\xBC3jH\xF4+\xF1\xDB\xC6\x97\"\xD1\x8D\x90\xD6\xFEp[u\x82\xC2\xBBK\xD2` R`@\x82 T`\x01`\x01`\xA0\x1B\x03\x16\x90\x81\x15`\xE9WP\x81\x80\x916\x82\x807\x816\x91Z\xF4=\x82\x80>\x15`kW=\x90\xF3[c\n\x82\xDDs`\xE3\x1B`\x80R`\x84R`$`\x80\xFD\xFE\xA2dipfsX\"\x12 7#\x08y\xE7\x8Bv\xA8\xF5^\xD5\xAB\x1D\xBB=\xE2\x81z\xE0H.;\x99\x1D\x066\x93\xF3m\r\xE5#dsolcC\0\x08\x13\x003";
const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R6\x15`\x87W`\0\x805`\x01`\x01`\xE0\x1B\x03\x19\x16\x80\x82R\x7F\x80n\x0C\xBB\x9F\xCE)k\xBC3jH\xF4+\xF1\xDB\xC6\x97\"\xD1\x8D\x90\xD6\xFEp[u\x82\xC2\xBBK\xD2` R`@\x82 T`\x01`\x01`\xA0\x1B\x03\x16\x90\x81\x15`oWP\x81\x80\x916\x82\x807\x816\x91Z\xF4=\x82\x80>\x15`kW=\x90\xF3[=\x90\xFD[`$\x90`@Q\x90c\n\x82\xDDs`\xE3\x1B\x82R`\x04\x82\x01R\xFD[`\0\x805`\x01`\x01`\xE0\x1B\x03\x19\x16\x80\x82R\x7F\x80n\x0C\xBB\x9F\xCE)k\xBC3jH\xF4+\xF1\xDB\xC6\x97\"\xD1\x8D\x90\xD6\xFEp[u\x82\xC2\xBBK\xD2` R`@\x82 T`\x01`\x01`\xA0\x1B\x03\x16\x90\x81\x15`\xE9WP\x81\x80\x916\x82\x807\x816\x91Z\xF4=\x82\x80>\x15`kW=\x90\xF3[c\n\x82\xDDs`\xE3\x1B`\x80R`\x84R`$`\x80\xFD\xFE\xA2dipfsX\"\x12 \xDDD\xCB-\x05\x94H\xB6\x19g\xFD?\xCB\x9C\xD5\xE3\x08Z\xE1\xB9\xDB\xF8Q22V\x97\xDC\xF1\x17\xBEidsolcC\0\x08\x13\x003";
/// The deployed bytecode of the contract.
pub static GATEWAYDIAMOND_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static(
__DEPLOYED_BYTECODE,
Expand Down
Loading