From 1ba952809d78129a04ce8e4d874c7a7087d45319 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sat, 14 Sep 2024 21:24:21 +0400 Subject: [PATCH 01/23] 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 bec8dee1a49c43..e9dfb8a9eb9ab6 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 52d36f3b238ffb..a7e9070c201d9a 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 98dd73792c191a..7860e9e25d3107 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 839bfd3155b901e0b0c30dd58dc30aa76b34fcd9 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 15 Sep 2024 21:17:22 +0400 Subject: [PATCH 02/23] 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 17193f5973a6d562a7e38b6986cf243bc174587e Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 15 Sep 2024 22:35:17 +0400 Subject: [PATCH 03/23] 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 473e8998aaf116fa0c1ef057df85353f711a941c Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Mon, 23 Sep 2024 16:33:21 +0400 Subject: [PATCH 04/23] 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 e9dfb8a9eb9ab6..a6c66e34732341 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 a7e9070c201d9a..6fc6dff2ecbeb0 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 7860e9e25d3107..abf08f16aa59d3 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 83de4fabec3571e1a71aeaf4093f233c792e1796 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Mon, 23 Sep 2024 16:52:42 +0400 Subject: [PATCH 05/23] 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 a6c66e34732341..3a53e20d2b11a5 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 6fc6dff2ecbeb0..3c5f6a299346ae 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 abf08f16aa59d3..1f2b83819cd1c3 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 db2d87b27f4ccd764fd9c36c179057d73346f90a Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Mon, 23 Sep 2024 16:53:33 +0400 Subject: [PATCH 06/23] 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 32d37ded6aa8bbf0b0ad316b9db28e842329acf9 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Mon, 23 Sep 2024 17:09:08 +0400 Subject: [PATCH 07/23] 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 3a53e20d2b11a5..4fe3b5db12b17f 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 3c5f6a299346ae..7a062effef54db 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 1f2b83819cd1c3..c9f695ba8e387c 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 d4bbaecd6a838625fac758b0b78de9ce48e930fa Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Mon, 23 Sep 2024 21:19:28 +0400 Subject: [PATCH 08/23] 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 a6ffedc3e858c0c3108f4708aaf10b1b91b28993 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Tue, 24 Sep 2024 10:33:51 +0400 Subject: [PATCH 09/23] 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 4fe3b5db12b17f..fb4438dcd5a592 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 c9f695ba8e387c..9cb2347c93b062 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 8d7a97cd62bef70d3a105110cc00486b82756eed Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 13:50:13 +0400 Subject: [PATCH 10/23] 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 fb4438dcd5a592..3fa3f15e80834e 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 9cb2347c93b062..28f30190634f7b 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 b2c21d84ac2fb70e3125c98da08ae87741a2155e Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 15:35:12 +0400 Subject: [PATCH 11/23] 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 71cb20028d2770a574fefa0450c4ae9fa9252a7d Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 15:44:36 +0400 Subject: [PATCH 12/23] 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 c0091767587dbad9c85bb4d0d843e42ebe32fcc5 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 15:45:34 +0400 Subject: [PATCH 13/23] 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 beb17051a44bd568678af63ab1d0326baed60c4c Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 15:52:20 +0400 Subject: [PATCH 14/23] 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 a022daf6838dc9bc43cf5e050abc9d0a780ede86 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 17:45:04 +0400 Subject: [PATCH 15/23] 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 3fa3f15e80834e..e413c87ede9238 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 28f30190634f7b..185872137f5c59 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 75c44cdcd74e67fff91f11af74aec9f72d5fcc08 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sun, 6 Oct 2024 19:08:21 +0400 Subject: [PATCH 16/23] 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 e413c87ede9238..608d76e7813375 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 185872137f5c59..abc97f019a7188 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 e616a0fd69c12f354355b392291fdca51e271714 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Sat, 12 Oct 2024 14:14:06 +0400 Subject: [PATCH 17/23] 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 be1efc7fbe22bbe766494097b6bc6433a9b348d2 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Fri, 1 Nov 2024 12:33:52 +0400 Subject: [PATCH 18/23] 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 b782c8335c1335a1eb3fce71e94607f118067de8 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Fri, 1 Nov 2024 12:39:42 +0400 Subject: [PATCH 19/23] 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 a8c4179629894e6a7a0e1f384115453718105998 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Fri, 1 Nov 2024 12:46:22 +0400 Subject: [PATCH 20/23] 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 a8205269c8b91808f2ae2b69fb03193c8ca83634 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Fri, 1 Nov 2024 13:53:05 +0400 Subject: [PATCH 21/23] 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 a96f16fe807a3b1481753a531d87d7dc7081a38b Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Fri, 1 Nov 2024 14:10:27 +0400 Subject: [PATCH 22/23] 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 608d76e7813375..694d238ba4966a 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 4c0fee71b2c4604b11553209d186f0bb99b554aa Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Fri, 1 Nov 2024 13:43:42 +0400 Subject: [PATCH 23/23] remove solana-sdk from solana-zk-token-sdk --- Cargo.lock | 5 ++++- programs/sbf/Cargo.lock | 5 ++++- zk-token-sdk/Cargo.toml | 7 +++++-- zk-token-sdk/src/encryption/auth_encryption.rs | 14 ++++++-------- zk-token-sdk/src/encryption/elgamal.rs | 14 ++++++-------- zk-token-sdk/src/sigma_proofs/pubkey_proof.rs | 5 +---- 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 694d238ba4966a..d29e2276929aba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9124,7 +9124,10 @@ dependencies = [ "solana-derivation-path", "solana-instruction", "solana-pubkey", - "solana-sdk", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-signature", + "solana-signer", "subtle", "thiserror", "tiny-bip39", diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index abc97f019a7188..34f1cdf15edbe7 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -7556,7 +7556,10 @@ dependencies = [ "solana-derivation-path", "solana-instruction", "solana-pubkey", - "solana-sdk", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-signature", + "solana-signer", "subtle", "thiserror", "zeroize", diff --git a/zk-token-sdk/Cargo.toml b/zk-token-sdk/Cargo.toml index f5e431f54c94ae..39f7e584767b33 100644 --- a/zk-token-sdk/Cargo.toml +++ b/zk-token-sdk/Cargo.toml @@ -17,7 +17,7 @@ num-derive = { workspace = true } num-traits = { workspace = true } solana-curve25519 = { workspace = true } solana-instruction = { workspace = true, features = ["std"] } -solana-pubkey = { workspace = true } +solana-pubkey = { workspace = true, features = ["bytemuck"] } thiserror = { workspace = true } [dev-dependencies] @@ -37,7 +37,10 @@ serde_derive = { workspace = true } serde_json = { workspace = true } sha3 = { workspace = true } solana-derivation-path = { workspace = true } -solana-sdk = { workspace = true } +solana-seed-derivable = { workspace = true } +solana-seed-phrase = { workspace = true } +solana-signature = { workspace = true } +solana-signer = { workspace = true } subtle = { workspace = true } zeroize = { workspace = true, features = ["zeroize_derive"] } diff --git a/zk-token-sdk/src/encryption/auth_encryption.rs b/zk-token-sdk/src/encryption/auth_encryption.rs index c61d27e486f7f1..a1395e472abe5b 100644 --- a/zk-token-sdk/src/encryption/auth_encryption.rs +++ b/zk-token-sdk/src/encryption/auth_encryption.rs @@ -7,13 +7,10 @@ use { base64::{prelude::BASE64_STANDARD, Engine}, sha3::{Digest, Sha3_512}, solana_derivation_path::DerivationPath, - solana_sdk::{ - signature::Signature, - signer::{ - keypair::generate_seed_from_seed_phrase_and_passphrase, EncodableKey, SeedDerivable, - Signer, SignerError, - }, - }, + solana_seed_derivable::SeedDerivable, + solana_seed_phrase::generate_seed_from_seed_phrase_and_passphrase, + solana_signature::Signature, + solana_signer::{EncodableKey, Signer, SignerError}, std::{ convert::TryInto, error, fmt, @@ -268,7 +265,8 @@ impl fmt::Display for AeCiphertext { mod tests { use { super::*, - solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::null_signer::NullSigner}, + solana_pubkey::Pubkey, + solana_signer::{keypair::Keypair, null_signer::NullSigner}, }; #[test] diff --git a/zk-token-sdk/src/encryption/elgamal.rs b/zk-token-sdk/src/encryption/elgamal.rs index d4ffc2028bf27a..b642a337ca74d0 100644 --- a/zk-token-sdk/src/encryption/elgamal.rs +++ b/zk-token-sdk/src/encryption/elgamal.rs @@ -34,13 +34,10 @@ use { }, serde::{Deserialize, Serialize}, solana_derivation_path::DerivationPath, - solana_sdk::{ - signature::Signature, - signer::{ - keypair::generate_seed_from_seed_phrase_and_passphrase, EncodableKey, EncodableKeypair, - SeedDerivable, Signer, SignerError, - }, - }, + solana_seed_derivable::SeedDerivable, + solana_seed_phrase::generate_seed_from_seed_phrase_and_passphrase, + solana_signature::Signature, + solana_signer::{EncodableKey, EncodableKeypair, Signer, SignerError}, std::convert::TryInto, subtle::{Choice, ConstantTimeEq}, zeroize::Zeroize, @@ -870,7 +867,8 @@ mod tests { super::*, crate::encryption::pedersen::Pedersen, bip39::{Language, Mnemonic, MnemonicType, Seed}, - solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::null_signer::NullSigner}, + solana_pubkey::Pubkey, + solana_signer::{keypair::Keypair, null_signer::NullSigner}, std::fs::{self, File}, }; diff --git a/zk-token-sdk/src/sigma_proofs/pubkey_proof.rs b/zk-token-sdk/src/sigma_proofs/pubkey_proof.rs index 2498a1e68686b5..57b0f87bdcd69e 100644 --- a/zk-token-sdk/src/sigma_proofs/pubkey_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/pubkey_proof.rs @@ -138,10 +138,7 @@ impl PubkeyValidityProof { #[cfg(test)] mod test { - use { - super::*, - solana_sdk::{pubkey::Pubkey, signature::Keypair}, - }; + use {super::*, solana_pubkey::Pubkey, solana_signer::keypair::Keypair}; #[test] fn test_pubkey_proof_correctness() {