From cfa93915cb5c760fa15e73e89021521b00340a88 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sat, 14 Sep 2024 21:24:21 +0400 Subject: [PATCH 01/28] extract signer crate --- Cargo.lock | 26 +++++++++++++-- Cargo.toml | 3 ++ programs/sbf/Cargo.lock | 24 ++++++++++++-- sdk/Cargo.toml | 8 ++--- sdk/signer/Cargo.toml | 33 +++++++++++++++++++ sdk/{src/signer => signer/src}/keypair.rs | 10 ++---- sdk/{src/signer/mod.rs => signer/src/lib.rs} | 12 +++---- sdk/{src/signer => signer/src}/null_signer.rs | 10 +++--- sdk/{src/signer => signer/src}/presigner.rs | 12 +++---- sdk/{src/signer => signer/src}/signers.rs | 9 +++-- sdk/src/lib.rs | 5 ++- 11 files changed, 104 insertions(+), 48 deletions(-) create mode 100644 sdk/signer/Cargo.toml rename sdk/{src/signer => signer/src}/keypair.rs (98%) rename sdk/{src/signer/mod.rs => signer/src/lib.rs} (97%) rename sdk/{src/signer => signer/src}/null_signer.rs (87%) rename sdk/{src/signer => signer/src}/presigner.rs (91%) rename sdk/{src/signer => signer/src}/signers.rs (96%) diff --git a/Cargo.lock b/Cargo.lock index d3b18093e7e78f..3078c7b3359520 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8056,11 +8056,9 @@ dependencies = [ "curve25519-dalek 4.1.3", "digest 0.10.7", "ed25519-dalek", - "ed25519-dalek-bip32", "generic-array 0.14.7", "getrandom 0.1.16", "hex", - "hmac 0.12.1", "itertools 0.12.1", "js-sys", "lazy_static", @@ -8070,7 +8068,6 @@ dependencies = [ "num-derive", "num-traits", "num_enum", - "pbkdf2 0.11.0", "qualifier_attr", "rand 0.7.3", "rand 0.8.5", @@ -8107,6 +8104,7 @@ dependencies = [ "solana-serde-varint", "solana-short-vec", "solana-signature", + "solana-signer", "solana-time-utils", "solana-transaction-error", "static_assertions", @@ -8223,6 +8221,28 @@ dependencies = [ "solana-sanitize", ] +[[package]] +name = "solana-signer" +version = "2.2.0" +dependencies = [ + "bs58", + "ed25519-dalek", + "ed25519-dalek-bip32", + "hmac 0.12.1", + "itertools 0.12.1", + "pbkdf2 0.11.0", + "rand 0.7.3", + "serde_json", + "sha2 0.10.8", + "solana-derivation-path", + "solana-pubkey", + "solana-signature", + "solana-transaction-error", + "static_assertions", + "thiserror", + "tiny-bip39", +] + [[package]] name = "solana-slot-hashes" version = "2.2.0" diff --git a/Cargo.toml b/Cargo.toml index ff38eac7b3ac4e..7a1ded32496c41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,6 +143,7 @@ members = [ "sdk/serialize-utils", "sdk/sha256-hasher", "sdk/signature", + "sdk/signer", "sdk/slot-hashes", "sdk/slot-history", "sdk/stable-layout", @@ -359,6 +360,7 @@ quinn = "0.11.4" quinn-proto = "0.11.7" quote = "1.0" rand = "0.8.5" +rand0-7 = { package = "rand", version = "0.7" } rand_chacha = "0.3.1" rayon = "1.10.0" reed-solomon-erasure = "6.0.0" @@ -488,6 +490,7 @@ solana-serde-varint = { path = "sdk/serde-varint", version = "=2.2.0" } solana-serialize-utils = { path = "sdk/serialize-utils", version = "=2.2.0" } solana-sha256-hasher = { path = "sdk/sha256-hasher", version = "=2.2.0" } solana-signature = { path = "sdk/signature", version = "=2.2.0", default-features = false } +solana-signer = { path = "sdk/signer", version = "=2.2.0" } solana-slot-hashes = { path = "sdk/slot-hashes", version = "=2.2.0" } solana-slot-history = { path = "sdk/slot-history", version = "=2.2.0" } solana-time-utils = { path = "sdk/time-utils", version = "=2.2.0" } diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index ee1c9b2fc8ae09..090252cd775162 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -6807,9 +6807,7 @@ dependencies = [ "chrono", "digest 0.10.7", "ed25519-dalek", - "ed25519-dalek-bip32", "getrandom 0.1.14", - "hmac 0.12.1", "itertools 0.12.1", "js-sys", "lazy_static", @@ -6819,7 +6817,6 @@ dependencies = [ "num-derive", "num-traits", "num_enum", - "pbkdf2 0.11.0", "qualifier_attr", "rand 0.7.3", "rand 0.8.5", @@ -6852,6 +6849,7 @@ dependencies = [ "solana-serde-varint", "solana-short-vec", "solana-signature", + "solana-signer", "solana-time-utils", "solana-transaction-error", "thiserror", @@ -6944,6 +6942,26 @@ dependencies = [ "solana-sanitize", ] +[[package]] +name = "solana-signer" +version = "2.2.0" +dependencies = [ + "bs58", + "ed25519-dalek", + "ed25519-dalek-bip32", + "hmac 0.12.1", + "itertools 0.12.1", + "pbkdf2 0.11.0", + "rand 0.7.3", + "serde_json", + "sha2 0.10.8", + "solana-derivation-path", + "solana-pubkey", + "solana-signature", + "solana-transaction-error", + "thiserror", +] + [[package]] name = "solana-slot-hashes" version = "2.2.0" diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 3d8b1ac1725267..396eba747b281e 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -28,8 +28,8 @@ full = [ "rand0-7", "serde_json", "solana-signature", + "solana-signer", "ed25519-dalek", - "ed25519-dalek-bip32", "libsecp256k1", "sha3", "solana-commitment-config", @@ -65,12 +65,10 @@ chrono = { workspace = true, features = ["alloc"], optional = true } curve25519-dalek = { workspace = true, optional = true } digest = { workspace = true, optional = true } ed25519-dalek = { workspace = true, optional = true } -ed25519-dalek-bip32 = { workspace = true, optional = true } generic-array = { workspace = true, features = [ "serde", "more_lengths", ], optional = true } -hmac = { workspace = true } itertools = { workspace = true } lazy_static = { workspace = true } libsecp256k1 = { workspace = true, optional = true, features = ["hmac"] } @@ -79,10 +77,9 @@ memmap2 = { workspace = true, optional = true } num-derive = { workspace = true } num-traits = { workspace = true } num_enum = { workspace = true } -pbkdf2 = { workspace = true } qualifier_attr = { workspace = true, optional = true } rand = { workspace = true, optional = true } -rand0-7 = { package = "rand", version = "0.7", optional = true } +rand0-7 = { workspace = true, optional = true } serde = { workspace = true } serde_bytes = { workspace = true } serde_derive = { workspace = true } @@ -117,6 +114,7 @@ solana-sdk-macro = { workspace = true } solana-secp256k1-recover = { workspace = true } solana-serde-varint = { workspace = true } solana-short-vec = { workspace = true } +solana-signer = { workspace = true, optional = true } solana-signature = { workspace = true, features = [ "rand", "serde", diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml new file mode 100644 index 00000000000000..0d4612a2c25963 --- /dev/null +++ b/sdk/signer/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "solana-signer" +description = "Solana abstractions and implementations for transaction signers." +documentation = "https://docs.rs/solana-signer" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[dependencies] +bs58 = { workspace = true } +ed25519-dalek = { workspace = true } +ed25519-dalek-bip32 = { workspace = true } +hmac = { workspace = true } +itertools = { workspace = true } +pbkdf2 = { workspace = true } +rand0-7 = { workspace = true } +serde_json = { workspace = true } +sha2 = { workspace = true } +solana-derivation-path = { workspace = true } +solana-pubkey = { workspace = true } +solana-signature = { workspace = true, features = ["verify" ]} +solana-transaction-error = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +static_assertions = { workspace = true } +tiny-bip39 = { workspace = true } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/src/signer/keypair.rs b/sdk/signer/src/keypair.rs similarity index 98% rename from sdk/src/signer/keypair.rs rename to sdk/signer/src/keypair.rs index ecd98ec3c7aa3f..f47529573fde74 100644 --- a/sdk/src/signer/keypair.rs +++ b/sdk/signer/src/keypair.rs @@ -1,18 +1,14 @@ -#![cfg(feature = "full")] - #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; use { - crate::{ - pubkey::Pubkey, - signature::Signature, - signer::{EncodableKey, EncodableKeypair, SeedDerivable, Signer, SignerError}, - }, + crate::{EncodableKey, EncodableKeypair, SeedDerivable, Signer, SignerError}, ed25519_dalek::Signer as DalekSigner, ed25519_dalek_bip32::Error as Bip32Error, hmac::Hmac, rand0_7::{rngs::OsRng, CryptoRng, RngCore}, solana_derivation_path::DerivationPath, + solana_pubkey::Pubkey, + solana_signature::Signature, std::{ error, io::{Read, Write}, diff --git a/sdk/src/signer/mod.rs b/sdk/signer/src/lib.rs similarity index 97% rename from sdk/src/signer/mod.rs rename to sdk/signer/src/lib.rs index ad3ca085cbc52b..dc8c64a33923b5 100644 --- a/sdk/src/signer/mod.rs +++ b/sdk/signer/src/lib.rs @@ -1,14 +1,10 @@ //! Abstractions and implementations for transaction signers. - -#![cfg(feature = "full")] - use { - crate::{ - pubkey::Pubkey, - signature::{PresignerError, Signature}, - }, + crate::presigner::PresignerError, itertools::Itertools, solana_derivation_path::DerivationPath, + solana_pubkey::Pubkey, + solana_signature::Signature, solana_transaction_error::TransactionError, std::{ error, @@ -189,7 +185,7 @@ pub trait EncodableKeypair: EncodableKey { #[cfg(test)] mod tests { - use {super::*, crate::signer::keypair::Keypair}; + use {super::*, crate::keypair::Keypair}; fn pubkeys(signers: &[&dyn Signer]) -> Vec { signers.iter().map(|x| x.pubkey()).collect() diff --git a/sdk/src/signer/null_signer.rs b/sdk/signer/src/null_signer.rs similarity index 87% rename from sdk/src/signer/null_signer.rs rename to sdk/signer/src/null_signer.rs index 2e9508511832fd..b96eea7b89e752 100644 --- a/sdk/src/signer/null_signer.rs +++ b/sdk/signer/src/null_signer.rs @@ -1,9 +1,7 @@ -#![cfg(feature = "full")] - -use crate::{ - pubkey::Pubkey, - signature::Signature, - signer::{Signer, SignerError}, +use { + crate::{Signer, SignerError}, + solana_pubkey::Pubkey, + solana_signature::Signature, }; /// NullSigner - A `Signer` implementation that always produces `Signature::default()`. diff --git a/sdk/src/signer/presigner.rs b/sdk/signer/src/presigner.rs similarity index 91% rename from sdk/src/signer/presigner.rs rename to sdk/signer/src/presigner.rs index 649bd3c3101d95..9e5ca75766073f 100644 --- a/sdk/src/signer/presigner.rs +++ b/sdk/signer/src/presigner.rs @@ -1,11 +1,7 @@ -#![cfg(feature = "full")] - use { - crate::{ - pubkey::Pubkey, - signature::Signature, - signer::{Signer, SignerError}, - }, + crate::{Signer, SignerError}, + solana_pubkey::Pubkey, + solana_signature::Signature, thiserror::Error, }; @@ -63,7 +59,7 @@ where #[cfg(test)] mod tests { - use {super::*, crate::signer::keypair::keypair_from_seed}; + use {super::*, crate::keypair::keypair_from_seed}; #[test] fn test_presigner() { diff --git a/sdk/src/signer/signers.rs b/sdk/signer/src/signers.rs similarity index 96% rename from sdk/src/signer/signers.rs rename to sdk/signer/src/signers.rs index 5b41b5f93717f2..8859bbf59f9046 100644 --- a/sdk/src/signer/signers.rs +++ b/sdk/signer/src/signers.rs @@ -1,8 +1,7 @@ -#![cfg(feature = "full")] - -use crate::{ - pubkey::Pubkey, - signature::{Signature, Signer, SignerError}, +use { + crate::{Signer, SignerError}, + solana_pubkey::Pubkey, + solana_signature::Signature, }; /// Convenience trait for working with mixed collections of `Signer`s diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 4c4ead3010697e..75b8b002a344e9 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -35,10 +35,10 @@ // Allows macro expansion of `use ::solana_sdk::*` to work within this crate extern crate self as solana_sdk; -#[cfg(feature = "full")] -pub use signer::signers; #[cfg(feature = "full")] pub use solana_commitment_config as commitment_config; +#[cfg(feature = "full")] +pub use solana_signer::{self as signer, signers}; #[cfg(not(target_os = "solana"))] pub use solana_program::program_stubs; // These solana_program imports could be *-imported, but that causes a bunch of @@ -94,7 +94,6 @@ pub mod rpc_port; pub mod secp256k1_instruction; pub mod shred_version; pub mod signature; -pub mod signer; pub mod simple_vote_transaction_checker; pub mod system_transaction; pub mod transaction; From 9c65ee00f1336e8dcfb99f72e62ecbb27a04ede7 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 15 Sep 2024 21:17:22 +0400 Subject: [PATCH 02/28] fmt --- sdk/signer/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index 0d4612a2c25963..bc21770a75e91f 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -21,7 +21,7 @@ serde_json = { workspace = true } sha2 = { workspace = true } solana-derivation-path = { workspace = true } solana-pubkey = { workspace = true } -solana-signature = { workspace = true, features = ["verify" ]} +solana-signature = { workspace = true, features = ["verify"] } solana-transaction-error = { workspace = true } thiserror = { workspace = true } From c8fbe04bb60174126337f7f45c0ad933e39eea84 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 15 Sep 2024 22:35:17 +0400 Subject: [PATCH 03/28] fix bs58 std usage --- sdk/signer/Cargo.toml | 2 +- sdk/signer/src/keypair.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index bc21770a75e91f..a6944c839b369b 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -10,7 +10,7 @@ license = { workspace = true } edition = { workspace = true } [dependencies] -bs58 = { workspace = true } +bs58 = { workspace = true, features = ["std"] } ed25519-dalek = { workspace = true } ed25519-dalek-bip32 = { workspace = true } hmac = { workspace = true } diff --git a/sdk/signer/src/keypair.rs b/sdk/signer/src/keypair.rs index f47529573fde74..795b555f4154df 100644 --- a/sdk/signer/src/keypair.rs +++ b/sdk/signer/src/keypair.rs @@ -65,7 +65,9 @@ impl Keypair { /// Recovers a `Keypair` from a base58-encoded string pub fn from_base58_string(s: &str) -> Self { - Self::from_bytes(&bs58::decode(s).into_vec().unwrap()).unwrap() + let mut buf = [0u8; ed25519_dalek::KEYPAIR_LENGTH]; + bs58::decode(s).onto(&mut buf).unwrap(); + Self::from_bytes(&buf).unwrap() } /// Returns this `Keypair` as a base58-encoded string From eefbb8716ea96feba21aba5d9ddea4ea0ab15733 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Mon, 23 Sep 2024 16:33:21 +0400 Subject: [PATCH 04/28] extract seed-derivable crate --- Cargo.lock | 13 +++++-- Cargo.toml | 2 ++ programs/sbf/Cargo.lock | 13 +++++-- sdk/Cargo.toml | 2 ++ sdk/seed-derivable/Cargo.toml | 19 ++++++++++ sdk/seed-derivable/src/lib.rs | 65 +++++++++++++++++++++++++++++++++++ sdk/signer/Cargo.toml | 2 -- sdk/signer/src/keypair.rs | 54 ++++------------------------- sdk/signer/src/lib.rs | 15 -------- sdk/src/lib.rs | 1 + sdk/src/signer/keypair.rs | 1 + sdk/src/signer/mod.rs | 9 +++++ 12 files changed, 128 insertions(+), 68 deletions(-) create mode 100644 sdk/seed-derivable/Cargo.toml create mode 100644 sdk/seed-derivable/src/lib.rs create mode 100644 sdk/src/signer/keypair.rs create mode 100644 sdk/src/signer/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 3078c7b3359520..977194eac6db80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8101,6 +8101,7 @@ dependencies = [ "solana-sdk", "solana-sdk-macro", "solana-secp256k1-recover", + "solana-seed-derivable", "solana-serde-varint", "solana-short-vec", "solana-signature", @@ -8143,6 +8144,16 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" +[[package]] +name = "solana-seed-derivable" +version = "2.2.0" +dependencies = [ + "ed25519-dalek", + "ed25519-dalek-bip32", + "solana-derivation-path", + "solana-signer", +] + [[package]] name = "solana-send-transaction-service" version = "2.2.0" @@ -8227,14 +8238,12 @@ version = "2.2.0" dependencies = [ "bs58", "ed25519-dalek", - "ed25519-dalek-bip32", "hmac 0.12.1", "itertools 0.12.1", "pbkdf2 0.11.0", "rand 0.7.3", "serde_json", "sha2 0.10.8", - "solana-derivation-path", "solana-pubkey", "solana-signature", "solana-transaction-error", diff --git a/Cargo.toml b/Cargo.toml index 7a1ded32496c41..fdb1f481598eb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -139,6 +139,7 @@ members = [ "sdk/pubkey", "sdk/rent", "sdk/sanitize", + "sdk/seed-derivable", "sdk/serde-varint", "sdk/serialize-utils", "sdk/sha256-hasher", @@ -486,6 +487,7 @@ solana-rayon-threadlimit = { path = "rayon-threadlimit", version = "=2.2.0" } solana-remote-wallet = { path = "remote-wallet", version = "=2.2.0", default-features = false } solana-rent = { path = "sdk/rent", version = "=2.2.0", default-features = false } solana-sanitize = { path = "sdk/sanitize", version = "=2.2.0" } +solana-seed-derivable = { path = "sdk/seed-derivable", version = "=2.2.0" } solana-serde-varint = { path = "sdk/serde-varint", version = "=2.2.0" } solana-serialize-utils = { path = "sdk/serialize-utils", version = "=2.2.0" } solana-sha256-hasher = { path = "sdk/sha256-hasher", version = "=2.2.0" } diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 090252cd775162..80f7f49d16d710 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -6846,6 +6846,7 @@ dependencies = [ "solana-sanitize", "solana-sdk-macro", "solana-secp256k1-recover", + "solana-seed-derivable", "solana-serde-varint", "solana-short-vec", "solana-signature", @@ -6882,6 +6883,16 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" +[[package]] +name = "solana-seed-derivable" +version = "2.2.0" +dependencies = [ + "ed25519-dalek", + "ed25519-dalek-bip32", + "solana-derivation-path", + "solana-signer", +] + [[package]] name = "solana-send-transaction-service" version = "2.2.0" @@ -6948,14 +6959,12 @@ version = "2.2.0" dependencies = [ "bs58", "ed25519-dalek", - "ed25519-dalek-bip32", "hmac 0.12.1", "itertools 0.12.1", "pbkdf2 0.11.0", "rand 0.7.3", "serde_json", "sha2 0.10.8", - "solana-derivation-path", "solana-pubkey", "solana-signature", "solana-transaction-error", diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 396eba747b281e..637f093236e5a7 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -36,6 +36,7 @@ full = [ "digest", "solana-pubkey/rand", "dep:solana-precompile-error", + "dep:solana-seed-derivable", "dep:solana-transaction-error" ] borsh = ["dep:borsh", "solana-program/borsh", "solana-secp256k1-recover/borsh"] @@ -114,6 +115,7 @@ solana-sdk-macro = { workspace = true } solana-secp256k1-recover = { workspace = true } solana-serde-varint = { workspace = true } solana-short-vec = { workspace = true } +solana-seed-derivable = { workspace = true, optional = true } solana-signer = { workspace = true, optional = true } solana-signature = { workspace = true, features = [ "rand", diff --git a/sdk/seed-derivable/Cargo.toml b/sdk/seed-derivable/Cargo.toml new file mode 100644 index 00000000000000..2a9cdc5f7fa142 --- /dev/null +++ b/sdk/seed-derivable/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "solana-seed-derivable" +description = "Solana trait defining the interface by which keys are derived." +documentation = "https://docs.rs/solana-seed-derivable" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[dependencies] +ed25519-dalek = { workspace = true } +ed25519-dalek-bip32 = { workspace = true } +solana-derivation-path = { workspace = true } +solana-signer = { workspace = true } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/seed-derivable/src/lib.rs b/sdk/seed-derivable/src/lib.rs new file mode 100644 index 00000000000000..e47eea6d432ba6 --- /dev/null +++ b/sdk/seed-derivable/src/lib.rs @@ -0,0 +1,65 @@ +//! Abstractions and implementations for transaction signers. +use { + ed25519_dalek_bip32::Error as Bip32Error, + solana_derivation_path::DerivationPath, + solana_signer::keypair::{keypair_from_seed, keypair_from_seed_phrase_and_passphrase, Keypair}, + std::error, +}; + +/// The `SeedDerivable` trait defines the interface by which cryptographic keys/keypairs are +/// derived from byte seeds, derivation paths, and passphrases. +pub trait SeedDerivable: Sized { + fn from_seed(seed: &[u8]) -> Result>; + fn from_seed_and_derivation_path( + seed: &[u8], + derivation_path: Option, + ) -> Result>; + fn from_seed_phrase_and_passphrase( + seed_phrase: &str, + passphrase: &str, + ) -> Result>; +} + +impl SeedDerivable for Keypair { + fn from_seed(seed: &[u8]) -> Result> { + keypair_from_seed(seed) + } + + fn from_seed_and_derivation_path( + seed: &[u8], + derivation_path: Option, + ) -> Result> { + keypair_from_seed_and_derivation_path(seed, derivation_path) + } + + fn from_seed_phrase_and_passphrase( + seed_phrase: &str, + passphrase: &str, + ) -> Result> { + keypair_from_seed_phrase_and_passphrase(seed_phrase, passphrase) + } +} + +/// Generates a Keypair using Bip32 Hierarchical Derivation if derivation-path is provided; +/// otherwise generates the base Bip44 Solana keypair from the seed +pub fn keypair_from_seed_and_derivation_path( + seed: &[u8], + derivation_path: Option, +) -> Result> { + let derivation_path = derivation_path.unwrap_or_default(); + bip32_derived_keypair(seed, derivation_path).map_err(|err| err.to_string().into()) +} + +/// Generates a Keypair using Bip32 Hierarchical Derivation +fn bip32_derived_keypair( + seed: &[u8], + derivation_path: DerivationPath, +) -> Result { + let extended = ed25519_dalek_bip32::ExtendedSecretKey::from_seed(seed) + .and_then(|extended| extended.derive(&derivation_path))?; + let extended_public_key = extended.public_key(); + Ok(Keypair::from(ed25519_dalek::Keypair { + secret: extended.secret_key, + public: extended_public_key, + })) +} diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index a6944c839b369b..91b8d747ecb565 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -12,14 +12,12 @@ edition = { workspace = true } [dependencies] bs58 = { workspace = true, features = ["std"] } ed25519-dalek = { workspace = true } -ed25519-dalek-bip32 = { workspace = true } hmac = { workspace = true } itertools = { workspace = true } pbkdf2 = { workspace = true } rand0-7 = { workspace = true } serde_json = { workspace = true } sha2 = { workspace = true } -solana-derivation-path = { workspace = true } solana-pubkey = { workspace = true } solana-signature = { workspace = true, features = ["verify"] } solana-transaction-error = { workspace = true } diff --git a/sdk/signer/src/keypair.rs b/sdk/signer/src/keypair.rs index 795b555f4154df..681c7ede14ad30 100644 --- a/sdk/signer/src/keypair.rs +++ b/sdk/signer/src/keypair.rs @@ -1,12 +1,10 @@ #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; use { - crate::{EncodableKey, EncodableKeypair, SeedDerivable, Signer, SignerError}, + crate::{EncodableKey, EncodableKeypair, Signer, SignerError}, ed25519_dalek::Signer as DalekSigner, - ed25519_dalek_bip32::Error as Bip32Error, hmac::Hmac, rand0_7::{rngs::OsRng, CryptoRng, RngCore}, - solana_derivation_path::DerivationPath, solana_pubkey::Pubkey, solana_signature::Signature, std::{ @@ -96,6 +94,12 @@ impl Keypair { } } +impl From for Keypair { + fn from(value: ed25519_dalek::Keypair) -> Self { + Self(value) + } +} + #[cfg(test)] static_assertions::const_assert_eq!(Keypair::SECRET_KEY_LENGTH, ed25519_dalek::SECRET_KEY_LENGTH); @@ -141,26 +145,6 @@ impl EncodableKey for Keypair { } } -impl SeedDerivable for Keypair { - fn from_seed(seed: &[u8]) -> Result> { - keypair_from_seed(seed) - } - - fn from_seed_and_derivation_path( - seed: &[u8], - derivation_path: Option, - ) -> Result> { - keypair_from_seed_and_derivation_path(seed, derivation_path) - } - - fn from_seed_phrase_and_passphrase( - seed_phrase: &str, - passphrase: &str, - ) -> Result> { - keypair_from_seed_phrase_and_passphrase(seed_phrase, passphrase) - } -} - impl EncodableKeypair for Keypair { type Pubkey = Pubkey; @@ -214,30 +198,6 @@ pub fn keypair_from_seed(seed: &[u8]) -> Result> Ok(Keypair(dalek_keypair)) } -/// Generates a Keypair using Bip32 Hierarchical Derivation if derivation-path is provided; -/// otherwise generates the base Bip44 Solana keypair from the seed -pub fn keypair_from_seed_and_derivation_path( - seed: &[u8], - derivation_path: Option, -) -> Result> { - let derivation_path = derivation_path.unwrap_or_default(); - bip32_derived_keypair(seed, derivation_path).map_err(|err| err.to_string().into()) -} - -/// Generates a Keypair using Bip32 Hierarchical Derivation -fn bip32_derived_keypair( - seed: &[u8], - derivation_path: DerivationPath, -) -> Result { - let extended = ed25519_dalek_bip32::ExtendedSecretKey::from_seed(seed) - .and_then(|extended| extended.derive(&derivation_path))?; - let extended_public_key = extended.public_key(); - Ok(Keypair(ed25519_dalek::Keypair { - secret: extended.secret_key, - public: extended_public_key, - })) -} - pub fn generate_seed_from_seed_phrase_and_passphrase( seed_phrase: &str, passphrase: &str, diff --git a/sdk/signer/src/lib.rs b/sdk/signer/src/lib.rs index dc8c64a33923b5..f4156ef052a139 100644 --- a/sdk/signer/src/lib.rs +++ b/sdk/signer/src/lib.rs @@ -2,7 +2,6 @@ use { crate::presigner::PresignerError, itertools::Itertools, - solana_derivation_path::DerivationPath, solana_pubkey::Pubkey, solana_signature::Signature, solana_transaction_error::TransactionError, @@ -160,20 +159,6 @@ pub trait EncodableKey: Sized { } } -/// The `SeedDerivable` trait defines the interface by which cryptographic keys/keypairs are -/// derived from byte seeds, derivation paths, and passphrases. -pub trait SeedDerivable: Sized { - fn from_seed(seed: &[u8]) -> Result>; - fn from_seed_and_derivation_path( - seed: &[u8], - derivation_path: Option, - ) -> Result>; - fn from_seed_phrase_and_passphrase( - seed_phrase: &str, - passphrase: &str, - ) -> Result>; -} - /// The `EncodableKeypair` trait extends `EncodableKey` for asymmetric keypairs, i.e. have /// associated public keys. pub trait EncodableKeypair: EncodableKey { diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 75b8b002a344e9..c3e7426016ec41 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -94,6 +94,7 @@ pub mod rpc_port; pub mod secp256k1_instruction; pub mod shred_version; pub mod signature; +pub mod signer; pub mod simple_vote_transaction_checker; pub mod system_transaction; pub mod transaction; diff --git a/sdk/src/signer/keypair.rs b/sdk/src/signer/keypair.rs new file mode 100644 index 00000000000000..569935d930bc52 --- /dev/null +++ b/sdk/src/signer/keypair.rs @@ -0,0 +1 @@ +pub use {solana_seed_derivable::keypair_from_seed_and_derivation_path, solana_signer::keypair::*}; diff --git a/sdk/src/signer/mod.rs b/sdk/src/signer/mod.rs new file mode 100644 index 00000000000000..cfbe543da74b1e --- /dev/null +++ b/sdk/src/signer/mod.rs @@ -0,0 +1,9 @@ +#![cfg(feature = "full")] +pub use { + solana_seed_derivable::SeedDerivable, + solana_signer::{ + null_signer, presigner, signers, unique_signers, EncodableKey, EncodableKeypair, Signer, + SignerError, + }, +}; +pub mod keypair; From 50224e52cdeede022b138d61a3062e0357819d82 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Mon, 23 Sep 2024 16:52:42 +0400 Subject: [PATCH 05/28] extract seed-phrase crate --- Cargo.lock | 17 ++++++++--- Cargo.toml | 2 ++ programs/sbf/Cargo.lock | 15 ++++++++-- sdk/Cargo.toml | 2 ++ sdk/seed-derivable/Cargo.toml | 1 + sdk/seed-derivable/src/lib.rs | 3 +- sdk/seed-phrase/Cargo.toml | 22 ++++++++++++++ sdk/seed-phrase/src/lib.rs | 55 +++++++++++++++++++++++++++++++++++ sdk/signer/Cargo.toml | 4 --- sdk/signer/src/keypair.rs | 42 -------------------------- sdk/src/signer/keypair.rs | 8 ++++- 11 files changed, 116 insertions(+), 55 deletions(-) create mode 100644 sdk/seed-phrase/Cargo.toml create mode 100644 sdk/seed-phrase/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 977194eac6db80..f9100c7527f3f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8102,6 +8102,7 @@ dependencies = [ "solana-sdk-macro", "solana-secp256k1-recover", "solana-seed-derivable", + "solana-seed-phrase", "solana-serde-varint", "solana-short-vec", "solana-signature", @@ -8151,9 +8152,21 @@ dependencies = [ "ed25519-dalek", "ed25519-dalek-bip32", "solana-derivation-path", + "solana-seed-phrase", "solana-signer", ] +[[package]] +name = "solana-seed-phrase" +version = "2.2.0" +dependencies = [ + "hmac 0.12.1", + "pbkdf2 0.11.0", + "sha2 0.10.8", + "solana-signer", + "tiny-bip39", +] + [[package]] name = "solana-send-transaction-service" version = "2.2.0" @@ -8238,18 +8251,14 @@ version = "2.2.0" dependencies = [ "bs58", "ed25519-dalek", - "hmac 0.12.1", "itertools 0.12.1", - "pbkdf2 0.11.0", "rand 0.7.3", "serde_json", - "sha2 0.10.8", "solana-pubkey", "solana-signature", "solana-transaction-error", "static_assertions", "thiserror", - "tiny-bip39", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fdb1f481598eb7..440c6f95a12e7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -140,6 +140,7 @@ members = [ "sdk/rent", "sdk/sanitize", "sdk/seed-derivable", + "sdk/seed-phrase", "sdk/serde-varint", "sdk/serialize-utils", "sdk/sha256-hasher", @@ -488,6 +489,7 @@ solana-remote-wallet = { path = "remote-wallet", version = "=2.2.0", default-fea solana-rent = { path = "sdk/rent", version = "=2.2.0", default-features = false } solana-sanitize = { path = "sdk/sanitize", version = "=2.2.0" } solana-seed-derivable = { path = "sdk/seed-derivable", version = "=2.2.0" } +solana-seed-phrase = { path = "sdk/seed-phrase", version = "=2.2.0" } solana-serde-varint = { path = "sdk/serde-varint", version = "=2.2.0" } solana-serialize-utils = { path = "sdk/serialize-utils", version = "=2.2.0" } solana-sha256-hasher = { path = "sdk/sha256-hasher", version = "=2.2.0" } diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 80f7f49d16d710..78cbc7184cbe6e 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -6847,6 +6847,7 @@ dependencies = [ "solana-sdk-macro", "solana-secp256k1-recover", "solana-seed-derivable", + "solana-seed-phrase", "solana-serde-varint", "solana-short-vec", "solana-signature", @@ -6890,6 +6891,17 @@ dependencies = [ "ed25519-dalek", "ed25519-dalek-bip32", "solana-derivation-path", + "solana-seed-phrase", + "solana-signer", +] + +[[package]] +name = "solana-seed-phrase" +version = "2.2.0" +dependencies = [ + "hmac 0.12.1", + "pbkdf2 0.11.0", + "sha2 0.10.8", "solana-signer", ] @@ -6959,12 +6971,9 @@ version = "2.2.0" dependencies = [ "bs58", "ed25519-dalek", - "hmac 0.12.1", "itertools 0.12.1", - "pbkdf2 0.11.0", "rand 0.7.3", "serde_json", - "sha2 0.10.8", "solana-pubkey", "solana-signature", "solana-transaction-error", diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 637f093236e5a7..65e76713dd64eb 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -37,6 +37,7 @@ full = [ "solana-pubkey/rand", "dep:solana-precompile-error", "dep:solana-seed-derivable", + "dep:solana-seed-phrase", "dep:solana-transaction-error" ] borsh = ["dep:borsh", "solana-program/borsh", "solana-secp256k1-recover/borsh"] @@ -116,6 +117,7 @@ solana-secp256k1-recover = { workspace = true } solana-serde-varint = { workspace = true } solana-short-vec = { workspace = true } solana-seed-derivable = { workspace = true, optional = true } +solana-seed-phrase = { workspace = true, optional = true } solana-signer = { workspace = true, optional = true } solana-signature = { workspace = true, features = [ "rand", diff --git a/sdk/seed-derivable/Cargo.toml b/sdk/seed-derivable/Cargo.toml index 2a9cdc5f7fa142..6ece90faddd5da 100644 --- a/sdk/seed-derivable/Cargo.toml +++ b/sdk/seed-derivable/Cargo.toml @@ -13,6 +13,7 @@ edition = { workspace = true } ed25519-dalek = { workspace = true } ed25519-dalek-bip32 = { workspace = true } solana-derivation-path = { workspace = true } +solana-seed-phrase = { workspace = true } solana-signer = { workspace = true } [package.metadata.docs.rs] diff --git a/sdk/seed-derivable/src/lib.rs b/sdk/seed-derivable/src/lib.rs index e47eea6d432ba6..4e8ad17b2c5fde 100644 --- a/sdk/seed-derivable/src/lib.rs +++ b/sdk/seed-derivable/src/lib.rs @@ -2,7 +2,8 @@ use { ed25519_dalek_bip32::Error as Bip32Error, solana_derivation_path::DerivationPath, - solana_signer::keypair::{keypair_from_seed, keypair_from_seed_phrase_and_passphrase, Keypair}, + solana_seed_phrase::keypair_from_seed_phrase_and_passphrase, + solana_signer::keypair::{keypair_from_seed, Keypair}, std::error, }; diff --git a/sdk/seed-phrase/Cargo.toml b/sdk/seed-phrase/Cargo.toml new file mode 100644 index 00000000000000..8e93600a6a66e9 --- /dev/null +++ b/sdk/seed-phrase/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "solana-seed-phrase" +description = "Solana functions for generating keypairs from seed phrases." +documentation = "https://docs.rs/solana-seed-phrase" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[dependencies] +pbkdf2 = { workspace = true } +hmac = { workspace = true } +sha2 = { workspace = true } +solana-signer = { workspace = true } + +[dev-dependencies] +tiny-bip39 = { workspace = true } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/seed-phrase/src/lib.rs b/sdk/seed-phrase/src/lib.rs new file mode 100644 index 00000000000000..91b5e912f63b1d --- /dev/null +++ b/sdk/seed-phrase/src/lib.rs @@ -0,0 +1,55 @@ +//! Functions for generating keypairs from seed phrases. +use { + hmac::Hmac, + solana_signer::keypair::{keypair_from_seed, Keypair}, + std::error, +}; + +pub fn generate_seed_from_seed_phrase_and_passphrase( + seed_phrase: &str, + passphrase: &str, +) -> Vec { + const PBKDF2_ROUNDS: u32 = 2048; + const PBKDF2_BYTES: usize = 64; + + let salt = format!("mnemonic{passphrase}"); + + let mut seed = vec![0u8; PBKDF2_BYTES]; + pbkdf2::pbkdf2::>( + seed_phrase.as_bytes(), + salt.as_bytes(), + PBKDF2_ROUNDS, + &mut seed, + ); + seed +} + +pub fn keypair_from_seed_phrase_and_passphrase( + seed_phrase: &str, + passphrase: &str, +) -> Result> { + keypair_from_seed(&generate_seed_from_seed_phrase_and_passphrase( + seed_phrase, + passphrase, + )) +} + +#[cfg(test)] +mod tests { + use { + super::*, + bip39::{Language, Mnemonic, MnemonicType, Seed}, + solana_signer::Signer, + }; + + #[test] + fn test_keypair_from_seed_phrase_and_passphrase() { + let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); + let passphrase = "42"; + let seed = Seed::new(&mnemonic, passphrase); + let expected_keypair = keypair_from_seed(seed.as_bytes()).unwrap(); + let keypair = + keypair_from_seed_phrase_and_passphrase(mnemonic.phrase(), passphrase).unwrap(); + assert_eq!(keypair.pubkey(), expected_keypair.pubkey()); + } +} diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index 91b8d747ecb565..f20b3de6cb9b8f 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -12,12 +12,9 @@ edition = { workspace = true } [dependencies] bs58 = { workspace = true, features = ["std"] } ed25519-dalek = { workspace = true } -hmac = { workspace = true } itertools = { workspace = true } -pbkdf2 = { workspace = true } rand0-7 = { workspace = true } serde_json = { workspace = true } -sha2 = { workspace = true } solana-pubkey = { workspace = true } solana-signature = { workspace = true, features = ["verify"] } solana-transaction-error = { workspace = true } @@ -25,7 +22,6 @@ thiserror = { workspace = true } [dev-dependencies] static_assertions = { workspace = true } -tiny-bip39 = { workspace = true } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/signer/src/keypair.rs b/sdk/signer/src/keypair.rs index 681c7ede14ad30..3addaf969e504e 100644 --- a/sdk/signer/src/keypair.rs +++ b/sdk/signer/src/keypair.rs @@ -3,7 +3,6 @@ use wasm_bindgen::prelude::*; use { crate::{EncodableKey, EncodableKeypair, Signer, SignerError}, ed25519_dalek::Signer as DalekSigner, - hmac::Hmac, rand0_7::{rngs::OsRng, CryptoRng, RngCore}, solana_pubkey::Pubkey, solana_signature::Signature, @@ -198,40 +197,10 @@ pub fn keypair_from_seed(seed: &[u8]) -> Result> Ok(Keypair(dalek_keypair)) } -pub fn generate_seed_from_seed_phrase_and_passphrase( - seed_phrase: &str, - passphrase: &str, -) -> Vec { - const PBKDF2_ROUNDS: u32 = 2048; - const PBKDF2_BYTES: usize = 64; - - let salt = format!("mnemonic{passphrase}"); - - let mut seed = vec![0u8; PBKDF2_BYTES]; - pbkdf2::pbkdf2::>( - seed_phrase.as_bytes(), - salt.as_bytes(), - PBKDF2_ROUNDS, - &mut seed, - ); - seed -} - -pub fn keypair_from_seed_phrase_and_passphrase( - seed_phrase: &str, - passphrase: &str, -) -> Result> { - keypair_from_seed(&generate_seed_from_seed_phrase_and_passphrase( - seed_phrase, - passphrase, - )) -} - #[cfg(test)] mod tests { use { super::*, - bip39::{Language, Mnemonic, MnemonicType, Seed}, std::{ fs::{self, File}, mem, @@ -314,17 +283,6 @@ mod tests { assert!(keypair_from_seed(&too_short_seed).is_err()); } - #[test] - fn test_keypair_from_seed_phrase_and_passphrase() { - let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); - let passphrase = "42"; - let seed = Seed::new(&mnemonic, passphrase); - let expected_keypair = keypair_from_seed(seed.as_bytes()).unwrap(); - let keypair = - keypair_from_seed_phrase_and_passphrase(mnemonic.phrase(), passphrase).unwrap(); - assert_eq!(keypair.pubkey(), expected_keypair.pubkey()); - } - #[test] fn test_keypair() { let keypair = keypair_from_seed(&[0u8; 32]).unwrap(); diff --git a/sdk/src/signer/keypair.rs b/sdk/src/signer/keypair.rs index 569935d930bc52..f7f8c33fce93b6 100644 --- a/sdk/src/signer/keypair.rs +++ b/sdk/src/signer/keypair.rs @@ -1 +1,7 @@ -pub use {solana_seed_derivable::keypair_from_seed_and_derivation_path, solana_signer::keypair::*}; +pub use { + solana_seed_derivable::keypair_from_seed_and_derivation_path, + solana_seed_phrase::{ + generate_seed_from_seed_phrase_and_passphrase, keypair_from_seed_phrase_and_passphrase, + }, + solana_signer::keypair::*, +}; From d63eb33ceabbb8e303a38515a582330ca786c70a Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Mon, 23 Sep 2024 16:53:33 +0400 Subject: [PATCH 06/28] fix copy-pasted doc --- sdk/seed-derivable/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/seed-derivable/src/lib.rs b/sdk/seed-derivable/src/lib.rs index 4e8ad17b2c5fde..cf2e1ac84be98d 100644 --- a/sdk/seed-derivable/src/lib.rs +++ b/sdk/seed-derivable/src/lib.rs @@ -1,4 +1,4 @@ -//! Abstractions and implementations for transaction signers. +//! The interface by which keys are derived. use { ed25519_dalek_bip32::Error as Bip32Error, solana_derivation_path::DerivationPath, From a8b6c3886e0a6f2a013544528e216f49c54ab255 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Mon, 23 Sep 2024 17:09:08 +0400 Subject: [PATCH 07/28] extract presigner crate --- Cargo.lock | 10 ++++++++++ Cargo.toml | 2 ++ programs/sbf/Cargo.lock | 10 ++++++++++ sdk/Cargo.toml | 2 ++ sdk/presigner/Cargo.toml | 18 ++++++++++++++++++ .../src/presigner.rs => presigner/src/lib.rs} | 12 +++--------- sdk/signer/Cargo.toml | 2 +- sdk/signer/src/lib.rs | 8 ++++++-- sdk/src/signer/mod.rs | 4 ++-- 9 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 sdk/presigner/Cargo.toml rename sdk/{signer/src/presigner.rs => presigner/src/lib.rs} (88%) diff --git a/Cargo.lock b/Cargo.lock index f9100c7527f3f0..43c9703dc05886 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7432,6 +7432,15 @@ dependencies = [ "solana-decode-error", ] +[[package]] +name = "solana-presigner" +version = "2.2.0" +dependencies = [ + "solana-pubkey", + "solana-signature", + "solana-signer", +] + [[package]] name = "solana-program" version = "2.2.0" @@ -8094,6 +8103,7 @@ dependencies = [ "solana-native-token", "solana-packet", "solana-precompile-error", + "solana-presigner", "solana-program", "solana-program-memory", "solana-pubkey", diff --git a/Cargo.toml b/Cargo.toml index 440c6f95a12e7e..f62bed232686dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -130,6 +130,7 @@ members = [ "sdk/package-metadata-macro", "sdk/packet", "sdk/precompile-error", + "sdk/presigner", "sdk/program", "sdk/program-entrypoint", "sdk/program-error", @@ -474,6 +475,7 @@ solana-perf = { path = "perf", version = "=2.2.0" } solana-poh = { path = "poh", version = "=2.2.0" } solana-poseidon = { path = "poseidon", version = "=2.2.0" } solana-precompile-error = { path = "sdk/precompile-error", version = "=2.2.0" } +solana-presigner = { path = "sdk/presigner", version = "=2.2.0" } solana-program = { path = "sdk/program", version = "=2.2.0", default-features = false } solana-program-error = { path = "sdk/program-error", version = "=2.2.0" } solana-program-memory = { path = "sdk/program-memory", version = "=2.2.0" } diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 78cbc7184cbe6e..7f2d4ec49f6168 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -5803,6 +5803,15 @@ dependencies = [ "solana-decode-error", ] +[[package]] +name = "solana-presigner" +version = "2.2.0" +dependencies = [ + "solana-pubkey", + "solana-signature", + "solana-signer", +] + [[package]] name = "solana-program" version = "2.2.0" @@ -6840,6 +6849,7 @@ dependencies = [ "solana-native-token", "solana-packet", "solana-precompile-error", + "solana-presigner", "solana-program", "solana-program-memory", "solana-pubkey", diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 65e76713dd64eb..f09832eace7e15 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -36,6 +36,7 @@ full = [ "digest", "solana-pubkey/rand", "dep:solana-precompile-error", + "dep:solana-presigner", "dep:solana-seed-derivable", "dep:solana-seed-phrase", "dep:solana-transaction-error" @@ -108,6 +109,7 @@ solana-instruction = { workspace = true } solana-native-token = { workspace = true } solana-packet = { workspace = true, features = ["bincode", "serde"] } solana-precompile-error = { workspace = true, optional = true } +solana-presigner = { workspace = true, optional = true } solana-program = { workspace = true } solana-program-memory = { workspace = true } solana-pubkey = { workspace = true, default-features = false, features = ["std"] } diff --git a/sdk/presigner/Cargo.toml b/sdk/presigner/Cargo.toml new file mode 100644 index 00000000000000..ae5099a8708448 --- /dev/null +++ b/sdk/presigner/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "solana-presigner" +description = "A Solana `Signer` implementation representing an externally-constructed `Signature`." +documentation = "https://docs.rs/solana-presigner" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[dependencies] +solana-pubkey = { workspace = true } +solana-signature = { workspace = true, features = ["verify"] } +solana-signer = { workspace = true } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/signer/src/presigner.rs b/sdk/presigner/src/lib.rs similarity index 88% rename from sdk/signer/src/presigner.rs rename to sdk/presigner/src/lib.rs index 9e5ca75766073f..1b2ea5e34ee362 100644 --- a/sdk/signer/src/presigner.rs +++ b/sdk/presigner/src/lib.rs @@ -1,8 +1,8 @@ +pub use solana_signer::PresignerError; use { - crate::{Signer, SignerError}, solana_pubkey::Pubkey, solana_signature::Signature, - thiserror::Error, + solana_signer::{Signer, SignerError}, }; /// A `Signer` implementation that represents a `Signature` that has been @@ -24,12 +24,6 @@ impl Presigner { } } -#[derive(Debug, Error, PartialEq, Eq)] -pub enum PresignerError { - #[error("pre-generated signature cannot verify data")] - VerificationFailure, -} - impl Signer for Presigner { fn try_pubkey(&self) -> Result { Ok(self.pubkey) @@ -59,7 +53,7 @@ where #[cfg(test)] mod tests { - use {super::*, crate::keypair::keypair_from_seed}; + use {super::*, solana_signer::keypair::keypair_from_seed}; #[test] fn test_presigner() { diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index f20b3de6cb9b8f..763da29aba299c 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -16,7 +16,7 @@ itertools = { workspace = true } rand0-7 = { workspace = true } serde_json = { workspace = true } solana-pubkey = { workspace = true } -solana-signature = { workspace = true, features = ["verify"] } +solana-signature = { workspace = true } solana-transaction-error = { workspace = true } thiserror = { workspace = true } diff --git a/sdk/signer/src/lib.rs b/sdk/signer/src/lib.rs index f4156ef052a139..70af41fc86e2e6 100644 --- a/sdk/signer/src/lib.rs +++ b/sdk/signer/src/lib.rs @@ -1,6 +1,5 @@ //! Abstractions and implementations for transaction signers. use { - crate::presigner::PresignerError, itertools::Itertools, solana_pubkey::Pubkey, solana_signature::Signature, @@ -17,9 +16,14 @@ use { pub mod keypair; pub mod null_signer; -pub mod presigner; pub mod signers; +#[derive(Debug, Error, PartialEq, Eq)] +pub enum PresignerError { + #[error("pre-generated signature cannot verify data")] + VerificationFailure, +} + #[derive(Debug, Error, PartialEq, Eq)] pub enum SignerError { #[error("keypair-pubkey mismatch")] diff --git a/sdk/src/signer/mod.rs b/sdk/src/signer/mod.rs index cfbe543da74b1e..36b789741d80d8 100644 --- a/sdk/src/signer/mod.rs +++ b/sdk/src/signer/mod.rs @@ -1,9 +1,9 @@ #![cfg(feature = "full")] pub use { + solana_presigner as presigner, solana_seed_derivable::SeedDerivable, solana_signer::{ - null_signer, presigner, signers, unique_signers, EncodableKey, EncodableKeypair, Signer, - SignerError, + null_signer, signers, unique_signers, EncodableKey, EncodableKeypair, Signer, SignerError, }, }; pub mod keypair; From 7b55f262454ff54e7f85e3f47eee75c20559053b Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Mon, 23 Sep 2024 21:19:28 +0400 Subject: [PATCH 08/28] make keypair module optional in solana-signer --- sdk/Cargo.toml | 2 +- sdk/seed-phrase/Cargo.toml | 2 +- sdk/signer/Cargo.toml | 16 ++++++++++++---- sdk/signer/src/lib.rs | 1 + 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index f09832eace7e15..16e587a3c8f9f5 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -120,7 +120,7 @@ solana-serde-varint = { workspace = true } solana-short-vec = { workspace = true } solana-seed-derivable = { workspace = true, optional = true } solana-seed-phrase = { workspace = true, optional = true } -solana-signer = { workspace = true, optional = true } +solana-signer = { workspace = true, optional = true, features = ["keypair"] } solana-signature = { workspace = true, features = [ "rand", "serde", diff --git a/sdk/seed-phrase/Cargo.toml b/sdk/seed-phrase/Cargo.toml index 8e93600a6a66e9..3df8d8369b5422 100644 --- a/sdk/seed-phrase/Cargo.toml +++ b/sdk/seed-phrase/Cargo.toml @@ -13,7 +13,7 @@ edition = { workspace = true } pbkdf2 = { workspace = true } hmac = { workspace = true } sha2 = { workspace = true } -solana-signer = { workspace = true } +solana-signer = { workspace = true, features = ["keypair"] } [dev-dependencies] tiny-bip39 = { workspace = true } diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index 763da29aba299c..493df01d023a62 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -10,11 +10,11 @@ license = { workspace = true } edition = { workspace = true } [dependencies] -bs58 = { workspace = true, features = ["std"] } -ed25519-dalek = { workspace = true } +bs58 = { workspace = true, features = ["std"], optional = true } +ed25519-dalek = { workspace = true, optional = true } itertools = { workspace = true } -rand0-7 = { workspace = true } -serde_json = { workspace = true } +rand0-7 = { workspace = true, optional = true } +serde_json = { workspace = true, optional = true } solana-pubkey = { workspace = true } solana-signature = { workspace = true } solana-transaction-error = { workspace = true } @@ -23,5 +23,13 @@ thiserror = { workspace = true } [dev-dependencies] static_assertions = { workspace = true } +[features] +keypair = [ + "dep:bs58", + "dep:ed25519-dalek", + "dep:rand0-7", + "dep:serde_json" +] + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/signer/src/lib.rs b/sdk/signer/src/lib.rs index 70af41fc86e2e6..dfaf685fd5638d 100644 --- a/sdk/signer/src/lib.rs +++ b/sdk/signer/src/lib.rs @@ -14,6 +14,7 @@ use { thiserror::Error, }; +#[cfg(feature = "keypair")] pub mod keypair; pub mod null_signer; pub mod signers; From dc57f376cefd96ba62fbcfecab6c47afef77576d Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Tue, 24 Sep 2024 10:33:51 +0400 Subject: [PATCH 09/28] remove serde_json dep and fix test features --- Cargo.lock | 1 + programs/sbf/Cargo.lock | 1 - sdk/signer/Cargo.toml | 5 ++-- sdk/signer/src/keypair.rs | 53 +++++++++++++++++++++++++++++++++++---- 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43c9703dc05886..a7b4011cbe748d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8266,6 +8266,7 @@ dependencies = [ "serde_json", "solana-pubkey", "solana-signature", + "solana-signer", "solana-transaction-error", "static_assertions", "thiserror", diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 7f2d4ec49f6168..2451677e95f8ae 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -6983,7 +6983,6 @@ dependencies = [ "ed25519-dalek", "itertools 0.12.1", "rand 0.7.3", - "serde_json", "solana-pubkey", "solana-signature", "solana-transaction-error", diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index 493df01d023a62..fe40a99b67ae6a 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -14,21 +14,22 @@ bs58 = { workspace = true, features = ["std"], optional = true } ed25519-dalek = { workspace = true, optional = true } itertools = { workspace = true } rand0-7 = { workspace = true, optional = true } -serde_json = { workspace = true, optional = true } solana-pubkey = { workspace = true } solana-signature = { workspace = true } solana-transaction-error = { workspace = true } thiserror = { workspace = true } [dev-dependencies] +serde_json = { workspace = true } +solana-signer = { path = ".", features = ["dev-context-only-utils"] } static_assertions = { workspace = true } [features] +dev-context-only-utils = ["keypair"] keypair = [ "dep:bs58", "dep:ed25519-dalek", "dep:rand0-7", - "dep:serde_json" ] [package.metadata.docs.rs] diff --git a/sdk/signer/src/keypair.rs b/sdk/signer/src/keypair.rs index 3addaf969e504e..a8cbde108aeb16 100644 --- a/sdk/signer/src/keypair.rs +++ b/sdk/signer/src/keypair.rs @@ -156,8 +156,36 @@ impl EncodableKeypair for Keypair { /// Reads a JSON-encoded `Keypair` from a `Reader` implementor pub fn read_keypair(reader: &mut R) -> Result> { - let bytes: Vec = serde_json::from_reader(reader)?; - Keypair::from_bytes(&bytes) + let mut buffer = String::new(); + reader.read_to_string(&mut buffer)?; + let trimmed = buffer.trim(); + if !trimmed.starts_with('[') || !trimmed.ends_with(']') { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Input must be a JSON array", + ) + .into()); + } + let contents = &trimmed[1..trimmed.len() - 1]; + let elements_vec: Vec<&str> = contents.split(',').map(|s| s.trim()).collect(); + let len = elements_vec.len(); + let elements: [&str; ed25519_dalek::KEYPAIR_LENGTH] = + elements_vec.try_into().map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!( + "Expected {} elements, found {}", + ed25519_dalek::KEYPAIR_LENGTH, + len + ), + ) + })?; + let mut out = [0u8; ed25519_dalek::KEYPAIR_LENGTH]; + for (idx, element) in elements.into_iter().enumerate() { + let parsed: u8 = element.parse()?; + out[idx] = parsed; + } + Keypair::from_bytes(&out) .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()).into()) } @@ -172,9 +200,24 @@ pub fn write_keypair( writer: &mut W, ) -> Result> { let keypair_bytes = keypair.0.to_bytes(); - let serialized = serde_json::to_string(&keypair_bytes.to_vec())?; - writer.write_all(serialized.as_bytes())?; - Ok(serialized) + let mut result = Vec::with_capacity(64 * 4 + 2); // Estimate capacity: 64 numbers * (up to 3 digits + 1 comma) + 2 brackets + + result.push(b'['); // Opening bracket + + for (i, &num) in keypair_bytes.iter().enumerate() { + if i > 0 { + result.push(b','); // Comma separator for all elements except the first + } + + // Convert number to string and then to bytes + let num_str = num.to_string(); + result.extend_from_slice(num_str.as_bytes()); + } + + result.push(b']'); // Closing bracket + writer.write_all(&result)?; + let as_string = String::from_utf8(result)?; + Ok(as_string) } /// Writes a `Keypair` to a file with JSON-encoding From 82d5af74efeff47bb1429aa9d66c473b7b4f4f1d Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 13:50:13 +0400 Subject: [PATCH 10/28] remove thiserror from solana-signer --- Cargo.lock | 1 - programs/sbf/Cargo.lock | 1 - sdk/signer/Cargo.toml | 1 - sdk/signer/src/lib.rs | 93 +++++++++++++++++++++++++++++------------ 4 files changed, 66 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7b4011cbe748d..ff2ce87a42f592 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8269,7 +8269,6 @@ dependencies = [ "solana-signer", "solana-transaction-error", "static_assertions", - "thiserror", ] [[package]] diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 2451677e95f8ae..de5ceecb3519c0 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -6986,7 +6986,6 @@ dependencies = [ "solana-pubkey", "solana-signature", "solana-transaction-error", - "thiserror", ] [[package]] diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index fe40a99b67ae6a..2914a709a03ff2 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -17,7 +17,6 @@ rand0-7 = { workspace = true, optional = true } solana-pubkey = { workspace = true } solana-signature = { workspace = true } solana-transaction-error = { workspace = true } -thiserror = { workspace = true } [dev-dependencies] serde_json = { workspace = true } diff --git a/sdk/signer/src/lib.rs b/sdk/signer/src/lib.rs index dfaf685fd5638d..a01d0d31f0748f 100644 --- a/sdk/signer/src/lib.rs +++ b/sdk/signer/src/lib.rs @@ -1,5 +1,6 @@ //! Abstractions and implementations for transaction signers. use { + core::fmt, itertools::Itertools, solana_pubkey::Pubkey, solana_signature::Signature, @@ -11,7 +12,6 @@ use { ops::Deref, path::Path, }, - thiserror::Error, }; #[cfg(feature = "keypair")] @@ -19,50 +19,89 @@ pub mod keypair; pub mod null_signer; pub mod signers; -#[derive(Debug, Error, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub enum PresignerError { - #[error("pre-generated signature cannot verify data")] VerificationFailure, } -#[derive(Debug, Error, PartialEq, Eq)] +impl std::error::Error for PresignerError {} + +impl fmt::Display for PresignerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::VerificationFailure => f.write_str("pre-generated signature cannot verify data"), + } + } +} + +#[derive(Debug, PartialEq, Eq)] pub enum SignerError { - #[error("keypair-pubkey mismatch")] KeypairPubkeyMismatch, - - #[error("not enough signers")] NotEnoughSigners, - - #[error("transaction error")] - TransactionError(#[from] TransactionError), - - #[error("custom error: {0}")] + TransactionError(TransactionError), Custom(String), - // Presigner-specific Errors - #[error("presigner error")] - PresignerError(#[from] PresignerError), - + PresignerError(PresignerError), // Remote Keypair-specific Errors - #[error("connection error: {0}")] Connection(String), - - #[error("invalid input: {0}")] InvalidInput(String), - - #[error("no device found")] NoDeviceFound, - - #[error("{0}")] Protocol(String), - - #[error("{0}")] UserCancel(String), - - #[error("too many signers")] TooManySigners, } +impl std::error::Error for SignerError { + fn source(&self) -> ::core::option::Option<&(dyn std::error::Error + 'static)> { + match self { + Self::KeypairPubkeyMismatch => None, + Self::NotEnoughSigners => None, + Self::TransactionError(e) => Some(e), + Self::Custom(_) => None, + Self::PresignerError(e) => Some(e), + Self::Connection(_) => None, + Self::InvalidInput(_) => None, + Self::NoDeviceFound => None, + Self::Protocol(_) => None, + Self::UserCancel(_) => None, + Self::TooManySigners => None, + } + } +} +impl fmt::Display for SignerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SignerError::KeypairPubkeyMismatch => f.write_str("keypair-pubkey mismatch"), + SignerError::NotEnoughSigners => f.write_str("not enough signers"), + SignerError::TransactionError(_0) => f.write_str("transaction error"), + SignerError::Custom(e) => write!(f, "custom error: {e}",), + SignerError::PresignerError(_0) => f.write_str("presigner error"), + SignerError::Connection(e) => write!(f, "connection error: {e}",), + SignerError::InvalidInput(s) => write!(f, "invalid input: {s}",), + SignerError::NoDeviceFound => f.write_str("no device found"), + SignerError::Protocol(s) => { + write!(f, "{s}") + } + SignerError::UserCancel(s) => { + write!(f, "{s}") + } + SignerError::TooManySigners => f.write_str("too many signers"), + } + } +} + +impl From for SignerError { + fn from(source: TransactionError) -> Self { + SignerError::TransactionError(source) + } +} + +impl From for SignerError { + fn from(source: PresignerError) -> Self { + SignerError::PresignerError(source) + } +} + /// The `Signer` trait declares operations that all digital signature providers /// must support. It is the primary interface by which signers are specified in /// `Transaction` signing interfaces From 91031a068be790d69751bc0bca7722968ed43f35 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 15:35:12 +0400 Subject: [PATCH 11/28] lint --- sdk/signer/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/signer/src/lib.rs b/sdk/signer/src/lib.rs index a01d0d31f0748f..1ea4f22de041e6 100644 --- a/sdk/signer/src/lib.rs +++ b/sdk/signer/src/lib.rs @@ -73,9 +73,9 @@ impl fmt::Display for SignerError { match self { SignerError::KeypairPubkeyMismatch => f.write_str("keypair-pubkey mismatch"), SignerError::NotEnoughSigners => f.write_str("not enough signers"), - SignerError::TransactionError(_0) => f.write_str("transaction error"), + SignerError::TransactionError(_) => f.write_str("transaction error"), SignerError::Custom(e) => write!(f, "custom error: {e}",), - SignerError::PresignerError(_0) => f.write_str("presigner error"), + SignerError::PresignerError(_) => f.write_str("presigner error"), SignerError::Connection(e) => write!(f, "connection error: {e}",), SignerError::InvalidInput(s) => write!(f, "invalid input: {s}",), SignerError::NoDeviceFound => f.write_str("no device found"), From 10d59cd529b12c7ca73038d53a0212984f143e0e Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 15:44:36 +0400 Subject: [PATCH 12/28] more lint --- sdk/signer/src/keypair.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk/signer/src/keypair.rs b/sdk/signer/src/keypair.rs index a8cbde108aeb16..f4012563c3ed76 100644 --- a/sdk/signer/src/keypair.rs +++ b/sdk/signer/src/keypair.rs @@ -166,6 +166,9 @@ pub fn read_keypair(reader: &mut R) -> Result = contents.split(',').map(|s| s.trim()).collect(); let len = elements_vec.len(); From e27c28d5640d57df4ba9d9b6bd0e1b3159c8c6da Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 15:45:34 +0400 Subject: [PATCH 13/28] fix dev dep feature --- sdk/presigner/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk/presigner/Cargo.toml b/sdk/presigner/Cargo.toml index ae5099a8708448..14a7cb09a26e37 100644 --- a/sdk/presigner/Cargo.toml +++ b/sdk/presigner/Cargo.toml @@ -14,5 +14,8 @@ solana-pubkey = { workspace = true } solana-signature = { workspace = true, features = ["verify"] } solana-signer = { workspace = true } +[dev-dependencies] +solana-signer = { workspace = true, features = ["keypair"] } + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] From 3920ad92be1e5c22e43c5c8c1cbee305373568c4 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 15:52:20 +0400 Subject: [PATCH 14/28] sort deps --- sdk/Cargo.toml | 6 +++--- sdk/seed-phrase/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 16e587a3c8f9f5..43817ef958c792 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -116,17 +116,17 @@ solana-pubkey = { workspace = true, default-features = false, features = ["std"] solana-sanitize = { workspace = true } solana-sdk-macro = { workspace = true } solana-secp256k1-recover = { workspace = true } -solana-serde-varint = { workspace = true } -solana-short-vec = { workspace = true } solana-seed-derivable = { workspace = true, optional = true } solana-seed-phrase = { workspace = true, optional = true } -solana-signer = { workspace = true, optional = true, features = ["keypair"] } +solana-serde-varint = { workspace = true } +solana-short-vec = { workspace = true } solana-signature = { workspace = true, features = [ "rand", "serde", "std", "verify", ], optional = true } +solana-signer = { workspace = true, optional = true, features = ["keypair"] } solana-time-utils = { workspace = true } solana-transaction-error = { workspace = true, features = ["serde"], optional = true } thiserror = { workspace = true } diff --git a/sdk/seed-phrase/Cargo.toml b/sdk/seed-phrase/Cargo.toml index 3df8d8369b5422..a54c7bf64589f1 100644 --- a/sdk/seed-phrase/Cargo.toml +++ b/sdk/seed-phrase/Cargo.toml @@ -10,8 +10,8 @@ license = { workspace = true } edition = { workspace = true } [dependencies] -pbkdf2 = { workspace = true } hmac = { workspace = true } +pbkdf2 = { workspace = true } sha2 = { workspace = true } solana-signer = { workspace = true, features = ["keypair"] } From fd8a47a49761604f93a8250a0a8d741bb57a2730 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 17:45:04 +0400 Subject: [PATCH 15/28] remove itertools from solana-signer --- Cargo.lock | 1 - programs/sbf/Cargo.lock | 1 - sdk/signer/Cargo.toml | 1 - sdk/signer/src/lib.rs | 13 +++++++++++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff2ce87a42f592..74c929dc236e10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8261,7 +8261,6 @@ version = "2.2.0" dependencies = [ "bs58", "ed25519-dalek", - "itertools 0.12.1", "rand 0.7.3", "serde_json", "solana-pubkey", diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index de5ceecb3519c0..321cf3ca55c63a 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -6981,7 +6981,6 @@ version = "2.2.0" dependencies = [ "bs58", "ed25519-dalek", - "itertools 0.12.1", "rand 0.7.3", "solana-pubkey", "solana-signature", diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index 2914a709a03ff2..516d951130db03 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -12,7 +12,6 @@ edition = { workspace = true } [dependencies] bs58 = { workspace = true, features = ["std"], optional = true } ed25519-dalek = { workspace = true, optional = true } -itertools = { workspace = true } rand0-7 = { workspace = true, optional = true } solana-pubkey = { workspace = true } solana-signature = { workspace = true } diff --git a/sdk/signer/src/lib.rs b/sdk/signer/src/lib.rs index 1ea4f22de041e6..37410c0cb233d2 100644 --- a/sdk/signer/src/lib.rs +++ b/sdk/signer/src/lib.rs @@ -1,7 +1,6 @@ //! Abstractions and implementations for transaction signers. use { core::fmt, - itertools::Itertools, solana_pubkey::Pubkey, solana_signature::Signature, solana_transaction_error::TransactionError, @@ -164,7 +163,17 @@ impl std::fmt::Debug for dyn Signer { /// Removes duplicate signers while preserving order. O(n²) pub fn unique_signers(signers: Vec<&dyn Signer>) -> Vec<&dyn Signer> { - signers.into_iter().unique_by(|s| s.pubkey()).collect() + let capacity = signers.len(); + let mut out = Vec::with_capacity(capacity); + let mut seen = std::collections::HashSet::with_capacity(capacity); + for signer in signers { + let pubkey = signer.pubkey(); + if !seen.contains(&pubkey) { + seen.insert(pubkey); + out.push(signer); + } + } + out } /// The `EncodableKey` trait defines the interface by which cryptographic keys/keypairs are read, From 144ec3c5d65318af1718a153ed2cb3a2cd59c2f7 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 19:08:21 +0400 Subject: [PATCH 16/28] move wasm impl to signer crate --- Cargo.lock | 1 + programs/sbf/Cargo.lock | 1 + sdk/signer/Cargo.toml | 3 +++ sdk/signer/src/keypair.rs | 28 ++++++++++++++++++++++++++++ sdk/src/wasm/keypair.rs | 37 +++---------------------------------- 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74c929dc236e10..1f831204fe7c8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8268,6 +8268,7 @@ dependencies = [ "solana-signer", "solana-transaction-error", "static_assertions", + "wasm-bindgen", ] [[package]] diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 321cf3ca55c63a..21b6eff97764b0 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -6985,6 +6985,7 @@ dependencies = [ "solana-pubkey", "solana-signature", "solana-transaction-error", + "wasm-bindgen", ] [[package]] diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index 516d951130db03..37df95d72d77b7 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -17,6 +17,9 @@ solana-pubkey = { workspace = true } solana-signature = { workspace = true } solana-transaction-error = { workspace = true } +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = { workspace = true } + [dev-dependencies] serde_json = { workspace = true } solana-signer = { path = ".", features = ["dev-context-only-utils"] } diff --git a/sdk/signer/src/keypair.rs b/sdk/signer/src/keypair.rs index f4012563c3ed76..f0062a9f2c135b 100644 --- a/sdk/signer/src/keypair.rs +++ b/sdk/signer/src/keypair.rs @@ -93,6 +93,34 @@ impl Keypair { } } +#[cfg(target_arch = "wasm32")] +#[allow(non_snake_case)] +#[wasm_bindgen] +impl Keypair { + /// Create a new `Keypair ` + #[wasm_bindgen(constructor)] + pub fn constructor() -> Keypair { + Keypair::new() + } + + /// Convert a `Keypair` to a `Uint8Array` + pub fn toBytes(&self) -> Box<[u8]> { + self.to_bytes().into() + } + + /// Recover a `Keypair` from a `Uint8Array` + pub fn fromBytes(bytes: &[u8]) -> Result { + Keypair::from_bytes(bytes).map_err(|e| e.to_string().into()) + } + + /// Return the `Pubkey` for this `Keypair` + #[wasm_bindgen(js_name = pubkey)] + pub fn js_pubkey(&self) -> Pubkey { + // `wasm_bindgen` does not support traits (`Signer) yet + self.pubkey() + } +} + impl From for Keypair { fn from(value: ed25519_dalek::Keypair) -> Self { Self(value) diff --git a/sdk/src/wasm/keypair.rs b/sdk/src/wasm/keypair.rs index 6f2ffebbb7ccf5..5c5da471a7878a 100644 --- a/sdk/src/wasm/keypair.rs +++ b/sdk/src/wasm/keypair.rs @@ -1,34 +1,3 @@ -//! `Keypair` Javascript interface -#![cfg(target_arch = "wasm32")] -#![allow(non_snake_case)] -use { - crate::signer::{keypair::Keypair, Signer}, - solana_program::{pubkey::Pubkey, wasm::display_to_jsvalue}, - wasm_bindgen::prelude::*, -}; - -#[wasm_bindgen] -impl Keypair { - /// Create a new `Keypair ` - #[wasm_bindgen(constructor)] - pub fn constructor() -> Keypair { - Keypair::new() - } - - /// Convert a `Keypair` to a `Uint8Array` - pub fn toBytes(&self) -> Box<[u8]> { - self.to_bytes().into() - } - - /// Recover a `Keypair` from a `Uint8Array` - pub fn fromBytes(bytes: &[u8]) -> Result { - Keypair::from_bytes(bytes).map_err(display_to_jsvalue) - } - - /// Return the `Pubkey` for this `Keypair` - #[wasm_bindgen(js_name = pubkey)] - pub fn js_pubkey(&self) -> Pubkey { - // `wasm_bindgen` does not support traits (`Signer) yet - self.pubkey() - } -} +//! This module is empty but has not yet been removed because that would +//! technically be a breaking change. There was never anything to import +//! from here. From 4252bb7ad5e9405ace65f3cd997b12810e2ef5e2 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sat, 12 Oct 2024 14:14:06 +0400 Subject: [PATCH 17/28] add doc_auto_cfg like in #3121 --- sdk/signer/Cargo.toml | 2 ++ sdk/signer/src/lib.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index 37df95d72d77b7..3cb35b92755bcb 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -35,3 +35,5 @@ keypair = [ [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] +all-features = true +rustdoc-args = ["--cfg=docsrs"] diff --git a/sdk/signer/src/lib.rs b/sdk/signer/src/lib.rs index 37410c0cb233d2..06aa4ddf4c1276 100644 --- a/sdk/signer/src/lib.rs +++ b/sdk/signer/src/lib.rs @@ -1,4 +1,5 @@ //! Abstractions and implementations for transaction signers. +#![cfg_attr(docsrs, feature(doc_auto_cfg))] use { core::fmt, solana_pubkey::Pubkey, From 017783b71ad847aec2d26fc2d5d70e2fba6c6418 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Fri, 1 Nov 2024 12:33:52 +0400 Subject: [PATCH 18/28] post-rebase fix --- sdk/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index c3e7426016ec41..22d8dfc8478244 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -38,7 +38,7 @@ extern crate self as solana_sdk; #[cfg(feature = "full")] pub use solana_commitment_config as commitment_config; #[cfg(feature = "full")] -pub use solana_signer::{self as signer, signers}; +pub use solana_signer::signers; #[cfg(not(target_os = "solana"))] pub use solana_program::program_stubs; // These solana_program imports could be *-imported, but that causes a bunch of @@ -94,7 +94,6 @@ pub mod rpc_port; pub mod secp256k1_instruction; pub mod shred_version; pub mod signature; -pub mod signer; pub mod simple_vote_transaction_checker; pub mod system_transaction; pub mod transaction; From bc8fe70ae9fc4553e82910902f0610bc55752dc5 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Fri, 1 Nov 2024 12:39:42 +0400 Subject: [PATCH 19/28] another post-rebase fix --- sdk/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 22d8dfc8478244..92b94317820599 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -94,6 +94,7 @@ pub mod rpc_port; pub mod secp256k1_instruction; pub mod shred_version; pub mod signature; +pub mod signer; pub mod simple_vote_transaction_checker; pub mod system_transaction; pub mod transaction; From 5f3b78707d18eed66462249906d25a6ae268e3cb Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Fri, 1 Nov 2024 12:46:22 +0400 Subject: [PATCH 20/28] fmt --- sdk/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 92b94317820599..323e0ba1d06352 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -37,8 +37,6 @@ extern crate self as solana_sdk; #[cfg(feature = "full")] pub use solana_commitment_config as commitment_config; -#[cfg(feature = "full")] -pub use solana_signer::signers; #[cfg(not(target_os = "solana"))] pub use solana_program::program_stubs; // These solana_program imports could be *-imported, but that causes a bunch of @@ -60,6 +58,8 @@ pub use solana_program::{ }; #[cfg(feature = "borsh")] pub use solana_program::{borsh, borsh0_10, borsh1}; +#[cfg(feature = "full")] +pub use solana_signer::signers; pub mod client; pub mod compute_budget; pub mod deserialize_utils; From c5cbeb64bc6a80e39cbb7d2547676635e53f477c Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Fri, 1 Nov 2024 13:53:05 +0400 Subject: [PATCH 21/28] make keypair functionality of solana-seed-phrase optional --- sdk/seed-derivable/Cargo.toml | 2 +- sdk/seed-phrase/Cargo.toml | 5 ++++- sdk/seed-phrase/src/lib.rs | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/seed-derivable/Cargo.toml b/sdk/seed-derivable/Cargo.toml index 6ece90faddd5da..5133b01afbbf41 100644 --- a/sdk/seed-derivable/Cargo.toml +++ b/sdk/seed-derivable/Cargo.toml @@ -13,7 +13,7 @@ edition = { workspace = true } ed25519-dalek = { workspace = true } ed25519-dalek-bip32 = { workspace = true } solana-derivation-path = { workspace = true } -solana-seed-phrase = { workspace = true } +solana-seed-phrase = { workspace = true, features = ["keypair"] } solana-signer = { workspace = true } [package.metadata.docs.rs] diff --git a/sdk/seed-phrase/Cargo.toml b/sdk/seed-phrase/Cargo.toml index a54c7bf64589f1..5d25042c33a7f2 100644 --- a/sdk/seed-phrase/Cargo.toml +++ b/sdk/seed-phrase/Cargo.toml @@ -13,10 +13,13 @@ edition = { workspace = true } hmac = { workspace = true } pbkdf2 = { workspace = true } sha2 = { workspace = true } -solana-signer = { workspace = true, features = ["keypair"] } +solana-signer = { workspace = true } [dev-dependencies] tiny-bip39 = { workspace = true } +[features] +keypair = ["solana-signer/keypair"] + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/seed-phrase/src/lib.rs b/sdk/seed-phrase/src/lib.rs index 91b5e912f63b1d..bc446c8ecee6a0 100644 --- a/sdk/seed-phrase/src/lib.rs +++ b/sdk/seed-phrase/src/lib.rs @@ -24,6 +24,7 @@ pub fn generate_seed_from_seed_phrase_and_passphrase( seed } +#[cfg(feature = "keypair")] pub fn keypair_from_seed_phrase_and_passphrase( seed_phrase: &str, passphrase: &str, From f531541a128517d785fbcf111d2418f107be6442 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Fri, 1 Nov 2024 14:10:27 +0400 Subject: [PATCH 22/28] fix feature-gated imports --- Cargo.lock | 1 + sdk/seed-phrase/Cargo.toml | 2 ++ sdk/seed-phrase/src/lib.rs | 10 ++++------ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f831204fe7c8d..714c84c10158cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8173,6 +8173,7 @@ dependencies = [ "hmac 0.12.1", "pbkdf2 0.11.0", "sha2 0.10.8", + "solana-seed-phrase", "solana-signer", "tiny-bip39", ] diff --git a/sdk/seed-phrase/Cargo.toml b/sdk/seed-phrase/Cargo.toml index 5d25042c33a7f2..31ae3cb8a10a93 100644 --- a/sdk/seed-phrase/Cargo.toml +++ b/sdk/seed-phrase/Cargo.toml @@ -16,9 +16,11 @@ sha2 = { workspace = true } solana-signer = { workspace = true } [dev-dependencies] +solana-seed-phrase = { path = ".", features = ["dev-context-only-utils"] } tiny-bip39 = { workspace = true } [features] +dev-context-only-utils = ["keypair"] keypair = ["solana-signer/keypair"] [package.metadata.docs.rs] diff --git a/sdk/seed-phrase/src/lib.rs b/sdk/seed-phrase/src/lib.rs index bc446c8ecee6a0..0766035e38e73b 100644 --- a/sdk/seed-phrase/src/lib.rs +++ b/sdk/seed-phrase/src/lib.rs @@ -1,9 +1,7 @@ //! Functions for generating keypairs from seed phrases. -use { - hmac::Hmac, - solana_signer::keypair::{keypair_from_seed, Keypair}, - std::error, -}; +use hmac::Hmac; +#[cfg(feature = "keypair")] +use solana_signer::keypair::{keypair_from_seed, Keypair}; pub fn generate_seed_from_seed_phrase_and_passphrase( seed_phrase: &str, @@ -28,7 +26,7 @@ pub fn generate_seed_from_seed_phrase_and_passphrase( pub fn keypair_from_seed_phrase_and_passphrase( seed_phrase: &str, passphrase: &str, -) -> Result> { +) -> Result> { keypair_from_seed(&generate_seed_from_seed_phrase_and_passphrase( seed_phrase, passphrase, From 952c99763ba391879afb15d1d932bc747c313cad Mon Sep 17 00:00:00 2001 From: Jon C Date: Mon, 4 Nov 2024 15:36:22 +0100 Subject: [PATCH 23/28] Extract solana-keypair, move other things around --- Cargo.lock | 31 +++++--- Cargo.toml | 2 + programs/sbf/Cargo.lock | 24 ++++-- sdk/Cargo.toml | 6 +- sdk/keypair/Cargo.toml | 33 ++++++++ .../src/keypair.rs => keypair/src/lib.rs} | 77 ++++++++++++++++++- sdk/presigner/Cargo.toml | 2 +- sdk/presigner/src/lib.rs | 2 +- sdk/seed-derivable/Cargo.toml | 3 +- sdk/seed-derivable/src/lib.rs | 3 +- sdk/seed-phrase/Cargo.toml | 9 --- sdk/seed-phrase/src/lib.rs | 33 -------- sdk/signer/Cargo.toml | 17 +--- sdk/signer/src/lib.rs | 58 -------------- sdk/src/signer/keypair.rs | 7 +- 15 files changed, 160 insertions(+), 147 deletions(-) create mode 100644 sdk/keypair/Cargo.toml rename sdk/{signer/src/keypair.rs => keypair/src/lib.rs} (84%) diff --git a/Cargo.lock b/Cargo.lock index 714c84c10158cc..249e035fe89cea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7004,6 +7004,24 @@ dependencies = [ "tiny-bip39", ] +[[package]] +name = "solana-keypair" +version = "2.2.0" +dependencies = [ + "bs58", + "ed25519-dalek", + "rand 0.7.3", + "serde_json", + "solana-pubkey", + "solana-seed-phrase", + "solana-signature", + "solana-signer", + "solana-transaction-error", + "static_assertions", + "tiny-bip39", + "wasm-bindgen", +] + [[package]] name = "solana-last-restart-slot" version = "2.2.0" @@ -7436,6 +7454,7 @@ dependencies = [ name = "solana-presigner" version = "2.2.0" dependencies = [ + "solana-keypair", "solana-pubkey", "solana-signature", "solana-signer", @@ -8099,6 +8118,7 @@ dependencies = [ "solana-frozen-abi-macro", "solana-inflation", "solana-instruction", + "solana-keypair", "solana-logger", "solana-native-token", "solana-packet", @@ -8162,8 +8182,7 @@ dependencies = [ "ed25519-dalek", "ed25519-dalek-bip32", "solana-derivation-path", - "solana-seed-phrase", - "solana-signer", + "solana-keypair", ] [[package]] @@ -8173,9 +8192,6 @@ dependencies = [ "hmac 0.12.1", "pbkdf2 0.11.0", "sha2 0.10.8", - "solana-seed-phrase", - "solana-signer", - "tiny-bip39", ] [[package]] @@ -8260,16 +8276,11 @@ dependencies = [ name = "solana-signer" version = "2.2.0" dependencies = [ - "bs58", - "ed25519-dalek", - "rand 0.7.3", "serde_json", "solana-pubkey", "solana-signature", - "solana-signer", "solana-transaction-error", "static_assertions", - "wasm-bindgen", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f62bed232686dc..dba21d93610587 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,6 +122,7 @@ members = [ "sdk/hash", "sdk/inflation", "sdk/instruction", + "sdk/keypair", "sdk/macro", "sdk/msg", "sdk/native-token", @@ -452,6 +453,7 @@ solana-hash = { path = "sdk/hash", version = "=2.2.0", default-features = false solana-inflation = { path = "sdk/inflation", version = "=2.2.0" } solana-inline-spl = { path = "inline-spl", version = "=2.2.0" } solana-instruction = { path = "sdk/instruction", version = "=2.2.0", default-features = false } +solana-keypair = { path = "sdk/keypair", version = "=2.2.0" } solana-last-restart-slot = { path = "sdk/last-restart-slot", version = "=2.2.0" } solana-lattice-hash = { path = "lattice-hash", version = "=2.2.0" } solana-ledger = { path = "ledger", version = "=2.2.0" } diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 21b6eff97764b0..a2fd71a6ae6bff 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -5538,6 +5538,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "solana-keypair" +version = "2.2.0" +dependencies = [ + "bs58", + "ed25519-dalek", + "rand 0.7.3", + "solana-pubkey", + "solana-seed-phrase", + "solana-signature", + "solana-signer", + "solana-transaction-error", + "wasm-bindgen", +] + [[package]] name = "solana-last-restart-slot" version = "2.2.0" @@ -6846,6 +6861,7 @@ dependencies = [ "solana-fee-structure", "solana-inflation", "solana-instruction", + "solana-keypair", "solana-native-token", "solana-packet", "solana-precompile-error", @@ -6901,8 +6917,7 @@ dependencies = [ "ed25519-dalek", "ed25519-dalek-bip32", "solana-derivation-path", - "solana-seed-phrase", - "solana-signer", + "solana-keypair", ] [[package]] @@ -6912,7 +6927,6 @@ dependencies = [ "hmac 0.12.1", "pbkdf2 0.11.0", "sha2 0.10.8", - "solana-signer", ] [[package]] @@ -6979,13 +6993,9 @@ dependencies = [ name = "solana-signer" version = "2.2.0" dependencies = [ - "bs58", - "ed25519-dalek", - "rand 0.7.3", "solana-pubkey", "solana-signature", "solana-transaction-error", - "wasm-bindgen", ] [[package]] diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 43817ef958c792..22ce22c441983e 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -28,17 +28,18 @@ full = [ "rand0-7", "serde_json", "solana-signature", - "solana-signer", "ed25519-dalek", "libsecp256k1", "sha3", "solana-commitment-config", "digest", "solana-pubkey/rand", + "dep:solana-keypair", "dep:solana-precompile-error", "dep:solana-presigner", "dep:solana-seed-derivable", "dep:solana-seed-phrase", + "dep:solana-signer", "dep:solana-transaction-error" ] borsh = ["dep:borsh", "solana-program/borsh", "solana-secp256k1-recover/borsh"] @@ -106,6 +107,7 @@ solana-frozen-abi-macro = { workspace = true, optional = true, features = [ ] } solana-inflation = { workspace = true, features = ["serde"] } solana-instruction = { workspace = true } +solana-keypair = { workspace = true, optional = true } solana-native-token = { workspace = true } solana-packet = { workspace = true, features = ["bincode", "serde"] } solana-precompile-error = { workspace = true, optional = true } @@ -126,7 +128,7 @@ solana-signature = { workspace = true, features = [ "std", "verify", ], optional = true } -solana-signer = { workspace = true, optional = true, features = ["keypair"] } +solana-signer = { workspace = true, optional = true } solana-time-utils = { workspace = true } solana-transaction-error = { workspace = true, features = ["serde"], optional = true } thiserror = { workspace = true } diff --git a/sdk/keypair/Cargo.toml b/sdk/keypair/Cargo.toml new file mode 100644 index 00000000000000..17131d94830bdb --- /dev/null +++ b/sdk/keypair/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "solana-keypair" +description = "Concrete implementation of a Solana `Signer`." +documentation = "https://docs.rs/solana-keypair" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[dependencies] +bs58 = { workspace = true, features = ["std"] } +ed25519-dalek = { workspace = true } +rand0-7 = { workspace = true } +solana-pubkey = { workspace = true } +solana-seed-phrase = { workspace = true } +solana-signature = { workspace = true } +solana-signer = { workspace = true } +solana-transaction-error = { workspace = true } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = { workspace = true } + +[dev-dependencies] +serde_json = { workspace = true } +static_assertions = { workspace = true } +tiny-bip39 = { workspace = true } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] +all-features = true +rustdoc-args = ["--cfg=docsrs"] diff --git a/sdk/signer/src/keypair.rs b/sdk/keypair/src/lib.rs similarity index 84% rename from sdk/signer/src/keypair.rs rename to sdk/keypair/src/lib.rs index f0062a9f2c135b..d1281fad986610 100644 --- a/sdk/signer/src/keypair.rs +++ b/sdk/keypair/src/lib.rs @@ -1,11 +1,12 @@ #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; use { - crate::{EncodableKey, EncodableKeypair, Signer, SignerError}, ed25519_dalek::Signer as DalekSigner, rand0_7::{rngs::OsRng, CryptoRng, RngCore}, solana_pubkey::Pubkey, + solana_seed_phrase::generate_seed_from_seed_phrase_and_passphrase, solana_signature::Signature, + solana_signer::{EncodableKey, EncodableKeypair, Signer, SignerError}, std::{ error, io::{Read, Write}, @@ -271,10 +272,22 @@ pub fn keypair_from_seed(seed: &[u8]) -> Result> Ok(Keypair(dalek_keypair)) } +pub fn keypair_from_seed_phrase_and_passphrase( + seed_phrase: &str, + passphrase: &str, +) -> Result> { + keypair_from_seed(&generate_seed_from_seed_phrase_and_passphrase( + seed_phrase, + passphrase, + )) +} + #[cfg(test)] mod tests { use { super::*, + bip39::{Language, Mnemonic, MnemonicType, Seed}, + solana_signer::unique_signers, std::{ fs::{self, File}, mem, @@ -374,4 +387,66 @@ mod tests { let keypair2 = keypair_from_seed(&[0u8; 32]).unwrap(); assert_eq!(keypair, keypair2); } + + fn pubkeys(signers: &[&dyn Signer]) -> Vec { + signers.iter().map(|x| x.pubkey()).collect() + } + + #[test] + fn test_unique_signers() { + let alice = Keypair::new(); + let bob = Keypair::new(); + assert_eq!( + pubkeys(&unique_signers(vec![&alice, &bob, &alice])), + pubkeys(&[&alice, &bob]) + ); + } + + #[test] + fn test_containers() { + use std::{rc::Rc, sync::Arc}; + + struct Foo { + #[allow(unused)] + signer: S, + } + + fn foo(_s: impl Signer) {} + + let _arc_signer = Foo { + signer: Arc::new(Keypair::new()), + }; + foo(Arc::new(Keypair::new())); + + let _rc_signer = Foo { + signer: Rc::new(Keypair::new()), + }; + foo(Rc::new(Keypair::new())); + + let _ref_signer = Foo { + signer: &Keypair::new(), + }; + foo(Keypair::new()); + + let _box_signer = Foo { + signer: Box::new(Keypair::new()), + }; + foo(Box::new(Keypair::new())); + + let _signer = Foo { + signer: Keypair::new(), + }; + foo(Keypair::new()); + } + + #[test] + fn test_keypair_from_seed_phrase_and_passphrase() { + let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); + let passphrase = "42"; + let seed = Seed::new(&mnemonic, passphrase); + let expected_keypair = keypair_from_seed(seed.as_bytes()).unwrap(); + let keypair = + keypair_from_seed_phrase_and_passphrase(mnemonic.phrase(), passphrase).unwrap(); + assert_eq!(keypair.pubkey(), expected_keypair.pubkey()); + } } diff --git a/sdk/presigner/Cargo.toml b/sdk/presigner/Cargo.toml index 14a7cb09a26e37..41feb96a301670 100644 --- a/sdk/presigner/Cargo.toml +++ b/sdk/presigner/Cargo.toml @@ -15,7 +15,7 @@ solana-signature = { workspace = true, features = ["verify"] } solana-signer = { workspace = true } [dev-dependencies] -solana-signer = { workspace = true, features = ["keypair"] } +solana-keypair = { workspace = true } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/presigner/src/lib.rs b/sdk/presigner/src/lib.rs index 1b2ea5e34ee362..2f3e20a942ca0a 100644 --- a/sdk/presigner/src/lib.rs +++ b/sdk/presigner/src/lib.rs @@ -53,7 +53,7 @@ where #[cfg(test)] mod tests { - use {super::*, solana_signer::keypair::keypair_from_seed}; + use {super::*, solana_keypair::keypair_from_seed}; #[test] fn test_presigner() { diff --git a/sdk/seed-derivable/Cargo.toml b/sdk/seed-derivable/Cargo.toml index 5133b01afbbf41..69e0eaa0e691c0 100644 --- a/sdk/seed-derivable/Cargo.toml +++ b/sdk/seed-derivable/Cargo.toml @@ -13,8 +13,7 @@ edition = { workspace = true } ed25519-dalek = { workspace = true } ed25519-dalek-bip32 = { workspace = true } solana-derivation-path = { workspace = true } -solana-seed-phrase = { workspace = true, features = ["keypair"] } -solana-signer = { workspace = true } +solana-keypair = { workspace = true } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/seed-derivable/src/lib.rs b/sdk/seed-derivable/src/lib.rs index cf2e1ac84be98d..99a39497d753a8 100644 --- a/sdk/seed-derivable/src/lib.rs +++ b/sdk/seed-derivable/src/lib.rs @@ -2,8 +2,7 @@ use { ed25519_dalek_bip32::Error as Bip32Error, solana_derivation_path::DerivationPath, - solana_seed_phrase::keypair_from_seed_phrase_and_passphrase, - solana_signer::keypair::{keypair_from_seed, Keypair}, + solana_keypair::{keypair_from_seed, keypair_from_seed_phrase_and_passphrase, Keypair}, std::error, }; diff --git a/sdk/seed-phrase/Cargo.toml b/sdk/seed-phrase/Cargo.toml index 31ae3cb8a10a93..6e7e84fb217769 100644 --- a/sdk/seed-phrase/Cargo.toml +++ b/sdk/seed-phrase/Cargo.toml @@ -13,15 +13,6 @@ edition = { workspace = true } hmac = { workspace = true } pbkdf2 = { workspace = true } sha2 = { workspace = true } -solana-signer = { workspace = true } - -[dev-dependencies] -solana-seed-phrase = { path = ".", features = ["dev-context-only-utils"] } -tiny-bip39 = { workspace = true } - -[features] -dev-context-only-utils = ["keypair"] -keypair = ["solana-signer/keypair"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/seed-phrase/src/lib.rs b/sdk/seed-phrase/src/lib.rs index 0766035e38e73b..37b857a4de09b2 100644 --- a/sdk/seed-phrase/src/lib.rs +++ b/sdk/seed-phrase/src/lib.rs @@ -1,7 +1,5 @@ //! Functions for generating keypairs from seed phrases. use hmac::Hmac; -#[cfg(feature = "keypair")] -use solana_signer::keypair::{keypair_from_seed, Keypair}; pub fn generate_seed_from_seed_phrase_and_passphrase( seed_phrase: &str, @@ -21,34 +19,3 @@ pub fn generate_seed_from_seed_phrase_and_passphrase( ); seed } - -#[cfg(feature = "keypair")] -pub fn keypair_from_seed_phrase_and_passphrase( - seed_phrase: &str, - passphrase: &str, -) -> Result> { - keypair_from_seed(&generate_seed_from_seed_phrase_and_passphrase( - seed_phrase, - passphrase, - )) -} - -#[cfg(test)] -mod tests { - use { - super::*, - bip39::{Language, Mnemonic, MnemonicType, Seed}, - solana_signer::Signer, - }; - - #[test] - fn test_keypair_from_seed_phrase_and_passphrase() { - let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); - let passphrase = "42"; - let seed = Seed::new(&mnemonic, passphrase); - let expected_keypair = keypair_from_seed(seed.as_bytes()).unwrap(); - let keypair = - keypair_from_seed_phrase_and_passphrase(mnemonic.phrase(), passphrase).unwrap(); - assert_eq!(keypair.pubkey(), expected_keypair.pubkey()); - } -} diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index 3cb35b92755bcb..aeb4f23fbdf0d4 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-signer" -description = "Solana abstractions and implementations for transaction signers." +description = "Abstractions for Solana transaction signers. See `solana-keypair` for a concrete implementation." documentation = "https://docs.rs/solana-signer" version = { workspace = true } authors = { workspace = true } @@ -10,29 +10,14 @@ license = { workspace = true } edition = { workspace = true } [dependencies] -bs58 = { workspace = true, features = ["std"], optional = true } -ed25519-dalek = { workspace = true, optional = true } -rand0-7 = { workspace = true, optional = true } solana-pubkey = { workspace = true } solana-signature = { workspace = true } solana-transaction-error = { workspace = true } -[target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = { workspace = true } - [dev-dependencies] serde_json = { workspace = true } -solana-signer = { path = ".", features = ["dev-context-only-utils"] } static_assertions = { workspace = true } -[features] -dev-context-only-utils = ["keypair"] -keypair = [ - "dep:bs58", - "dep:ed25519-dalek", - "dep:rand0-7", -] - [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] all-features = true diff --git a/sdk/signer/src/lib.rs b/sdk/signer/src/lib.rs index 06aa4ddf4c1276..e6c286867da9dd 100644 --- a/sdk/signer/src/lib.rs +++ b/sdk/signer/src/lib.rs @@ -14,8 +14,6 @@ use { }, }; -#[cfg(feature = "keypair")] -pub mod keypair; pub mod null_signer; pub mod signers; @@ -221,59 +219,3 @@ pub trait EncodableKeypair: EncodableKey { /// Returns an encodable representation of the associated public key. fn encodable_pubkey(&self) -> Self::Pubkey; } - -#[cfg(test)] -mod tests { - use {super::*, crate::keypair::Keypair}; - - fn pubkeys(signers: &[&dyn Signer]) -> Vec { - signers.iter().map(|x| x.pubkey()).collect() - } - - #[test] - fn test_unique_signers() { - let alice = Keypair::new(); - let bob = Keypair::new(); - assert_eq!( - pubkeys(&unique_signers(vec![&alice, &bob, &alice])), - pubkeys(&[&alice, &bob]) - ); - } - - #[test] - fn test_containers() { - use std::{rc::Rc, sync::Arc}; - - struct Foo { - #[allow(unused)] - signer: S, - } - - fn foo(_s: impl Signer) {} - - let _arc_signer = Foo { - signer: Arc::new(Keypair::new()), - }; - foo(Arc::new(Keypair::new())); - - let _rc_signer = Foo { - signer: Rc::new(Keypair::new()), - }; - foo(Rc::new(Keypair::new())); - - let _ref_signer = Foo { - signer: &Keypair::new(), - }; - foo(Keypair::new()); - - let _box_signer = Foo { - signer: Box::new(Keypair::new()), - }; - foo(Box::new(Keypair::new())); - - let _signer = Foo { - signer: Keypair::new(), - }; - foo(Keypair::new()); - } -} diff --git a/sdk/src/signer/keypair.rs b/sdk/src/signer/keypair.rs index f7f8c33fce93b6..e06b6af3bd1caa 100644 --- a/sdk/src/signer/keypair.rs +++ b/sdk/src/signer/keypair.rs @@ -1,7 +1,4 @@ pub use { - solana_seed_derivable::keypair_from_seed_and_derivation_path, - solana_seed_phrase::{ - generate_seed_from_seed_phrase_and_passphrase, keypair_from_seed_phrase_and_passphrase, - }, - solana_signer::keypair::*, + solana_keypair::*, solana_seed_derivable::keypair_from_seed_and_derivation_path, + solana_seed_phrase::generate_seed_from_seed_phrase_and_passphrase, solana_signer::*, }; From 5d19c0598f283a9a10555a79114aff2bb3ee372c Mon Sep 17 00:00:00 2001 From: Jon C Date: Mon, 4 Nov 2024 23:39:17 +0100 Subject: [PATCH 24/28] Cleanup things that I missed --- Cargo.lock | 3 --- programs/sbf/Cargo.lock | 1 - sdk/keypair/Cargo.toml | 1 - sdk/keypair/src/lib.rs | 2 ++ sdk/signer/Cargo.toml | 4 ---- 5 files changed, 2 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 249e035fe89cea..74d6572b21be5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7016,7 +7016,6 @@ dependencies = [ "solana-seed-phrase", "solana-signature", "solana-signer", - "solana-transaction-error", "static_assertions", "tiny-bip39", "wasm-bindgen", @@ -8276,11 +8275,9 @@ dependencies = [ name = "solana-signer" version = "2.2.0" dependencies = [ - "serde_json", "solana-pubkey", "solana-signature", "solana-transaction-error", - "static_assertions", ] [[package]] diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index a2fd71a6ae6bff..9637d41654dd78 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -5549,7 +5549,6 @@ dependencies = [ "solana-seed-phrase", "solana-signature", "solana-signer", - "solana-transaction-error", "wasm-bindgen", ] diff --git a/sdk/keypair/Cargo.toml b/sdk/keypair/Cargo.toml index 17131d94830bdb..842fe0a5c47f48 100644 --- a/sdk/keypair/Cargo.toml +++ b/sdk/keypair/Cargo.toml @@ -17,7 +17,6 @@ solana-pubkey = { workspace = true } solana-seed-phrase = { workspace = true } solana-signature = { workspace = true } solana-signer = { workspace = true } -solana-transaction-error = { workspace = true } [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = { workspace = true } diff --git a/sdk/keypair/src/lib.rs b/sdk/keypair/src/lib.rs index d1281fad986610..36338af0a80bfa 100644 --- a/sdk/keypair/src/lib.rs +++ b/sdk/keypair/src/lib.rs @@ -1,3 +1,5 @@ +//! Concrete implementation of a Solana `Signer` from raw bytes +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; use { diff --git a/sdk/signer/Cargo.toml b/sdk/signer/Cargo.toml index aeb4f23fbdf0d4..5d943a5ff65506 100644 --- a/sdk/signer/Cargo.toml +++ b/sdk/signer/Cargo.toml @@ -14,10 +14,6 @@ solana-pubkey = { workspace = true } solana-signature = { workspace = true } solana-transaction-error = { workspace = true } -[dev-dependencies] -serde_json = { workspace = true } -static_assertions = { workspace = true } - [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] all-features = true From d6c275f52fb985b7a88338390fdcbc9655b13f13 Mon Sep 17 00:00:00 2001 From: Jon C Date: Mon, 4 Nov 2024 23:50:21 +0100 Subject: [PATCH 25/28] Remove space --- sdk/presigner/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/presigner/Cargo.toml b/sdk/presigner/Cargo.toml index 41feb96a301670..56ed8e1b28b03f 100644 --- a/sdk/presigner/Cargo.toml +++ b/sdk/presigner/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-presigner" -description = "A Solana `Signer` implementation representing an externally-constructed `Signature`." +description = "A Solana `Signer` implementation representing an externally-constructed `Signature`." documentation = "https://docs.rs/solana-presigner" version = { workspace = true } authors = { workspace = true } From a8ef16481150897ef1954e73ddfdd0e6c8a8d834 Mon Sep 17 00:00:00 2001 From: Jon C Date: Mon, 4 Nov 2024 23:50:28 +0100 Subject: [PATCH 26/28] Add deprecated notice --- sdk/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 323e0ba1d06352..43aa208f56fdb0 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -59,6 +59,7 @@ pub use solana_program::{ #[cfg(feature = "borsh")] pub use solana_program::{borsh, borsh0_10, borsh1}; #[cfg(feature = "full")] +#[deprecated(since = "2.2.0", note = "Use `solana-signer` crate instead")] pub use solana_signer::signers; pub mod client; pub mod compute_budget; From 06426cc53236039eca9aac6569d6de62c1a569d2 Mon Sep 17 00:00:00 2001 From: Jon C Date: Wed, 6 Nov 2024 02:28:45 +0100 Subject: [PATCH 27/28] Add deprecation notices --- sdk/src/signer/keypair.rs | 12 ++++++++---- sdk/src/signer/mod.rs | 13 +++++++------ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/sdk/src/signer/keypair.rs b/sdk/src/signer/keypair.rs index e06b6af3bd1caa..c7e56aca47c4ee 100644 --- a/sdk/src/signer/keypair.rs +++ b/sdk/src/signer/keypair.rs @@ -1,4 +1,8 @@ -pub use { - solana_keypair::*, solana_seed_derivable::keypair_from_seed_and_derivation_path, - solana_seed_phrase::generate_seed_from_seed_phrase_and_passphrase, solana_signer::*, -}; +#[deprecated(since = "2.2.0", note = "Use `solana-keypair` crate instead")] +pub use solana_keypair::*; +#[deprecated(since = "2.2.0", note = "Use `solana-seed-derivable` crate instead")] +pub use solana_seed_derivable::keypair_from_seed_and_derivation_path; +#[deprecated(since = "2.2.0", note = "Use `solana-seed-phrase` crate instead")] +pub use solana_seed_phrase::generate_seed_from_seed_phrase_and_passphrase; +#[deprecated(since = "2.2.0", note = "Use `solana-signer` crate instead")] +pub use solana_signer::*; diff --git a/sdk/src/signer/mod.rs b/sdk/src/signer/mod.rs index 36b789741d80d8..9d5239d7ff2d8a 100644 --- a/sdk/src/signer/mod.rs +++ b/sdk/src/signer/mod.rs @@ -1,9 +1,10 @@ #![cfg(feature = "full")] -pub use { - solana_presigner as presigner, - solana_seed_derivable::SeedDerivable, - solana_signer::{ - null_signer, signers, unique_signers, EncodableKey, EncodableKeypair, Signer, SignerError, - }, +#[deprecated(since = "2.2.0", note = "Use `solana-presigner` crate instead")] +pub use solana_presigner as presigner; +#[deprecated(since = "2.2.0", note = "Use `solana-seed-derivable` crate instead")] +pub use solana_seed_derivable::SeedDerivable; +#[deprecated(since = "2.2.0", note = "Use `solana-signer` crate instead")] +pub use solana_signer::{ + null_signer, signers, unique_signers, EncodableKey, EncodableKeypair, Signer, SignerError, }; pub mod keypair; From 1720552216c551723c02293d07b70ab5709e7224 Mon Sep 17 00:00:00 2001 From: Jon C Date: Wed, 6 Nov 2024 13:50:06 +0100 Subject: [PATCH 28/28] Refactor SeedDerivable trait implementation --- Cargo.lock | 6 ++-- programs/sbf/Cargo.lock | 6 ++-- sdk/Cargo.toml | 2 +- sdk/keypair/Cargo.toml | 6 ++++ sdk/keypair/src/lib.rs | 3 ++ sdk/keypair/src/seed_derivable.rs | 53 +++++++++++++++++++++++++++++++ sdk/seed-derivable/Cargo.toml | 3 -- sdk/seed-derivable/src/lib.rs | 51 +---------------------------- sdk/src/signer/keypair.rs | 8 +++-- 9 files changed, 75 insertions(+), 63 deletions(-) create mode 100644 sdk/keypair/src/seed_derivable.rs diff --git a/Cargo.lock b/Cargo.lock index 74d6572b21be5b..32db743458d9a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7010,9 +7010,12 @@ version = "2.2.0" dependencies = [ "bs58", "ed25519-dalek", + "ed25519-dalek-bip32", "rand 0.7.3", "serde_json", + "solana-derivation-path", "solana-pubkey", + "solana-seed-derivable", "solana-seed-phrase", "solana-signature", "solana-signer", @@ -8178,10 +8181,7 @@ checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" name = "solana-seed-derivable" version = "2.2.0" dependencies = [ - "ed25519-dalek", - "ed25519-dalek-bip32", "solana-derivation-path", - "solana-keypair", ] [[package]] diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 9637d41654dd78..d6f0641b191654 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -5544,8 +5544,11 @@ version = "2.2.0" dependencies = [ "bs58", "ed25519-dalek", + "ed25519-dalek-bip32", "rand 0.7.3", + "solana-derivation-path", "solana-pubkey", + "solana-seed-derivable", "solana-seed-phrase", "solana-signature", "solana-signer", @@ -6913,10 +6916,7 @@ checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" name = "solana-seed-derivable" version = "2.2.0" dependencies = [ - "ed25519-dalek", - "ed25519-dalek-bip32", "solana-derivation-path", - "solana-keypair", ] [[package]] diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 22ce22c441983e..762bc716ddaa2a 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -107,7 +107,7 @@ solana-frozen-abi-macro = { workspace = true, optional = true, features = [ ] } solana-inflation = { workspace = true, features = ["serde"] } solana-instruction = { workspace = true } -solana-keypair = { workspace = true, optional = true } +solana-keypair = { workspace = true, optional = true, features = ["seed-derivable"] } solana-native-token = { workspace = true } solana-packet = { workspace = true, features = ["bincode", "serde"] } solana-precompile-error = { workspace = true, optional = true } diff --git a/sdk/keypair/Cargo.toml b/sdk/keypair/Cargo.toml index 842fe0a5c47f48..dee76f0555a226 100644 --- a/sdk/keypair/Cargo.toml +++ b/sdk/keypair/Cargo.toml @@ -12,8 +12,11 @@ edition = { workspace = true } [dependencies] bs58 = { workspace = true, features = ["std"] } ed25519-dalek = { workspace = true } +ed25519-dalek-bip32 = { workspace = true, optional = true } rand0-7 = { workspace = true } +solana-derivation-path = { workspace = true, optional = true } solana-pubkey = { workspace = true } +solana-seed-derivable = { workspace = true, optional = true } solana-seed-phrase = { workspace = true } solana-signature = { workspace = true } solana-signer = { workspace = true } @@ -26,6 +29,9 @@ serde_json = { workspace = true } static_assertions = { workspace = true } tiny-bip39 = { workspace = true } +[features] +seed-derivable = ["dep:solana-derivation-path", "dep:solana-seed-derivable", "dep:ed25519-dalek-bip32"] + [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] all-features = true diff --git a/sdk/keypair/src/lib.rs b/sdk/keypair/src/lib.rs index 36338af0a80bfa..08f9e2a5768bd2 100644 --- a/sdk/keypair/src/lib.rs +++ b/sdk/keypair/src/lib.rs @@ -16,6 +16,9 @@ use { }, }; +#[cfg(feature = "seed-derivable")] +pub mod seed_derivable; + /// A vanilla Ed25519 key pair #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] #[derive(Debug)] diff --git a/sdk/keypair/src/seed_derivable.rs b/sdk/keypair/src/seed_derivable.rs new file mode 100644 index 00000000000000..4f530f41fc8acc --- /dev/null +++ b/sdk/keypair/src/seed_derivable.rs @@ -0,0 +1,53 @@ +//! Implementation of the SeedDerivable trait for Keypair + +use { + crate::{keypair_from_seed, keypair_from_seed_phrase_and_passphrase, Keypair}, + ed25519_dalek_bip32::Error as Bip32Error, + solana_derivation_path::DerivationPath, + solana_seed_derivable::SeedDerivable, + std::error, +}; + +impl SeedDerivable for Keypair { + fn from_seed(seed: &[u8]) -> Result> { + keypair_from_seed(seed) + } + + fn from_seed_and_derivation_path( + seed: &[u8], + derivation_path: Option, + ) -> Result> { + keypair_from_seed_and_derivation_path(seed, derivation_path) + } + + fn from_seed_phrase_and_passphrase( + seed_phrase: &str, + passphrase: &str, + ) -> Result> { + keypair_from_seed_phrase_and_passphrase(seed_phrase, passphrase) + } +} + +/// Generates a Keypair using Bip32 Hierarchical Derivation if derivation-path is provided; +/// otherwise generates the base Bip44 Solana keypair from the seed +pub fn keypair_from_seed_and_derivation_path( + seed: &[u8], + derivation_path: Option, +) -> Result> { + let derivation_path = derivation_path.unwrap_or_default(); + bip32_derived_keypair(seed, derivation_path).map_err(|err| err.to_string().into()) +} + +/// Generates a Keypair using Bip32 Hierarchical Derivation +fn bip32_derived_keypair( + seed: &[u8], + derivation_path: DerivationPath, +) -> Result { + let extended = ed25519_dalek_bip32::ExtendedSecretKey::from_seed(seed) + .and_then(|extended| extended.derive(&derivation_path))?; + let extended_public_key = extended.public_key(); + Ok(Keypair::from(ed25519_dalek::Keypair { + secret: extended.secret_key, + public: extended_public_key, + })) +} diff --git a/sdk/seed-derivable/Cargo.toml b/sdk/seed-derivable/Cargo.toml index 69e0eaa0e691c0..98acb4f9dd5184 100644 --- a/sdk/seed-derivable/Cargo.toml +++ b/sdk/seed-derivable/Cargo.toml @@ -10,10 +10,7 @@ license = { workspace = true } edition = { workspace = true } [dependencies] -ed25519-dalek = { workspace = true } -ed25519-dalek-bip32 = { workspace = true } solana-derivation-path = { workspace = true } -solana-keypair = { workspace = true } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/sdk/seed-derivable/src/lib.rs b/sdk/seed-derivable/src/lib.rs index 99a39497d753a8..e9e34587ae7cc7 100644 --- a/sdk/seed-derivable/src/lib.rs +++ b/sdk/seed-derivable/src/lib.rs @@ -1,10 +1,5 @@ //! The interface by which keys are derived. -use { - ed25519_dalek_bip32::Error as Bip32Error, - solana_derivation_path::DerivationPath, - solana_keypair::{keypair_from_seed, keypair_from_seed_phrase_and_passphrase, Keypair}, - std::error, -}; +use {solana_derivation_path::DerivationPath, std::error}; /// The `SeedDerivable` trait defines the interface by which cryptographic keys/keypairs are /// derived from byte seeds, derivation paths, and passphrases. @@ -19,47 +14,3 @@ pub trait SeedDerivable: Sized { passphrase: &str, ) -> Result>; } - -impl SeedDerivable for Keypair { - fn from_seed(seed: &[u8]) -> Result> { - keypair_from_seed(seed) - } - - fn from_seed_and_derivation_path( - seed: &[u8], - derivation_path: Option, - ) -> Result> { - keypair_from_seed_and_derivation_path(seed, derivation_path) - } - - fn from_seed_phrase_and_passphrase( - seed_phrase: &str, - passphrase: &str, - ) -> Result> { - keypair_from_seed_phrase_and_passphrase(seed_phrase, passphrase) - } -} - -/// Generates a Keypair using Bip32 Hierarchical Derivation if derivation-path is provided; -/// otherwise generates the base Bip44 Solana keypair from the seed -pub fn keypair_from_seed_and_derivation_path( - seed: &[u8], - derivation_path: Option, -) -> Result> { - let derivation_path = derivation_path.unwrap_or_default(); - bip32_derived_keypair(seed, derivation_path).map_err(|err| err.to_string().into()) -} - -/// Generates a Keypair using Bip32 Hierarchical Derivation -fn bip32_derived_keypair( - seed: &[u8], - derivation_path: DerivationPath, -) -> Result { - let extended = ed25519_dalek_bip32::ExtendedSecretKey::from_seed(seed) - .and_then(|extended| extended.derive(&derivation_path))?; - let extended_public_key = extended.public_key(); - Ok(Keypair::from(ed25519_dalek::Keypair { - secret: extended.secret_key, - public: extended_public_key, - })) -} diff --git a/sdk/src/signer/keypair.rs b/sdk/src/signer/keypair.rs index c7e56aca47c4ee..3433931a7f95a7 100644 --- a/sdk/src/signer/keypair.rs +++ b/sdk/src/signer/keypair.rs @@ -1,7 +1,9 @@ #[deprecated(since = "2.2.0", note = "Use `solana-keypair` crate instead")] -pub use solana_keypair::*; -#[deprecated(since = "2.2.0", note = "Use `solana-seed-derivable` crate instead")] -pub use solana_seed_derivable::keypair_from_seed_and_derivation_path; +pub use solana_keypair::{ + keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair, read_keypair_file, + seed_derivable::keypair_from_seed_and_derivation_path, write_keypair, write_keypair_file, + Keypair, +}; #[deprecated(since = "2.2.0", note = "Use `solana-seed-phrase` crate instead")] pub use solana_seed_phrase::generate_seed_from_seed_phrase_and_passphrase; #[deprecated(since = "2.2.0", note = "Use `solana-signer` crate instead")]