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

FM-168: IPC solidity diamond #171

Merged
merged 7 commits into from
Aug 2, 2023
Merged
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
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
Loading