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

Import evm wallet #251

Merged
merged 4 commits into from
Jul 12, 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.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ bytes = "1.4.0"
serde_bytes = "0.11.9"
clap = { version = "4.1.4", features = ["env", "derive"] }
thiserror = { workspace = true }
hex = { workspace = true }
serde_tuple = "0.5.0"
zeroize = "1.6.0"

Expand Down
6 changes: 3 additions & 3 deletions identity/src/evm/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ impl<T: Clone + Eq + Hash + TryFrom<KeyInfo>> KeyStore for MemoryKeyStore<T> {
Ok(self.data.keys().cloned().collect())
}

fn put(&mut self, info: KeyInfo) -> Result<()> {
fn put(&mut self, info: KeyInfo) -> Result<Self::Key> {
let addr = Self::Key::try_from(info.clone())
.map_err(|_| anyhow!("cannot convert private key to public key"))?;
self.data.insert(addr, info);
Ok(())
self.data.insert(addr.clone(), info);
Ok(addr)
}

fn remove(&mut self, addr: &Self::Key) -> Result<()> {
Expand Down
10 changes: 9 additions & 1 deletion identity/src/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use zeroize::Zeroize;

pub use crate::evm::persistent::PersistentKeyStore;

pub const DEFAULT_KEYSTORE_NAME: &str = "evm_keystore.json";

/// The key store trait for different evm key store
pub trait KeyStore {
/// The type of the key that is stored
Expand All @@ -22,7 +24,7 @@ pub trait KeyStore {
/// List all addresses in the key store
fn list(&self) -> Result<Vec<Self::Key>>;
/// Put a new info to the addr
fn put(&mut self, info: KeyInfo) -> Result<()>;
fn put(&mut self, info: KeyInfo) -> Result<Self::Key>;
/// Remove address from the key store
fn remove(&mut self, addr: &Self::Key) -> Result<()>;
}
Expand All @@ -33,6 +35,12 @@ pub struct KeyInfo {
private_key: Vec<u8>,
}

impl KeyInfo {
pub fn new(private_key: Vec<u8>) -> Self {
Self { private_key }
}
}

impl KeyInfo {
pub fn private_key(&self) -> &[u8] {
&self.private_key
Expand Down
8 changes: 4 additions & 4 deletions identity/src/evm/persistent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ impl<T: Clone + Eq + Hash + AsRef<[u8]> + TryFrom<KeyInfo>> KeyStore for Persist
self.memory.list()
}

fn put(&mut self, info: KeyInfo) -> Result<()> {
self.memory.put(info)?;
// TODO: We can flush to disk only after certain number of `put` is called.
self.flush_no_encryption()
fn put(&mut self, info: KeyInfo) -> Result<Self::Key> {
let addr = self.memory.put(info)?;
self.flush_no_encryption()?;
Ok(addr)
}

fn remove(&mut self, addr: &Self::Key) -> Result<()> {
Expand Down
4 changes: 3 additions & 1 deletion identity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@
mod evm;
mod fvm;

pub use crate::evm::{KeyInfo as EvmKeyInfo, KeyStore as EvmKeyStore, PersistentKeyStore};
pub use crate::evm::{
KeyInfo as EvmKeyInfo, KeyStore as EvmKeyStore, PersistentKeyStore, DEFAULT_KEYSTORE_NAME,
};
pub use crate::fvm::*;
22 changes: 19 additions & 3 deletions src/cli/commands/wallet/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: MIT
//! Wallet import cli handler

use anyhow::anyhow;
use async_trait::async_trait;
use clap::Args;
use std::fmt::Debug;
Expand All @@ -11,6 +12,9 @@ use crate::cli::commands::get_ipc_agent_url;
use crate::cli::{CommandLineHandler, GlobalArguments};
use crate::sdk::{IpcAgentClient, LotusJsonKeyType};

const FVM_WALLET: u8 = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not an enum?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might not be able to pass enum to clap/cli, or at least not that easy. So use a number is just easier.

const EVM_WALLET: u8 = 1;

pub(crate) struct WalletImport;

#[async_trait]
Expand All @@ -28,11 +32,17 @@ impl CommandLineHandler for WalletImport {
return Err(anyhow::anyhow!("stdin not supported yet"));
};

let key_type = LotusJsonKeyType::from_str(&keyinfo)?;
let url = get_ipc_agent_url(&arguments.ipc_agent_url, global)?;

let client = IpcAgentClient::default_from_url(url);
let addr = client.import_lotus_json(key_type).await?;

let addr = match arguments.network_type {
FVM_WALLET => {
let key_type = LotusJsonKeyType::from_str(&keyinfo)?;
client.import_lotus_json(key_type).await?
}
EVM_WALLET => client.import_evm_private_key(keyinfo).await?,
_ => return Err(anyhow!("invalid network type")),
};

log::info!("imported wallet with address {:?}", addr);

Expand All @@ -45,6 +55,12 @@ impl CommandLineHandler for WalletImport {
pub(crate) struct WalletImportArgs {
#[arg(long, short, help = "The JSON RPC server url for ipc agent")]
pub ipc_agent_url: Option<String>,
#[arg(
long,
short,
help = "The network the key belongs to, 0 for fvm, 1 for evm"
)]
pub network_type: u8,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to make it an enum or even two different boolean flags --fvm and --evm?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think bool is better, but not sure how we can enforce only one flag should be set. I can check it in code, but if we have more, then we need to update the code every time.

#[arg(long, short, help = "Path of keyinfo file for the key to import")]
pub path: Option<String>,
}
5 changes: 4 additions & 1 deletion src/manager/evm/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ impl SubnetManager for EthSubnetManager {

log::info!("creating subnet on evm with params: {params:?}");

let signer = self.get_signer(&payload_to_evm_address(from.payload())?)?;
let evm_from = payload_to_evm_address(from.payload())?;
log::debug!("original from address: {from:?}, evm: {evm_from:?}");

let signer = self.get_signer(&evm_from)?;
let registry_contract =
SubnetRegistry::new(self.ipc_contract_info.registry_addr, Arc::new(signer));

Expand Down
14 changes: 11 additions & 3 deletions src/sdk/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use crate::config::json_rpc_methods;
use crate::jsonrpc::JsonRpcClient;
use crate::lotus::message::wallet::WalletKeyType;
use crate::sdk::IpcAgentClient;
use crate::server::wallet::import::{WalletImportParams, WalletImportResponse};
use crate::server::wallet::import::{
EvmImportParams, FvmImportParams, WalletImportParams, WalletImportResponse,
};
use fvm_shared::crypto::signature::SignatureType;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
Expand All @@ -15,10 +17,16 @@ use zeroize::Zeroize;
impl<T: JsonRpcClient> IpcAgentClient<T> {
/// Import a wallet address in the form of lotus json
pub async fn import_lotus_json(&self, key_type: LotusJsonKeyType) -> anyhow::Result<String> {
let params = WalletImportParams {
let params = WalletImportParams::Fvm(FvmImportParams {
key_type: SignatureType::try_from(WalletKeyType::from_str(&key_type.r#type)?)? as u8,
private_key: key_type.private_key.clone(),
};
});
self.import(params).await
}

/// Import a wallet address in the form of lotus json
pub async fn import_evm_private_key(&self, private_key: String) -> anyhow::Result<String> {
let params = WalletImportParams::Evm(EvmImportParams { private_key });
self.import(params).await
}

Expand Down
7 changes: 3 additions & 4 deletions src/server/handlers/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use crate::config::ReloadableConfig;
use crate::server::JsonRPCRequestHandler;
use anyhow::anyhow;
use async_trait::async_trait;
use ipc_identity::PersistentKeyStore;
use ipc_identity::{KeyStore, KeyStoreConfig, KEYSTORE_NAME};
use ipc_identity::{PersistentKeyStore, DEFAULT_KEYSTORE_NAME};
use serde::{Deserialize, Serialize};
use std::{path::Path, sync::Arc};

Expand Down Expand Up @@ -56,9 +56,8 @@ pub fn new_evm_keystore_from_config(
) -> anyhow::Result<PersistentKeyStore<ethers::types::Address>> {
let repo_str = config.get_config_repo();
if let Some(repo_str) = repo_str {
let repo = Path::new(&repo_str);
PersistentKeyStore::new(repo.into())
.map_err(|e| anyhow!("Failed to create evm keystore: {}", e))
let repo = Path::new(&repo_str).join(DEFAULT_KEYSTORE_NAME);
PersistentKeyStore::new(repo).map_err(|e| anyhow!("Failed to create evm keystore: {}", e))
} else {
Err(anyhow!("No keystore repo found in config"))
}
Expand Down
2 changes: 1 addition & 1 deletion src/server/handlers/manager/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ impl JsonRPCRequestHandler for CreateSubnetHandler {
};

let from = parse_from(subnet_config, request.from)?;
log::debug!("conn: {:?}", conn.subnet());
log::debug!("conn: {:?}, from: {from:?}", conn.subnet());

let created_subnet_addr = conn
.manager()
Expand Down
5 changes: 3 additions & 2 deletions src/server/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl Handlers {
let pool = Arc::new(SubnetManagerPool::new(
config,
fvm_wallet.clone(),
evm_keystore,
evm_keystore.clone(),
));
let h: Box<dyn HandlerWrapper> = Box::new(CreateSubnetHandler::new(pool.clone()));
handlers.insert(String::from(json_rpc_methods::CREATE_SUBNET), h);
Expand Down Expand Up @@ -131,7 +131,8 @@ impl Handlers {
let h: Box<dyn HandlerWrapper> = Box::new(WalletNewHandler::new(fvm_wallet.clone()));
handlers.insert(String::from(json_rpc_methods::WALLET_NEW), h);

let h: Box<dyn HandlerWrapper> = Box::new(WalletImportHandler::new(fvm_wallet.clone()));
let h: Box<dyn HandlerWrapper> =
Box::new(WalletImportHandler::new(fvm_wallet.clone(), evm_keystore));
handlers.insert(String::from(json_rpc_methods::WALLET_IMPORT), h);

let _h: Box<dyn HandlerWrapper> = Box::new(WalletExportHandler::new(fvm_wallet.clone()));
Expand Down
79 changes: 65 additions & 14 deletions src/server/handlers/wallet/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,43 @@ use async_trait::async_trait;
use base64::Engine;
use fvm_shared::crypto::signature::SignatureType;
use ipc_identity::json::KeyInfoJson;
use ipc_identity::{KeyInfo, Wallet};
use ipc_identity::{EvmKeyInfo, EvmKeyStore, KeyInfo, PersistentKeyStore, Wallet};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, RwLock};
use zeroize::Zeroize;

#[derive(Deserialize, Serialize, Debug)]
#[serde(tag = "network_type")]
pub enum WalletImportParams {
#[serde(rename = "fvm")]
Fvm(FvmImportParams),
#[serde(rename = "evm")]
Evm(EvmImportParams),
}

#[derive(Debug, Serialize, Deserialize)]
pub struct WalletImportParams {
pub struct FvmImportParams {
pub key_type: u8,
/// Base64 encoded private key string
pub private_key: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct EvmImportParams {
/// Hex encoded private key string
pub private_key: String,
}

impl Drop for WalletImportParams {
fn drop(&mut self) {
self.private_key.zeroize();
match self {
WalletImportParams::Fvm(p) => {
p.private_key.zeroize();
}
WalletImportParams::Evm(p) => {
p.private_key.zeroize();
}
}
}
}

Expand All @@ -32,22 +54,23 @@ pub struct WalletImportResponse {

/// Send value between two addresses within a subnet
pub(crate) struct WalletImportHandler {
wallet: Arc<RwLock<Wallet>>,
fvm_wallet: Arc<RwLock<Wallet>>,
evm_keystore: Arc<RwLock<PersistentKeyStore<ethers::types::Address>>>,
}

impl WalletImportHandler {
pub(crate) fn new(wallet: Arc<RwLock<Wallet>>) -> Self {
Self { wallet }
pub(crate) fn new(
fvm_wallet: Arc<RwLock<Wallet>>,
evm_keystore: Arc<RwLock<PersistentKeyStore<ethers::types::Address>>>,
) -> Self {
Self {
fvm_wallet,
evm_keystore,
}
}
}

#[async_trait]
impl JsonRPCRequestHandler for WalletImportHandler {
type Request = WalletImportParams;
type Response = WalletImportResponse;

async fn handle(&self, request: Self::Request) -> anyhow::Result<Self::Response> {
let mut wallet = self.wallet.write().unwrap();
fn import_fvm(&self, request: &FvmImportParams) -> anyhow::Result<WalletImportResponse> {
let mut wallet = self.fvm_wallet.write().unwrap();

let key_type = if request.key_type == SignatureType::BLS as u8 {
SignatureType::BLS
Expand All @@ -66,4 +89,32 @@ impl JsonRPCRequestHandler for WalletImportHandler {
address: address.to_string(),
})
}

fn import_evm(&self, request: &EvmImportParams) -> anyhow::Result<WalletImportResponse> {
let mut keystore = self.evm_keystore.write().unwrap();

let private_key = if !request.private_key.starts_with("0x") {
hex::decode(&request.private_key)?
} else {
hex::decode(&request.private_key.as_str()[2..])?
};
let addr = keystore.put(EvmKeyInfo::new(private_key))?;

Ok(WalletImportResponse {
address: format!("{:}", addr),
})
}
}

#[async_trait]
impl JsonRPCRequestHandler for WalletImportHandler {
type Request = WalletImportParams;
type Response = WalletImportResponse;

async fn handle(&self, request: Self::Request) -> anyhow::Result<Self::Response> {
match &request {
WalletImportParams::Fvm(p) => self.import_fvm(p),
WalletImportParams::Evm(p) => self.import_evm(p),
}
}
}
2 changes: 1 addition & 1 deletion testing/itest/src/infra/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl SubnetInfra {

let actor_addr = util::create_subnet(
self.config.ipc_agent_url(),
self.config.parent_wallet_address.clone(),
None,
parent,
self.config.name.clone(),
self.config.number_of_nodes as u64,
Expand Down
3 changes: 2 additions & 1 deletion testing/itest/src/infra/subnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,8 @@ impl SubnetNode {
pub async fn join_subnet(&self) -> Result<()> {
util::join_subnet(
self.ipc_agent_url.clone(),
self.wallet_address.clone().unwrap(),
None,
Some(self.wallet_address.clone().unwrap()),
self.id.to_string(),
DEFAULT_MIN_STAKE,
self.validator.net_addr.clone().unwrap(),
Expand Down
11 changes: 6 additions & 5 deletions testing/itest/src/infra/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ fn client_from_url(url: String) -> anyhow::Result<IpcAgentClient<JsonRpcClientIm
/// Create a new subnet in the actor
pub async fn create_subnet(
ipc_agent_url: String,
from: String,
from: Option<String>,
parent: String,
name: String,
min_validators: u64,
) -> anyhow::Result<String> {
let client = client_from_url(ipc_agent_url)?;
let params = CreateSubnetParams {
from: Some(from),
from,
parent,
name,
min_validator_stake: DEFAULT_MIN_STAKE,
Expand All @@ -42,18 +42,19 @@ pub async fn create_subnet(
/// Join the subnet
pub async fn join_subnet(
ipc_agent_url: String,
from: String,
from: Option<String>,
worker_addr: Option<String>,
subnet: String,
collateral: f64,
validator_net_addr: String,
) -> anyhow::Result<()> {
let client = client_from_url(ipc_agent_url)?;
let params = JoinSubnetParams {
subnet,
from: Some(from),
from,
collateral,
validator_net_addr,
worker_addr: None,
worker_addr,
};
client.join_subnet(params).await
}
Expand Down