From 10cda7824cdf05658f9c67a0ca3c0cfe34f18714 Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 26 Nov 2024 14:24:31 -0500 Subject: [PATCH] add key derivation --- Cargo.lock | 2 + crates/by-chain/icepick-solana/src/lib.rs | 12 ++-- crates/icepick/Cargo.toml | 2 + crates/icepick/src/cli/mod.rs | 84 ++++++++++++++++++++--- icepick.toml | 1 + 5 files changed, 87 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f0deac7..ae2381b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1046,7 +1046,9 @@ version = "0.1.0" dependencies = [ "clap", "icepick-module", + "keyfork-derive-util", "keyforkd-client", + "keyforkd-models", "serde", "serde_json", "thiserror 2.0.3", diff --git a/crates/by-chain/icepick-solana/src/lib.rs b/crates/by-chain/icepick-solana/src/lib.rs index 466f93d..9738cb7 100644 --- a/crates/by-chain/icepick-solana/src/lib.rs +++ b/crates/by-chain/icepick-solana/src/lib.rs @@ -167,7 +167,7 @@ impl Module for Solana { match (&fee_payer, &fee_payer_address) { (Some(payer), Some(address)) => { // Use the provided account - Some((payer.clone(), Pubkey::from_str_const(address))) + Some((u32::from_str(payer).unwrap(), Pubkey::from_str(address).unwrap())) } (None, None) => { // Use the transaction account @@ -185,19 +185,23 @@ impl Module for Solana { &hash, ); let transaction = solana_sdk::transaction::Transaction::new_unsigned(message); - let mut required_derivation_indices = vec![]; // TODO: error handling from_str let from_account = from_account.and_then(|a| u32::from_str(&a).ok()).unwrap_or(0); - required_derivation_indices.push(from_account); + let mut requested_accounts = vec![]; + requested_accounts.push(from_account | 1 << 31); + if let Some((account, _)) = &payer_account_and_pk { + requested_accounts.push(*account | 1 << 31); + } Ok(serde_json::json!({ "blob": transaction, + "derivation-accounts": requested_accounts, })) } Operation::Sign(Sign {}) => { let blob = request.blob.expect("passed in instruction blob"); let transaction: solana_sdk::transaction::Transaction = serde_json::from_value(blob).expect("valid message blob"); - dbg!(transaction); + let keys = request.derived_keys.unwrap_or_default(); Ok(serde_json::json!({ "blob": [] })) diff --git a/crates/icepick/Cargo.toml b/crates/icepick/Cargo.toml index 43ed8a9..4ce6583 100644 --- a/crates/icepick/Cargo.toml +++ b/crates/icepick/Cargo.toml @@ -6,7 +6,9 @@ edition = "2021" [dependencies] clap = { version = "4.5.20", features = ["cargo", "derive", "string"] } icepick-module = { version = "0.1.0", path = "../icepick-module" } +keyfork-derive-util = { version = "0.2.1", registry = "distrust" } keyforkd-client = { version = "0.2.1", registry = "distrust" } +keyforkd-models = { version = "0.2.0", registry = "distrust" } serde = { workspace = true, features = ["derive"] } serde_json.workspace = true thiserror = "2.0.3" diff --git a/crates/icepick/src/cli/mod.rs b/crates/icepick/src/cli/mod.rs index 99dbb14..abca32e 100644 --- a/crates/icepick/src/cli/mod.rs +++ b/crates/icepick/src/cli/mod.rs @@ -18,7 +18,33 @@ pub fn get_command(bin_name: &str) -> (&str, Vec<&str>) { struct ModuleConfig { name: String, command_name: Option, - derivation_prefix: String, + algorithm: keyfork_derive_util::request::DerivationAlgorithm, + #[serde(with = "serde_derivation")] + derivation_prefix: keyfork_derive_util::DerivationPath, +} + +mod serde_derivation { + use keyfork_derive_util::DerivationPath; + use serde::{Deserialize, Deserializer, Serializer}; + use std::str::FromStr; + + pub fn serialize(p: &DerivationPath, serializer: S) -> Result + where + S: Serializer, + { + let path = p.to_string(); + serializer.serialize_str(&path) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + String::deserialize(deserializer).and_then(|string| { + DerivationPath::from_str(&string).map_err(|e| Error::custom(e.to_string())) + }) + } } #[derive(Serialize, Deserialize, Debug, Default)] @@ -45,7 +71,10 @@ pub fn do_cli_thing() { // and coin_bin otherwise wouldn't live long enough for module in &config.modules { let module_name = &module.name; - let bin = module.command_name.clone().unwrap_or_else(|| format!("icepick-{module_name}")); + let bin = module + .command_name + .clone() + .unwrap_or_else(|| format!("icepick-{module_name}")); let (command, args) = get_command(&bin); let mut child = Command::new(command) .args(args) @@ -93,6 +122,10 @@ pub fn do_cli_thing() { } let blob = cli_input.as_ref().and_then(|json| json.get("blob")); + let derivation_accounts = cli_input + .as_ref() + .and_then(|json| json.get("derivation-accounts")); + let matches = icepick_command.get_matches(); if let Some((module, matches)) = matches.subcommand() { if let Some((subcommand, matches)) = matches.subcommand() { @@ -107,19 +140,50 @@ pub fn do_cli_thing() { for arg in &operation.arguments { args.insert(&arg.name, matches.get_one::(&arg.name)); } + + let (algo, path) = config.modules.iter().find_map(|fmodule| { + if fmodule.name == module { + return Some((fmodule.algorithm.clone(), fmodule.derivation_prefix.clone())) + } + None + }).unwrap(); + + let mut derived_keys: Vec> = vec![]; + if let Some(accounts) = derivation_accounts { + let accounts: Vec = + serde_json::from_value(accounts.clone()) + .expect("valid derivation-accounts"); + let mut client = + keyforkd_client::Client::discover_socket().expect("keyforkd started"); + for account in accounts { + let request = keyfork_derive_util::request::DerivationRequest::new(algo.clone(), &path.clone().chain_push(account)); + let request = keyforkd_models::Request::Derivation(request); + let response = client.request(&request).expect("valid derivation"); + match response { + keyforkd_models::Response::Derivation(keyfork_derive_util::request::DerivationResponse { data, .. }) => { + derived_keys.push(data.to_vec()); + }, + _ => panic!("Unexpected response"), + } + } + } + let json = serde_json::json!({ "operation": subcommand, "values": args, - "derived-keys": [], + "derived-keys": derived_keys, "blob": blob, }); - let bin = commands.iter().find_map(|(fmodule, fcommand, _)| { - if fmodule == module { - Some(fcommand) - } else { - None - } - }).expect("previously found module should exist in new search"); + let bin = commands + .iter() + .find_map(|(fmodule, fcommand, _)| { + if fmodule == module { + Some(fcommand) + } else { + None + } + }) + .expect("previously found module should exist in new search"); let (command, args) = get_command(bin); let mut child = Command::new(command) .args(args) diff --git a/icepick.toml b/icepick.toml index da16332..1c0a7f9 100644 --- a/icepick.toml +++ b/icepick.toml @@ -1,3 +1,4 @@ [[module]] name = "sol" derivation_prefix = "m/44'/501'" +algorithm = "Ed25519"