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

Commit

Permalink
FM-168: IPC solidity diamond (#171)
Browse files Browse the repository at this point in the history
* FM-168: Generate different ABI bindings for the diamond

* FM-168: First guess at how the genesis process should change

* FM-168: Update hardhat library resolver tests

* FM-168: Update genesis interpreter to handle facets

* FM-168: Fix; leave root dependencies in the topo sort and remove only top levels, leave facets

* FM-168: Factored out utility data structures

* FM-168: Fix lint
  • Loading branch information
aakoshh authored Aug 2, 2023
1 parent ef4045b commit adfb626
Show file tree
Hide file tree
Showing 22 changed files with 18,743 additions and 6,746 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.

4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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
Expand Down Expand Up @@ -37,6 +38,7 @@ clean:
cargo clean
cd $(BUILTIN_ACTORS_DIR) && cargo clean
rm $(BUILTIN_ACTORS_BUNDLE)
rm $(IPC_ACTORS_ABI)

lint: \
license \
Expand Down Expand Up @@ -103,7 +105,7 @@ $(IPC_ACTORS_ABI): $(IPC_ACTORS_CODE) | forge
fi
cd $(IPC_ACTORS_DIR) && \
git fetch origin && \
git checkout origin/fm-156-ipc-solidity-actors
git checkout $(IPC_ACTORS_TAG)
make -C $(IPC_ACTORS_DIR) compile-abi
touch $@

Expand Down
73 changes: 45 additions & 28 deletions fendermint/eth/hardhat/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,20 +97,20 @@ impl Hardhat {
}

/// Traverse the linked references and return the library contracts to be deployed in topological order.
pub fn library_dependencies(
///
/// The result will include the top contracts as well, and it's up to the caller to filter them out if
/// they have more complicated deployments including constructors. This is because there can be diamond
/// facets among them which aren't ABI visible dependencies but should be deployed as libraries.
pub fn dependencies(
&self,
top_contracts: &[(impl AsRef<Path>, &str)],
root_contracts: &[(impl AsRef<Path>, &str)],
) -> anyhow::Result<Vec<ContractSourceAndName>> {
let mut deps: DependencyTree<ContractSourceAndName> = Default::default();

let mut queue: VecDeque<ContractSourceAndName> = VecDeque::new();

let top_contracts = top_contracts
let mut queue = root_contracts
.iter()
.map(|(s, c)| (PathBuf::from(s.as_ref()), c.to_string()))
.collect::<Vec<_>>();

queue.extend(top_contracts.clone());
.collect::<VecDeque<_>>();

// Construct dependency tree by recursive traversal.
while let Some(sc) = queue.pop_front() {
Expand All @@ -131,10 +131,7 @@ impl Hardhat {
}

// Topo-sort the libraries in the order of deployment.
let mut sorted = topo_sort(deps)?;

// Remove the top contracts, which are assumed to be non-library contracts with potential constructor logic.
sorted.retain(|sc| !top_contracts.contains(sc));
let sorted = topo_sort(deps)?;

Ok(sorted)
}
Expand Down Expand Up @@ -289,8 +286,8 @@ mod tests {
Hardhat::new(contracts_path())
}

// Based on the `scripts/deploy-libraries.ts` in `ipc-solidity-actors`.
const GATEWAY_DEPS: [&str; 7] = [
// These are all the libraries based on the `scripts/deploy-libraries.ts` in `ipc-solidity-actors`.
const IPC_DEPS: [&str; 7] = [
"AccountHelper",
"CheckpointHelper",
"EpochVoteSubmissionHelper",
Expand All @@ -306,20 +303,22 @@ mod tests {

let mut libraries = HashMap::new();

for lib in GATEWAY_DEPS {
for lib in IPC_DEPS {
libraries.insert(lib.to_owned(), et::Address::default());
}

// This one requires a subset of above libraries.
let _bytecode = hardhat
.bytecode("Gateway.sol", "Gateway", &libraries)
.bytecode("GatewayManagerFacet.sol", "GatewayManagerFacet", &libraries)
.unwrap();
}

#[test]
fn bytecode_missing_link() {
let hardhat = test_hardhat();

let result = hardhat.bytecode("Gateway.sol", "Gateway", &Default::default());
// Not giving any dependency should result in a failure.
let result = hardhat.bytecode("GatewayDiamond.sol", "GatewayDiamond", &Default::default());

assert!(result.is_err());
assert!(result
Expand All @@ -332,16 +331,32 @@ mod tests {
fn library_dependencies() {
let hardhat = test_hardhat();

let lib_deps = hardhat
.library_dependencies(&[
("Gateway.sol", "Gateway"),
("SubnetRegistry.sol", "SubnetRegistry"),
])
let root_contracts: Vec<(String, &str)> = vec![
"GatewayDiamond",
"GatewayManagerFacet",
"GatewayGetterFacet",
"GatewayRouterFacet",
"SubnetRegistry",
]
.into_iter()
.map(|c| (format!("{c}.sol"), c))
.collect();

// Name our top level contracts and gather all required libraries.
let mut lib_deps = hardhat
.dependencies(&root_contracts)
.expect("failed to compute dependencies");

eprintln!("Gateway dependencies: {lib_deps:?}");
// For the sake of testing, let's remove top libraries from the dependency list.
lib_deps.retain(|(_, d)| !root_contracts.iter().any(|(_, c)| c == d));

eprintln!("IPC dependencies: {lib_deps:?}");

assert_eq!(lib_deps.len(), GATEWAY_DEPS.len());
assert_eq!(
lib_deps.len(),
IPC_DEPS.len(),
"should discover the same dependencies as expected"
);

let mut libs = HashMap::default();

Expand All @@ -353,13 +368,15 @@ mod tests {
libs.insert(hardhat.fqn(&s, &c), et::Address::default());
}

hardhat
.bytecode("Gateway.sol", "Gateway", &libs)
.expect("failed to produce contract bytecode in topo order");
for (src, name) in root_contracts {
hardhat
.bytecode(src, name, &libs)
.expect("failed to produce contract bytecode in topo order");
}
}

#[test]
fn sorting() {
fn topo_sorting() {
let mut tree: DependencyTree<u8> = Default::default();

for (k, ds) in [
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 @@ -11,6 +11,7 @@ license.workspace = true
[dependencies]
anyhow = { workspace = true }
ethers = { workspace = true }
lazy_static = { workspace = true }
paste = { workspace = true }
serde = { workspace = true }
serde_tuple = { workspace = true }
Expand Down
28 changes: 28 additions & 0 deletions fendermint/vm/actor_interface/src/diamond.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2022-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
//! Helper data structures to declare diamond pattern contracts.

// See https://medium.com/@MarqyMarq/how-to-implement-the-diamond-standard-69e87dae44e6

use std::collections::HashMap;

use ethers::abi::Abi;
use fvm_shared::ActorID;

#[derive(Clone, Debug)]
pub struct EthFacet {
pub name: &'static str,
pub abi: Abi,
}

/// Top level Ethereum contract with a pre-determined ID.
#[derive(Clone, Debug)]
pub struct EthContract {
/// Pre-determined ID for the contract.
pub actor_id: ActorID,
pub abi: Abi,
/// List of facets if the contract is using the diamond pattern.
pub facets: Vec<EthFacet>,
}

pub type EthContractMap = HashMap<&'static str, EthContract>;
4 changes: 2 additions & 2 deletions fendermint/vm/actor_interface/src/init.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};

// Copyright 2022-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
Expand Down Expand Up @@ -49,7 +49,7 @@ impl State {
// Accounts from the Genesis file.
accounts: &[Actor],
// Pre-defined IDs for top-level EVM contracts.
eth_builtin_ids: &[ActorID],
eth_builtin_ids: &HashSet<ActorID>,
// Number of dynamically deployed EVM library contracts.
eth_library_count: u64,
) -> anyhow::Result<(Self, AddressMap)> {
Expand Down
64 changes: 59 additions & 5 deletions fendermint/vm/actor_interface/src/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,70 @@
// Here we define stable IDs for them, so we can deploy the
// Solidity contracts during genesis.

use fendermint_vm_ipc_actors as ia;
pub use fendermint_vm_ipc_actors::gateway_manager_facet::SubnetID;
use lazy_static::lazy_static;

use crate::diamond::{EthContract, EthContractMap, EthFacet};

define_id!(GATEWAY { id: 64 });
define_id!(SUBNETREGISTRY { id: 65 });

lazy_static! {
pub static ref IPC_CONTRACTS: EthContractMap = {
[
(
"GatewayDiamond",
EthContract {
actor_id: GATEWAY_ACTOR_ID,
abi: ia::gateway_diamond::GATEWAYDIAMOND_ABI.to_owned(),
facets: vec![
EthFacet {
name: "GatewayGetterFacet",
abi: ia::gateway_getter_facet::GATEWAYGETTERFACET_ABI.to_owned(),
},
EthFacet {
name: "GatewayManagerFacet",
abi: ia::gateway_manager_facet::GATEWAYMANAGERFACET_ABI.to_owned(),
},
EthFacet {
name: "GatewayRouterFacet",
abi: ia::gateway_router_facet::GATEWAYROUTERFACET_ABI.to_owned(),
},
],
},
),
(
"SubnetRegistry",
EthContract {
actor_id: SUBNETREGISTRY_ACTOR_ID,
abi: ia::subnet_registry::SUBNETREGISTRY_ABI.to_owned(),
// The registry incorporates the SubnetActor facets.
facets: vec![
EthFacet {
name: "SubnetActorGetterFacet",
abi: ia::subnet_actor_getter_facet::SUBNETACTORGETTERFACET_ABI
.to_owned(),
},
EthFacet {
name: "SubnetActorManagerFacet",
abi: ia::subnet_actor_manager_facet::SUBNETACTORMANAGERFACET_ABI
.to_owned(),
},
],
},
),
]
.into_iter()
.collect()
};
}

pub mod gateway {
use super::SubnetID;
use ethers::contract::{EthAbiCodec, EthAbiType};
use ethers::core::types::{H160, U256};
use fendermint_vm_genesis::ipc::GatewayParams;
use fendermint_vm_ipc_actors::gateway::SubnetID;
use fvm_shared::address::Payload;

use crate::eam::{self, EthAddress};
Expand Down Expand Up @@ -64,9 +120,9 @@ pub mod gateway {

#[cfg(test)]
mod tests {
use super::SubnetID;
use ethers::core::types::U256;
use ethers_core::abi::Tokenize;
use fendermint_vm_ipc_actors::gateway::SubnetID;

use crate::ipc::tests::{check_param_types, constructor_param_types};

Expand All @@ -91,7 +147,7 @@ pub mod gateway {

let tokens = cp.into_tokens();

let cons = fendermint_vm_ipc_actors::gateway::GATEWAY_ABI
let cons = fendermint_vm_ipc_actors::gateway_diamond::GATEWAYDIAMOND_ABI
.constructor()
.expect("Gateway has a constructor");

Expand All @@ -105,8 +161,6 @@ pub mod gateway {
}
}

pub mod subnet_registry {}

#[cfg(test)]
mod tests {
use anyhow::bail;
Expand Down
1 change: 1 addition & 0 deletions fendermint/vm/actor_interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ macro_rules! define_singleton {
pub mod account;
pub mod burntfunds;
pub mod cron;
pub mod diamond;
pub mod eam;
pub mod ethaccount;
pub mod evm;
Expand Down
Loading

0 comments on commit adfb626

Please sign in to comment.