From 3d679ef99e0abf516aaf88f5462d4024193acf7e Mon Sep 17 00:00:00 2001 From: hkey0 Date: Thu, 21 Dec 2023 09:37:32 +0300 Subject: [PATCH 1/3] Added HD Wallet Support (from_seed_and_derivation_path) --- crates/keypair/src/lib.rs | 36 ++++++++++++++++++++++++++++++++---- python/solders/keypair.pyi | 2 ++ tests/test_keypair.py | 11 ++++++++++- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/crates/keypair/src/lib.rs b/crates/keypair/src/lib.rs index 53856fdb..58f96e24 100644 --- a/crates/keypair/src/lib.rs +++ b/crates/keypair/src/lib.rs @@ -1,11 +1,15 @@ use derive_more::{From, Into}; use pyo3::{prelude::*, types::PyBytes}; use serde::{Deserialize, Serialize}; -use solana_sdk::signer::{ - keypair::{ - keypair_from_seed, keypair_from_seed_phrase_and_passphrase, Keypair as KeypairOriginal, +use solana_sdk::{ + derivation_path::DerivationPath, + signature::keypair_from_seed_and_derivation_path, + signer::{ + keypair::{ + keypair_from_seed, keypair_from_seed_phrase_and_passphrase, Keypair as KeypairOriginal, + }, + Signer as SignerTrait, }, - Signer as SignerTrait, }; use solders_macros::{common_methods, pyhash, richcmp_signer}; use solders_pubkey::Pubkey; @@ -193,6 +197,30 @@ impl Keypair { handle_py_value_err(keypair_from_seed(&seed)) } + #[staticmethod] + /// Generate a keypair from a 32-byte seed and derivation path.. + /// + /// Args: + /// seed (bytes): 32-byte seed. + /// dpath (string): derivation path. + /// Returns: + /// Keypair: The generated keypair. + /// + /// Example: + /// >>> from solders.keypair import Keypair + /// >>> from solders.pubkey import Pubkey + /// >>> seed_bytes = bytes([0] * 64) + /// >>> account_index = 0 + /// >>> derivation_path = f"m/44'/501'/0'/{account_index}'" + /// >>> from_seed = Keypair.from_seed_and_derivation_path(seed_bytes, derivation_path) + + pub fn from_seed_and_derivation_path(seed: [u8; 64], dpath: &str) -> PyResult { + handle_py_value_err(keypair_from_seed_and_derivation_path( + &seed, + Some(DerivationPath::from_key_str(&dpath).unwrap()), + )) + } + #[staticmethod] /// Generate a keypair from a seed phrase and passphrase. /// diff --git a/python/solders/keypair.pyi b/python/solders/keypair.pyi index fb5dfb32..40d62793 100644 --- a/python/solders/keypair.pyi +++ b/python/solders/keypair.pyi @@ -12,6 +12,8 @@ class Keypair: @staticmethod def from_seed(seed: Union[bytes, Sequence[int]]) -> "Keypair": ... @staticmethod + def from_seed_and_derivation_path(seed: Union[bytes, Sequence[int]], dpath: str) -> "Keypair": ... + @staticmethod def from_base58_string(s: str) -> "Keypair": ... @staticmethod def from_seed_phrase_and_passphrase( diff --git a/tests/test_keypair.py b/tests/test_keypair.py index 4932c200..0e0b6f20 100644 --- a/tests/test_keypair.py +++ b/tests/test_keypair.py @@ -4,9 +4,17 @@ from pybip39 import Mnemonic, Seed from pytest import mark, raises -from solders.keypair import Keypair +from python.solders.keypair import Keypair +def test_from_seed_and_derivation_path(): + mnemo = Mnemonic("english") + seed = mnemo.to_seed("pill tomorrow foster begin walnut borrow virtual kick shift mutual shoe scatter") + + first_wallet = Keypair.from_seed_and_derivation_path(seed, "m/44'/501'/0'/0'").pubkey() + assert "5F86TNSTre3CYwZd1wELsGQGhqG2HkN3d8zxhbyBSnzm" == first_wallet + + def test_from_bytes() -> None: raw_bytes = ( b"\x99\xda\x95Y\xe1^\x91>\xe9\xab.S\xe3\xdf\xadW]\xa3;I\xbe\x11%\xbb\x92.3IOI" @@ -115,3 +123,4 @@ def test_pickle() -> None: def test_json() -> None: obj = Keypair() assert Keypair.from_json(obj.to_json()) == obj + From 740f8efdd32b8802aa6c992bbc14a36d33d2152d Mon Sep 17 00:00:00 2001 From: hkey0 Date: Thu, 21 Dec 2023 09:46:07 +0300 Subject: [PATCH 2/3] Added HD Wallet Support (from_seed_and_derivation_path) --- tests/test_keypair.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_keypair.py b/tests/test_keypair.py index 0e0b6f20..94c8e9fa 100644 --- a/tests/test_keypair.py +++ b/tests/test_keypair.py @@ -4,7 +4,7 @@ from pybip39 import Mnemonic, Seed from pytest import mark, raises -from python.solders.keypair import Keypair +from solders.keypair import Keypair def test_from_seed_and_derivation_path(): From ab3b1833dbbe217316c803762e1b2c4029e6af2f Mon Sep 17 00:00:00 2001 From: hkey0 Date: Thu, 21 Dec 2023 19:38:21 +0300 Subject: [PATCH 3/3] Test problems solved. --- tests/test_keypair.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_keypair.py b/tests/test_keypair.py index 94c8e9fa..583b4a92 100644 --- a/tests/test_keypair.py +++ b/tests/test_keypair.py @@ -5,6 +5,7 @@ from pybip39 import Mnemonic, Seed from pytest import mark, raises from solders.keypair import Keypair +from mnemonic import Mnemonic def test_from_seed_and_derivation_path():