add key derivation

This commit is contained in:
Ryan Heywood 2024-11-26 14:24:31 -05:00
parent 40467ce13d
commit 10cda7824c
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
5 changed files with 87 additions and 14 deletions

2
Cargo.lock generated
View File

@ -1046,7 +1046,9 @@ version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"icepick-module", "icepick-module",
"keyfork-derive-util",
"keyforkd-client", "keyforkd-client",
"keyforkd-models",
"serde", "serde",
"serde_json", "serde_json",
"thiserror 2.0.3", "thiserror 2.0.3",

View File

@ -167,7 +167,7 @@ impl Module for Solana {
match (&fee_payer, &fee_payer_address) { match (&fee_payer, &fee_payer_address) {
(Some(payer), Some(address)) => { (Some(payer), Some(address)) => {
// Use the provided account // 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) => { (None, None) => {
// Use the transaction account // Use the transaction account
@ -185,19 +185,23 @@ impl Module for Solana {
&hash, &hash,
); );
let transaction = solana_sdk::transaction::Transaction::new_unsigned(message); let transaction = solana_sdk::transaction::Transaction::new_unsigned(message);
let mut required_derivation_indices = vec![];
// TODO: error handling from_str // TODO: error handling from_str
let from_account = from_account.and_then(|a| u32::from_str(&a).ok()).unwrap_or(0); 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!({ Ok(serde_json::json!({
"blob": transaction, "blob": transaction,
"derivation-accounts": requested_accounts,
})) }))
} }
Operation::Sign(Sign {}) => { Operation::Sign(Sign {}) => {
let blob = request.blob.expect("passed in instruction blob"); let blob = request.blob.expect("passed in instruction blob");
let transaction: solana_sdk::transaction::Transaction = let transaction: solana_sdk::transaction::Transaction =
serde_json::from_value(blob).expect("valid message blob"); serde_json::from_value(blob).expect("valid message blob");
dbg!(transaction); let keys = request.derived_keys.unwrap_or_default();
Ok(serde_json::json!({ Ok(serde_json::json!({
"blob": [] "blob": []
})) }))

View File

@ -6,7 +6,9 @@ edition = "2021"
[dependencies] [dependencies]
clap = { version = "4.5.20", features = ["cargo", "derive", "string"] } clap = { version = "4.5.20", features = ["cargo", "derive", "string"] }
icepick-module = { version = "0.1.0", path = "../icepick-module" } 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-client = { version = "0.2.1", registry = "distrust" }
keyforkd-models = { version = "0.2.0", registry = "distrust" }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true serde_json.workspace = true
thiserror = "2.0.3" thiserror = "2.0.3"

View File

@ -18,7 +18,33 @@ pub fn get_command(bin_name: &str) -> (&str, Vec<&str>) {
struct ModuleConfig { struct ModuleConfig {
name: String, name: String,
command_name: Option<String>, command_name: Option<String>,
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<S>(p: &DerivationPath, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let path = p.to_string();
serializer.serialize_str(&path)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<DerivationPath, D::Error>
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)] #[derive(Serialize, Deserialize, Debug, Default)]
@ -45,7 +71,10 @@ pub fn do_cli_thing() {
// and coin_bin otherwise wouldn't live long enough // and coin_bin otherwise wouldn't live long enough
for module in &config.modules { for module in &config.modules {
let module_name = &module.name; 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 (command, args) = get_command(&bin);
let mut child = Command::new(command) let mut child = Command::new(command)
.args(args) .args(args)
@ -93,6 +122,10 @@ pub fn do_cli_thing() {
} }
let blob = cli_input.as_ref().and_then(|json| json.get("blob")); 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(); let matches = icepick_command.get_matches();
if let Some((module, matches)) = matches.subcommand() { if let Some((module, matches)) = matches.subcommand() {
if let Some((subcommand, matches)) = matches.subcommand() { if let Some((subcommand, matches)) = matches.subcommand() {
@ -107,19 +140,50 @@ pub fn do_cli_thing() {
for arg in &operation.arguments { for arg in &operation.arguments {
args.insert(&arg.name, matches.get_one::<String>(&arg.name)); args.insert(&arg.name, matches.get_one::<String>(&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<u8>> = vec![];
if let Some(accounts) = derivation_accounts {
let accounts: Vec<keyfork_derive_util::DerivationIndex> =
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!({ let json = serde_json::json!({
"operation": subcommand, "operation": subcommand,
"values": args, "values": args,
"derived-keys": [], "derived-keys": derived_keys,
"blob": blob, "blob": blob,
}); });
let bin = commands.iter().find_map(|(fmodule, fcommand, _)| { let bin = commands
.iter()
.find_map(|(fmodule, fcommand, _)| {
if fmodule == module { if fmodule == module {
Some(fcommand) Some(fcommand)
} else { } else {
None None
} }
}).expect("previously found module should exist in new search"); })
.expect("previously found module should exist in new search");
let (command, args) = get_command(bin); let (command, args) = get_command(bin);
let mut child = Command::new(command) let mut child = Command::new(command)
.args(args) .args(args)

View File

@ -1,3 +1,4 @@
[[module]] [[module]]
name = "sol" name = "sol"
derivation_prefix = "m/44'/501'" derivation_prefix = "m/44'/501'"
algorithm = "Ed25519"