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

Commit

Permalink
FM-283: Add validator account kind setting (#285)
Browse files Browse the repository at this point in the history
  • Loading branch information
aakoshh authored Sep 28, 2023
1 parent 10c7a29 commit 26510f2
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 32 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

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

8 changes: 7 additions & 1 deletion fendermint/app/config/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ contracts_dir = "contracts"
builtin_actors_bundle = "bundle.car"
# Where to reach CometBFT for queries or broadcasting transactions.
tendermint_rpc_url = "http://127.0.0.1:26657"

# Secp256k1 private key used for signing transactions. Leave empty if not validating,
# or if it's not needed to sign and broadcast transactions as a validator.
# Leaving empty by default so single node deployments don't fail to start because
# this key is not copied into place.
validator_key = ""
# [validator_key]
# # Path to the secret key file in base64 format.
# path =

# # The on-chain account kind (regular|ethereum)
# kind =

[abci]
# Number of concurrent requests allowed to reach the application.
Expand Down
4 changes: 4 additions & 0 deletions fendermint/app/config/test.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# These setting are overlayed over `default.toml` in unit tests
# to exercise parsing values that are not viable as defaults.

[validator_key]
path = "dummy.sk"
kind = "ethereum"

[resolver]
subnet_id = "/r31415926"

Expand Down
8 changes: 7 additions & 1 deletion fendermint/app/options/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use fvm_ipld_encoding::RawBytes;
use fvm_shared::{address::Address, econ::TokenAmount, MethodNum};
use tendermint_rpc::Url;

use crate::parse::{parse_address, parse_bytes, parse_cid, parse_full_fil, parse_token_amount};
use crate::{
genesis::AccountKind,
parse::{parse_address, parse_bytes, parse_cid, parse_full_fil, parse_token_amount},
};

#[derive(Args, Debug)]
pub struct RpcArgs {
Expand Down Expand Up @@ -151,6 +154,9 @@ pub struct TransArgs {
/// Path to the secret key of the sender to sign the transaction.
#[arg(long, short)]
pub secret_key: PathBuf,
/// Indicate whether its a regular or ethereum account.
#[arg(long, short, default_value = "regular")]
pub account_kind: AccountKind,
/// Sender account nonce.
#[arg(long, short = 'n')]
pub sequence: u64,
Expand Down
32 changes: 24 additions & 8 deletions fendermint/app/settings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,26 @@ impl std::net::ToSocketAddrs for SocketAddress {
}
}

#[derive(Debug, Deserialize, Clone)]
#[serde(rename_all = "lowercase")]
/// Indicate the FVM account kind for generating addresses from a key.
pub enum AccountKind {
/// Has an f1 address.
Regular,
/// Has an f410 address.
Ethereum,
}

/// A Secp256k1 key used to sign transactions,
/// with the account kind showing if it's a regular or an ethereum key.
#[derive(Debug, Deserialize, Clone)]
pub struct SigningKey {
path: PathBuf,
pub kind: AccountKind,
}

home_relative!(SigningKey { path });

#[derive(Debug, Deserialize, Clone)]
pub struct AbciSettings {
pub listen: SocketAddress,
Expand Down Expand Up @@ -99,8 +119,9 @@ pub struct Settings {
builtin_actors_bundle: PathBuf,
/// Where to reach CometBFT for queries or broadcasting transactions.
tendermint_rpc_url: Url,
/// Secp256k1 private key used for signing transactions. Leave empty if not validating.
validator_key: PathBuf,

/// Secp256k1 private key used for signing transactions sent in the validator's name. Leave empty if not validating.
pub validator_key: Option<SigningKey>,

pub abci: AbciSettings,
pub db: DbSettings,
Expand Down Expand Up @@ -133,12 +154,7 @@ macro_rules! home_relative {
}

impl Settings {
home_relative!(
data_dir,
contracts_dir,
builtin_actors_bundle,
validator_key
);
home_relative!(data_dir, contracts_dir, builtin_actors_bundle);

/// Load the default configuration from a directory,
/// then potential overrides specific to the run mode,
Expand Down
13 changes: 12 additions & 1 deletion fendermint/app/src/cmd/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use std::pin::Pin;
use anyhow::Context;
use async_trait::async_trait;
use bytes::Bytes;
use fendermint_app_options::genesis::AccountKind;
use fendermint_crypto::SecretKey;
use fendermint_rpc::client::BoundFendermintClient;
use fendermint_rpc::tx::{
AsyncResponse, BoundClient, CallClient, CommitResponse, SyncResponse, TxAsync, TxClient,
Expand Down Expand Up @@ -329,8 +331,9 @@ struct TransClient {
impl TransClient {
pub fn new(client: FendermintClient, args: &TransArgs) -> anyhow::Result<Self> {
let sk = read_secret_key(&args.secret_key)?;
let addr = to_address(&sk, &args.account_kind)?;
let chain_id = chainid::from_str_hashed(&args.chain_name)?;
let mf = MessageFactory::new(sk, args.sequence, chain_id)?;
let mf = MessageFactory::new(sk, addr, args.sequence, chain_id);
let client = client.bind(mf);
let client = Self {
inner: client,
Expand Down Expand Up @@ -377,3 +380,11 @@ fn gas_params(args: &TransArgs) -> GasParams {
gas_premium: args.gas_premium.clone(),
}
}

fn to_address(sk: &SecretKey, kind: &AccountKind) -> anyhow::Result<Address> {
let pk = sk.public_key().serialize();
match kind {
AccountKind::Regular => Ok(Address::new_secp256k1(&pk)?),
AccountKind::Ethereum => Ok(Address::new_delegated(eam::EAM_ACTOR_ID, &pk)?),
}
}
35 changes: 28 additions & 7 deletions fendermint/app/src/cmd/run.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
// Copyright 2022-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

use anyhow::{anyhow, Context};
use anyhow::{anyhow, bail, Context};
use fendermint_abci::ApplicationService;
use fendermint_app::{App, AppConfig, AppStore, BitswapBlockstore};
use fendermint_app_settings::AccountKind;
use fendermint_crypto::SecretKey;
use fendermint_rocksdb::{blockstore::NamespaceBlockstore, namespaces, RocksDb, RocksDbConfig};
use fendermint_vm_actor_interface::eam;
use fendermint_vm_interpreter::{
bytes::{BytesMessageInterpreter, ProposalPrepareMode},
chain::{ChainMessageInterpreter, CheckpointPool},
fvm::{Broadcaster, FvmMessageInterpreter, ValidatorContext},
signed::SignedMessageInterpreter,
};
use fendermint_vm_resolver::ipld::IpldResolver;
use fvm_shared::address::Address;
use libp2p::identity::secp256k1;
use libp2p::identity::Keypair;
use tracing::info;
Expand All @@ -33,20 +37,29 @@ async fn run(settings: Settings) -> anyhow::Result<()> {
tendermint_rpc::HttpClient::new(settings.tendermint_rpc_url()?)
.context("failed to create Tendermint client")?;

let validator_key = {
let sk = settings.validator_key();
if sk.exists() && sk.is_file() {
Some(read_secret_key(&sk).context("failed to read validator key")?)
} else {
let validator = match settings.validator_key {
Some(ref key) => {
let sk = key.path(settings.home_dir());
if sk.exists() && sk.is_file() {
let sk = read_secret_key(&sk).context("failed to read validator key")?;
let addr = to_address(&sk, &key.kind)?;
Some((sk, addr))
} else {
bail!("validator key does not exist: {}", sk.to_string_lossy());
}
}
None => {
tracing::debug!("validator key not configured");
None
}
};

let validator_ctx = validator_key.map(|sk| {
let validator_ctx = validator.map(|(sk, addr)| {
// For now we are using the validator key for submitting transactions.
// This allows us to identify transactions coming from bonded validators, to give priority to protocol related transactions.
let broadcaster = Broadcaster::new(
client.clone(),
addr,
sk.clone(),
settings.fvm.gas_fee_cap.clone(),
settings.fvm.gas_premium.clone(),
Expand Down Expand Up @@ -237,3 +250,11 @@ fn to_resolver_config(settings: &Settings) -> anyhow::Result<ipc_ipld_resolver::

Ok(config)
}

fn to_address(sk: &SecretKey, kind: &AccountKind) -> anyhow::Result<Address> {
let pk = sk.public_key().serialize();
match kind {
AccountKind::Regular => Ok(Address::new_secp256k1(&pk)?),
AccountKind::Ethereum => Ok(Address::new_delegated(eam::EAM_ACTOR_ID, &pk)?),
}
}
3 changes: 1 addition & 2 deletions fendermint/rpc/examples/simplecoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,7 @@ async fn main() {
.value
.chain_id;

let mf = MessageFactory::new(sk, sn, ChainID::from(chain_id))
.expect("failed to create message factor");
let mf = MessageFactory::new_secp256k1(sk, sn, ChainID::from(chain_id));

let mut client = client.bind(mf);

Expand Down
16 changes: 11 additions & 5 deletions fendermint/rpc/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,21 @@ pub struct MessageFactory {
}

impl MessageFactory {
pub fn new(sk: SecretKey, sequence: u64, chain_id: ChainID) -> anyhow::Result<Self> {
let pk = sk.public_key();
let addr = Address::new_secp256k1(&pk.serialize())?;
Ok(Self {
/// Create a factor from a secret key and its corresponding address, which could be a delegated one.
pub fn new(sk: SecretKey, addr: Address, sequence: u64, chain_id: ChainID) -> Self {
Self {
sk,
addr,
sequence,
chain_id,
})
}
}

/// Treat the secret key as an f1 type account.
pub fn new_secp256k1(sk: SecretKey, sequence: u64, chain_id: ChainID) -> Self {
let pk = sk.public_key();
let addr = Address::new_secp256k1(&pk.serialize()).expect("public key is 65 bytes");
Self::new(sk, addr, sequence, chain_id)
}

/// Convenience method to read the secret key from a file, expected to be in Base64 format.
Expand Down
7 changes: 2 additions & 5 deletions fendermint/vm/interpreter/src/fvm/broadcast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,12 @@ where
{
pub fn new(
client: C,
addr: Address,
secret_key: SecretKey,
gas_fee_cap: TokenAmount,
gas_premium: TokenAmount,
) -> Self {
let client = FendermintClient::new(client);
// TODO: We could use f410 addresses to send the transaction, but the `MessageFactory` assumes f1.
let addr = Address::new_secp256k1(&secret_key.public_key().serialize())
.expect("public key is 65 bytes");
Self {
client,
secret_key,
Expand All @@ -65,8 +63,7 @@ where
.await
.context("failed to get broadcaster sequence")?;

let factory = MessageFactory::new(self.secret_key.clone(), sequence, chain_id)
.context("failed to create MessageFactory")?;
let factory = MessageFactory::new(self.secret_key.clone(), self.addr, sequence, chain_id);

// Using the bound client as a one-shot transaction sender.
let mut client = self.client.clone().bind(factory);
Expand Down

0 comments on commit 26510f2

Please sign in to comment.