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

Commit

Permalink
FM-125: Ethereum accounts in genesis (#126)
Browse files Browse the repository at this point in the history
* FM-125: Add --kind=ethereum option to add-account CLI

* FM-125: Handle ethereum account creation in genesis

* FM-125: Only generate usable addresses in arbitrary genesis
  • Loading branch information
aakoshh authored Jun 20, 2023
1 parent 971fe24 commit 07e99ce
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 25 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.

11 changes: 8 additions & 3 deletions fendermint/app/src/cmd/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ use fendermint_app::APP_VERSION;
use fvm_shared::address::Address;
use std::path::PathBuf;

use fendermint_vm_actor_interface::eam::EthAddress;
use fendermint_vm_core::Timestamp;
use fendermint_vm_genesis::{
Account, Actor, ActorMeta, Genesis, Multisig, Power, SignerAddr, Validator, ValidatorKey,
};

use crate::cmd;
use crate::options::genesis::{
GenesisAddAccountArgs, GenesisAddMultisigArgs, GenesisAddValidatorArgs, GenesisArgs,
GenesisCommands, GenesisIntoTendermintArgs, GenesisNewArgs,
AccountKind, GenesisAddAccountArgs, GenesisAddMultisigArgs, GenesisAddValidatorArgs,
GenesisArgs, GenesisCommands, GenesisIntoTendermintArgs, GenesisNewArgs,
};

use super::key::read_public_key;
Expand Down Expand Up @@ -77,7 +78,11 @@ cmd! {
fn add_account(genesis_file: &PathBuf, args: &GenesisAddAccountArgs) -> anyhow::Result<()> {
update_genesis(genesis_file, |mut genesis| {
let pk = read_public_key(&args.public_key)?;
let addr = Address::new_secp256k1(&pk.serialize())?;
let pk = pk.serialize();
let addr = match args.kind {
AccountKind::Regular => Address::new_secp256k1(&pk)?,
AccountKind::Ethereum => Address::from(EthAddress::new_secp256k1(&pk)?),
};
let meta = ActorMeta::Account(Account {
owner: SignerAddr(addr),
});
Expand Down
11 changes: 10 additions & 1 deletion fendermint/app/src/options/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@

use std::path::PathBuf;

use clap::{Args, Subcommand};
use clap::{Args, Subcommand, ValueEnum};

use super::parse::{parse_network_version, parse_token_amount};
use fvm_shared::{econ::TokenAmount, version::NetworkVersion};

#[derive(Debug, Clone, ValueEnum)]
pub enum AccountKind {
Regular,
Ethereum,
}

#[derive(Subcommand, Debug)]
pub enum GenesisCommands {
/// Create a new Genesis file, with accounts and validators to be added later.
Expand Down Expand Up @@ -56,6 +62,9 @@ pub struct GenesisAddAccountArgs {
/// Initial balance in atto.
#[arg(long, short, value_parser = parse_token_amount)]
pub balance: TokenAmount,
/// Indicate whether the account is a regular or ethereum account.
#[arg(long, short, default_value = "regular")]
pub kind: AccountKind,
}

#[derive(Args, Debug)]
Expand Down
2 changes: 1 addition & 1 deletion fendermint/eth/api/src/apis/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ pub async fn get_uncle_by_block_number_and_index<C>(
}

fn h160_to_fvm_addr(addr: et::H160) -> fvm_shared::address::Address {
Address::from(&EthAddress(addr.0))
Address::from(EthAddress(addr.0))
}

/// Fetch transaction results to produce the full block.
Expand Down
26 changes: 21 additions & 5 deletions fendermint/vm/actor_interface/src/eam.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
// Copyright 2022-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

use cid::multihash::MultihashDigest;
use fvm_ipld_encoding::{
strict_bytes,
tuple::{Deserialize_tuple, Serialize_tuple},
};
use fvm_shared::{address::Address, ActorID, METHOD_CONSTRUCTOR};
use fvm_shared::{
address::{Address, Error, SECP_PUB_LEN},
ActorID, METHOD_CONSTRUCTOR,
};

define_singleton!(EAM {
id: 10,
Expand All @@ -28,16 +32,28 @@ impl EthAddress {
/// Returns an EVM-form ID address from actor ID.
///
/// This is copied from the `evm` actor library.
pub fn from_id(id: u64) -> EthAddress {
pub fn from_id(id: u64) -> Self {
let mut bytes = [0u8; 20];
bytes[0] = 0xff;
bytes[12..].copy_from_slice(&id.to_be_bytes());
EthAddress(bytes)
Self(bytes)
}

/// Hash the public key according to the Ethereum convention.
pub fn new_secp256k1(pubkey: &[u8]) -> Result<Self, Error> {
if pubkey.len() != SECP_PUB_LEN {
return Err(Error::InvalidSECPLength(pubkey.len()));
}
let mut hash20 = [0u8; 20];
// Based on [ethers::core::types::Signature]
let hash32 = cid::multihash::Code::Keccak256.digest(&pubkey[1..]);
hash20.copy_from_slice(&hash32.digest()[12..]);
Ok(Self(hash20))
}
}

impl From<&EthAddress> for Address {
fn from(value: &EthAddress) -> Address {
impl From<EthAddress> for Address {
fn from(value: EthAddress) -> Address {
if value.0[0] == 0xff {
let mut bytes = [0u8; 8];
bytes.copy_from_slice(&value.0[12..]);
Expand Down
4 changes: 4 additions & 0 deletions fendermint/vm/actor_interface/src/ethaccount.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright 2022-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

define_code!(ETHACCOUNT { code_id: 16 });
5 changes: 5 additions & 0 deletions fendermint/vm/actor_interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
//! The actor IDs can be found in [singletons](https://github.com/filecoin-project/builtin-actors/blob/master/runtime/src/builtin/singletons.rs),
//! while the code IDs are in [builtins](https://github.com/filecoin-project/builtin-actors/blob/master/runtime/src/runtime/builtins.rs)

/// Something we can use for empty state, similar to how the FVM uses `EMPTY_ARR_CID`.
pub const EMPTY_ARR: [(); 0] = [(); 0]; // Based on how it's done in `Tester`.

macro_rules! define_code {
($name:ident { code_id: $code_id:literal }) => {
paste::paste! {
Expand All @@ -36,7 +39,9 @@ macro_rules! define_singleton {
pub mod account;
pub mod cron;
pub mod eam;
pub mod ethaccount;
pub mod evm;
pub mod init;
pub mod multisig;
pub mod placeholder;
pub mod system;
8 changes: 8 additions & 0 deletions fendermint/vm/actor_interface/src/placeholder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright 2022-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
//! Placeholders can be used for delegated address types.
//! The FVM automatically creates one if the recipient of a transaction
//! doesn't exist. Then, the executor replaces the code later based on
//! the namespace in the delegated address.

define_code!(PLACEHOLDER { code_id: 13 });
3 changes: 2 additions & 1 deletion fendermint/vm/genesis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ arbitrary = { workspace = true, optional = true }
quickcheck = { workspace = true, optional = true }
rand = { workspace = true, optional = true }

cid = { workspace = true, optional = true }
fvm_shared = { workspace = true }
fendermint_testing = { path = "../../testing", optional = true }
fendermint_vm_core = { path = "../core" }
Expand All @@ -35,4 +36,4 @@ fvm_ipld_encoding = { workspace = true }

[features]
default = []
arb = ["arbitrary", "quickcheck", "fvm_shared/arb", "fendermint_testing/arb", "rand"]
arb = ["arbitrary", "quickcheck", "fvm_shared/arb", "fendermint_testing/arb", "rand", "cid"]
26 changes: 21 additions & 5 deletions fendermint/vm/genesis/src/arb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,39 @@
use crate::{
Account, Actor, ActorMeta, Genesis, Multisig, Power, SignerAddr, Validator, ValidatorKey,
};
use fendermint_testing::arb::{ArbAddress, ArbTokenAmount};
use cid::multihash::MultihashDigest;
use fendermint_testing::arb::ArbTokenAmount;
use fendermint_vm_core::Timestamp;
use fvm_shared::version::NetworkVersion;
use fvm_shared::{address::Address, version::NetworkVersion};
use quickcheck::{Arbitrary, Gen};
use rand::{rngs::StdRng, SeedableRng};

impl Arbitrary for ActorMeta {
fn arbitrary(g: &mut Gen) -> Self {
// NOTE: Signer addresses are probably only valid with public keys, but here we don't care.
// Generate keys which the loader knows how to initialize.
if bool::arbitrary(g) {
let pk = ValidatorKey::arbitrary(g).0;
let pk = pk.serialize();
let addr = if bool::arbitrary(g) {
Address::new_secp256k1(&pk).unwrap()
} else {
// NOTE: Not using `EthAddress` because it would be circular dependency.
let mut hash20 = [0u8; 20];
let hash32 = cid::multihash::Code::Keccak256.digest(&pk[1..]);
hash20.copy_from_slice(&hash32.digest()[12..]);
Address::new_delegated(10, &hash20).unwrap()
};
ActorMeta::Account(Account {
owner: SignerAddr(ArbAddress::arbitrary(g).0),
owner: SignerAddr(addr),
})
} else {
let n = u64::arbitrary(g) % 4 + 2;
let signers = (0..n)
.map(|_| SignerAddr(ArbAddress::arbitrary(g).0))
.map(|_| {
let pk = ValidatorKey::arbitrary(g).0;
let addr = Address::new_secp256k1(&pk.serialize()).unwrap();
SignerAddr(addr)
})
.collect();
let threshold = u64::arbitrary(g) % n + 1;
ActorMeta::Multisig(Multisig {
Expand Down
9 changes: 6 additions & 3 deletions fendermint/vm/interpreter/src/fvm/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0, MIT

use async_trait::async_trait;
use fendermint_vm_actor_interface::{cron, eam, init, system};
use fendermint_vm_actor_interface::{cron, eam, init, system, EMPTY_ARR};
use fendermint_vm_core::{chainid, Timestamp};
use fendermint_vm_genesis::{ActorMeta, Genesis, Validator};
use fvm_ipld_blockstore::Blockstore;
Expand Down Expand Up @@ -84,6 +84,7 @@ where
system::SYSTEM_ACTOR_ID,
&system_state,
TokenAmount::zero(),
None,
)?;

// Init actor
Expand All @@ -95,6 +96,7 @@ where
init::INIT_ACTOR_ID,
&init_state,
TokenAmount::zero(),
None,
)?;

// Cron actor
Expand All @@ -106,15 +108,16 @@ where
cron::CRON_ACTOR_ID,
&cron_state,
TokenAmount::zero(),
None,
)?;

// Ethereum Account Manager (EAM) actor
let eam_state = [(); 0]; // Based on how it's done in `Tester`.
state.create_actor(
eam::EAM_ACTOR_CODE_ID,
eam::EAM_ACTOR_ID,
&eam_state,
&EMPTY_ARR,
TokenAmount::zero(),
None,
)?;

// Create accounts
Expand Down
36 changes: 30 additions & 6 deletions fendermint/vm/interpreter/src/fvm/state/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@

use anyhow::{anyhow, Context};
use cid::{multihash::Code, Cid};
use fendermint_vm_actor_interface::{account, init, multisig};
use fendermint_vm_actor_interface::{
account::{self, ACCOUNT_ACTOR_CODE_ID},
eam,
ethaccount::ETHACCOUNT_ACTOR_CODE_ID,
init,
multisig::{self, MULTISIG_ACTOR_CODE_ID},
EMPTY_ARR,
};
use fendermint_vm_genesis::{Account, Multisig};
use fvm::{
machine::Manifest,
Expand All @@ -12,7 +19,13 @@ use fvm::{
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_car::load_car_unchecked;
use fvm_ipld_encoding::CborStore;
use fvm_shared::{clock::ChainEpoch, econ::TokenAmount, state::StateTreeVersion, ActorID};
use fvm_shared::{
address::{Address, Payload},
clock::ChainEpoch,
econ::TokenAmount,
state::StateTreeVersion,
ActorID,
};
use num_traits::Zero;
use serde::Serialize;

Expand Down Expand Up @@ -83,6 +96,7 @@ where
id: ActorID,
state: &impl Serialize,
balance: TokenAmount,
delegated_address: Option<Address>,
) -> anyhow::Result<()> {
// Retrieve the CID of the actor code by the numeric ID.
let code_cid = *self
Expand All @@ -97,7 +111,7 @@ where
state: state_cid,
sequence: 0,
balance,
delegated_address: None,
delegated_address,
};

self.state_tree.set_actor(id, actor_state);
Expand All @@ -112,13 +126,23 @@ where
ids: &init::AddressMap,
) -> anyhow::Result<()> {
let owner = acct.owner.0;
let state = account::State { address: owner };

let id = ids
.get(&owner)
.ok_or_else(|| anyhow!("can't find ID for owner {owner}"))?;

self.create_actor(account::ACCOUNT_ACTOR_CODE_ID, *id, &state, balance)
match owner.payload() {
Payload::Secp256k1(_) => {
let state = account::State { address: owner };
self.create_actor(ACCOUNT_ACTOR_CODE_ID, *id, &state, balance, None)
}
Payload::Delegated(d) if d.namespace() == eam::EAM_ACTOR_ID => {
let state = EMPTY_ARR;
// NOTE: Here we could use the placeholder code ID as well.
self.create_actor(ETHACCOUNT_ACTOR_CODE_ID, *id, &state, balance, Some(owner))
}
other => Err(anyhow!("unexpected actor owner: {other:?}")),
}
}

pub fn create_multisig_actor(
Expand Down Expand Up @@ -153,7 +177,7 @@ where
balance.clone(),
)?;

self.create_actor(multisig::MULTISIG_ACTOR_CODE_ID, next_id, &state, balance)
self.create_actor(MULTISIG_ACTOR_CODE_ID, next_id, &state, balance, None)
}

pub fn store(&self) -> &DB {
Expand Down

0 comments on commit 07e99ce

Please sign in to comment.