diff --git a/.changes/bip44.md b/.changes/bip44.md new file mode 100644 index 0000000000..7852ba66af --- /dev/null +++ b/.changes/bip44.md @@ -0,0 +1,5 @@ +--- +"wallet-nodejs-binding": patch +--- + +`Account::signSecp256k1Ecdsa` now accepts newly added Bip44 type. diff --git a/.github/workflows/wasm-compatibility.yml b/.github/workflows/wasm-compatibility.yml deleted file mode 100644 index fa7644d649..0000000000 --- a/.github/workflows/wasm-compatibility.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Wasm compatibility - -on: - push: - branches: [develop, production] - paths: - - ".github/workflows/wasm-compatibility.yml" - - ".github/actions/**" - - "**.rs" # Include all rust files - - "**Cargo.toml" # Include all Cargo.toml files - - "**Cargo.lock" # Include all Cargo.lock files - - "!**/bindings/**" # Exclude all bindings - - "!cli/**" # Exclude CLI - pull_request: - branches: [develop, production] - paths: - - ".github/workflows/wasm-compatibility.yml" - - ".github/actions/**" - - "**.rs" # Include all rust files - - "**Cargo.toml" # Include all Cargo.toml files - - "**Cargo.lock" # Include all Cargo.lock files - - "!**/bindings/**" # Exclude all bindings - - "!cli/**" # Exclude CLI - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Install stable rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: wasm32-unknown-unknown - override: true - - - name: Cargo check - uses: actions-rs/cargo@v1 - with: - command: check - args: --profile ci --target=wasm32-unknown-unknown --manifest-path sdk/Cargo.toml --no-default-features --features client,tls,events,storage,participation,wallet diff --git a/Cargo.lock b/Cargo.lock index 8d2df173c3..7ed0272ddd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,26 +54,30 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.6" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ - "getrandom", + "cfg-if", "once_cell", "version_check", ] [[package]] -name = "ahash" -version = "0.8.3" +name = "aho-corasick" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ - "cfg-if", - "once_cell", - "version_check", + "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -155,9 +159,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-trait" -version = "0.1.69" +version = "0.1.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7" +checksum = "79fa67157abdfd688a259b6648808757db9347af834624f27ec646da976aee5d" dependencies = [ "proc-macro2", "quote", @@ -529,7 +533,7 @@ version = "1.0.0-rc.2" dependencies = [ "chrono", "clap", - "colored 2.0.1", + "colored 2.0.4", "dialoguer", "dotenvy", "fern-logger", @@ -562,13 +566,13 @@ dependencies = [ [[package]] name = "colored" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17bfac9400fe632590700de801b5dfbdca8b6944073832d1284bdbeef7f00e45" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ - "atty", + "is-terminal", "lazy_static", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -630,9 +634,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -696,6 +700,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.23", +] + [[package]] name = "darling" version = "0.14.4" @@ -738,6 +770,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] @@ -889,21 +922,34 @@ dependencies = [ "digest 0.10.7", "elliptic-curve", "rfc6979", + "serdect", "signature", "spki", ] +[[package]] +name = "ed25519" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +dependencies = [ + "serde", + "signature", +] + [[package]] name = "ed25519-zebra" -version = "3.1.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +checksum = "ffb0d653b2c06ec7ec1b4c570bb4eac748035d6f44dd14e5fd9e7e2549938488" dependencies = [ - "curve25519-dalek", - "hashbrown 0.12.3", + "curve25519-dalek 4.0.0-rc.3", + "ed25519", + "hashbrown 0.14.0", "hex", "rand_core 0.6.4", - "sha2 0.9.9", + "serde", + "sha2", "zeroize", ] @@ -925,10 +971,11 @@ dependencies = [ "ff", "generic-array", "group", - "hkdf", + "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", + "serdect", "subtle", "zeroize", ] @@ -1028,6 +1075,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + [[package]] name = "fixed-hash" version = "0.8.0" @@ -1297,9 +1350,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] [[package]] name = "hashbrown" @@ -1307,7 +1357,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.3", + "ahash", "serde", ] @@ -1316,6 +1366,10 @@ name = "hashbrown" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" @@ -1547,9 +1601,9 @@ dependencies = [ [[package]] name = "iota-crypto" -version = "0.20.1" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb2df6763689b6e2b95787941eb9a58b29d16d17c2055392f550b3daf40bdce" +checksum = "6c7acb27266b648833a9fda748acbf1ac78e7ec6deaa42104b5e7a3f4ef913be" dependencies = [ "aead", "aes", @@ -1559,20 +1613,21 @@ dependencies = [ "blake2", "chacha20poly1305", "cipher", - "curve25519-dalek", + "curve25519-dalek 3.2.0", "digest 0.10.7", "ed25519-zebra", "generic-array", "getrandom", "hkdf", "hmac", + "iterator-sorted", "k256", "num-traits", "pbkdf2", "rand", "scrypt", "serde", - "sha2 0.10.7", + "sha2", "tiny-keccak", "unicode-normalization", "x25519-dalek", @@ -1640,7 +1695,6 @@ dependencies = [ "rocksdb", "rumqttc", "serde", - "serde-big-array", "serde_json", "serde_repr", "thiserror", @@ -1728,8 +1782,9 @@ dependencies = [ [[package]] name = "iota_stronghold" -version = "1.1.0" -source = "git+https://github.com/iotaledger/stronghold.rs?rev=9df6d19de325481771d69b47534d151e03eae623#9df6d19de325481771d69b47534d151e03eae623" +version = "2.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec36553cbc1499de54e3c6c6cc8f7f88108a6eb360ded33d97cd207a1b27859d" dependencies = [ "bincode", "hkdf", @@ -1809,7 +1864,8 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.7", + "serdect", + "sha2", ] [[package]] @@ -2245,6 +2301,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.0" @@ -2309,6 +2374,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "platforms" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + [[package]] name = "poly1305" version = "0.8.0" @@ -2351,9 +2422,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9825a04601d60621feed79c4e6b56d65db77cdca55cef43b46b0de1096d1c282" +checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387" dependencies = [ "proc-macro2", "syn 2.0.23", @@ -2556,18 +2627,32 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.4" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" [[package]] name = "reqwest" @@ -2704,9 +2789,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.2" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabcb0461ebd01d6b79945797c27f8529082226cb630a9865a71870ff63532a4" +checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" dependencies = [ "bitflags 2.3.3", "errno", @@ -2817,7 +2902,7 @@ checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" dependencies = [ "pbkdf2", "salsa20", - "sha2 0.10.7", + "sha2", ] [[package]] @@ -2840,6 +2925,7 @@ dependencies = [ "der", "generic-array", "pkcs8", + "serdect", "subtle", "zeroize", ] @@ -2897,15 +2983,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-big-array" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" -dependencies = [ - "serde", -] - [[package]] name = "serde_derive" version = "1.0.166" @@ -2919,9 +2996,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" +checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" dependencies = [ "itoa", "ryu", @@ -2952,23 +3029,20 @@ dependencies = [ ] [[package]] -name = "sha-1" -version = "0.9.8" +name = "serdect" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "base16ct", + "serde", ] [[package]] -name = "sha2" -version = "0.9.9" +name = "sha-1" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ "block-buffer 0.9.0", "cfg-if", @@ -3091,7 +3165,8 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stronghold-derive" version = "1.0.0" -source = "git+https://github.com/iotaledger/stronghold.rs?rev=9df6d19de325481771d69b47534d151e03eae623#9df6d19de325481771d69b47534d151e03eae623" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2835db23c4724c05a2f85b81c4681f4aa8ea158edc8a7f4ad791c916fb766c2e" dependencies = [ "proc-macro2", "quote", @@ -3100,8 +3175,9 @@ dependencies = [ [[package]] name = "stronghold-runtime" -version = "1.1.0" -source = "git+https://github.com/iotaledger/stronghold.rs?rev=9df6d19de325481771d69b47534d151e03eae623#9df6d19de325481771d69b47534d151e03eae623" +version = "2.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ded9181ba64ef82d0c417cd10779e76900e057f19e135659652f3d5b5d84b6" dependencies = [ "dirs", "iota-crypto", @@ -3119,7 +3195,8 @@ dependencies = [ [[package]] name = "stronghold-utils" version = "1.0.0" -source = "git+https://github.com/iotaledger/stronghold.rs?rev=9df6d19de325481771d69b47534d151e03eae623#9df6d19de325481771d69b47534d151e03eae623" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8300214898af5e153e7f66e49dbd1c6a21585f2d592d9f24f58b969792475ed6" dependencies = [ "rand", "stronghold-derive", @@ -3127,8 +3204,9 @@ dependencies = [ [[package]] name = "stronghold_engine" -version = "1.1.0" -source = "git+https://github.com/iotaledger/stronghold.rs?rev=9df6d19de325481771d69b47534d151e03eae623#9df6d19de325481771d69b47534d151e03eae623" +version = "2.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e502cccc6c0db434c651accc23c8cd643fc5e930e5effdf9cafac0ea79b037cd" dependencies = [ "anyhow", "dirs-next", @@ -3201,18 +3279,18 @@ checksum = "1b1c7f239eb94671427157bd93b3694320f3668d4e1eff08c7285366fd777fac" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59" dependencies = [ "proc-macro2", "quote", @@ -3921,7 +3999,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "rand_core 0.5.1", "zeroize", ] diff --git a/bindings/core/Cargo.toml b/bindings/core/Cargo.toml index eac6dd049e..9c48bb2c73 100644 --- a/bindings/core/Cargo.toml +++ b/bindings/core/Cargo.toml @@ -15,7 +15,7 @@ backtrace = { version = "0.3.67", default-features = false, features = [ "std" ] derivative = { version = "2.2.0", default-features = false } fern-logger = { version = "0.5.0", default-features = false } futures = { version = "0.3.28", default-features = false } -iota-crypto = { version = "0.20.1", default-features = false, features = [ "slip10" ] } +iota-crypto = { version = "0.22.1", default-features = false, features = [ "slip10" ] } log = { version = "0.4.18", default-features = false } packable = { version = "0.8.1", default-features = false } prefix-hex = { version = "0.7.0", default-features = false } diff --git a/bindings/core/src/message_interface_old/account_method.rs b/bindings/core/src/message_interface_old/account_method.rs index a5b1d1a8d4..a1299aeb1d 100644 --- a/bindings/core/src/message_interface_old/account_method.rs +++ b/bindings/core/src/message_interface_old/account_method.rs @@ -1,6 +1,7 @@ // Copyright 2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use crypto::keys::bip44::Bip44; #[cfg(feature = "participation")] use iota_sdk::wallet::account::types::participation::ParticipationEventRegistrationOptions; #[cfg(feature = "participation")] @@ -24,6 +25,7 @@ use iota_sdk::{ payload::transaction::TransactionId, signature::dto::Ed25519SignatureDto, }, + utils::serde::bip44::Bip44Def, wallet::{ account::{ CreateAliasParams, CreateNativeTokenParams, FilterOptions, MintNftParams, OutputParams, OutputsToClaim, @@ -181,7 +183,8 @@ pub enum AccountMethod { /// The message to sign, hex encoded String message: String, /// Chain to sign the message with - chain: Vec, + #[serde(with = "Bip44Def")] + chain: Bip44, }, /// Get the [`OutputData`](crate::wallet::account::types::OutputData) of an output stored in the account /// Expected response: [`OutputData`](crate::wallet::message_interface::Response::OutputData) @@ -438,3 +441,30 @@ pub enum AccountMethod { /// Expected response: [`Faucet`](crate::wallet::message_interface::Response::Faucet) RequestFundsFromFaucet { url: String, address: Bech32Address }, } + +#[cfg(test)] +mod test { + #[test] + fn bip44_deserialization() { + let sign_secp256k1_ecdsa_method: super::AccountMethod = serde_json::from_str( + r#"{"name": "signSecp256k1Ecdsa", "data": {"message": "0xFFFFFFFF", "chain": {"account": 2, "addressIndex": 1}}}"#, + ) + .unwrap(); + + assert_eq!( + serde_json::to_value(&sign_secp256k1_ecdsa_method).unwrap(), + serde_json::json!({ + "name": "signSecp256k1Ecdsa", + "data": { + "message": "0xFFFFFFFF", + "chain": { + "coinType": 4218, + "account": 2, + "change": 0, + "addressIndex": 1 + } + } + }) + ); + } +} diff --git a/bindings/core/src/message_interface_old/message_handler.rs b/bindings/core/src/message_interface_old/message_handler.rs index 817c9a8032..20f70e73f5 100644 --- a/bindings/core/src/message_interface_old/message_handler.rs +++ b/bindings/core/src/message_interface_old/message_handler.rs @@ -10,7 +10,7 @@ use std::{ }; use backtrace::Backtrace; -use crypto::keys::slip10::Chain; +use crypto::keys::bip39::Mnemonic; use futures::{Future, FutureExt}; #[cfg(feature = "events")] use iota_sdk::wallet::events::types::{Event, WalletEventType}; @@ -39,7 +39,6 @@ use iota_sdk::{ Result, Wallet, }, }; -use zeroize::Zeroize; use crate::message_interface_old::{account_method::AccountMethod, message::Message, response::Response}; @@ -230,12 +229,14 @@ impl WalletMessageHandler { }) .await } - Message::GenerateMnemonic => { - convert_panics(|| self.wallet.generate_mnemonic().map(Response::GeneratedMnemonic)) - } - Message::VerifyMnemonic { mut mnemonic } => convert_panics(|| { + Message::GenerateMnemonic => convert_panics(|| { + self.wallet + .generate_mnemonic() + .map(|m| Response::GeneratedMnemonic(m.as_ref().to_owned())) + }), + Message::VerifyMnemonic { mnemonic } => convert_panics(|| { + let mnemonic = Mnemonic::from(mnemonic); self.wallet.verify_mnemonic(&mnemonic)?; - mnemonic.zeroize(); Ok(Response::Ok(())) }), Message::SetClientOptions { client_options } => { @@ -307,6 +308,7 @@ impl WalletMessageHandler { } #[cfg(feature = "stronghold")] Message::StoreMnemonic { mnemonic } => { + let mnemonic = mnemonic.into(); convert_async_panics(|| async { self.wallet.store_mnemonic(mnemonic).await?; Ok(Response::Ok(())) @@ -574,7 +576,7 @@ impl WalletMessageHandler { AccountMethod::VerifyEd25519Signature { signature, message } => { let signature = Ed25519Signature::try_from(signature)?; let message: Vec = prefix_hex::decode(message).map_err(iota_sdk::client::Error::from)?; - Ok(Response::Bool(signature.verify(&message)?)) + Ok(Response::Bool(signature.verify(&message))) } AccountMethod::VerifySecp256k1EcdsaSignature { public_key, @@ -587,7 +589,7 @@ impl WalletMessageHandler { let signature = prefix_hex::decode(signature).map_err(|_| Error::InvalidField("signature"))?; let signature = secp256k1_ecdsa::Signature::try_from_bytes(&signature)?; let message: Vec = prefix_hex::decode(message).map_err(iota_sdk::client::Error::from)?; - Ok(Response::Bool(public_key.verify(&signature, &message))) + Ok(Response::Bool(public_key.verify_keccak256(&signature, &message))) } AccountMethod::SignSecp256k1Ecdsa { message, chain } => { let msg: Vec = prefix_hex::decode(message).map_err(iota_sdk::client::Error::from)?; @@ -595,7 +597,7 @@ impl WalletMessageHandler { .get_secret_manager() .read() .await - .sign_secp256k1_ecdsa(&msg, &Chain::from_u32(chain)) + .sign_secp256k1_ecdsa(&msg, chain) .await?; Ok(Response::Secp256k1EcdsaSignature { public_key: prefix_hex::encode(public_key.to_bytes()), diff --git a/bindings/core/src/method/secret_manager.rs b/bindings/core/src/method/secret_manager.rs index c715b93b87..8648aa9f33 100644 --- a/bindings/core/src/method/secret_manager.rs +++ b/bindings/core/src/method/secret_manager.rs @@ -1,8 +1,12 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use crypto::keys::bip44::Bip44; use derivative::Derivative; -use iota_sdk::client::api::{GetAddressesOptions, PreparedTransactionDataDto}; +use iota_sdk::{ + client::api::{GetAddressesOptions, PreparedTransactionDataDto}, + utils::serde::bip44::Bip44Def, +}; use serde::{Deserialize, Serialize}; #[cfg(feature = "stronghold")] @@ -31,21 +35,24 @@ pub enum SecretManagerMethod { /// Transaction Essence Hash transaction_essence_hash: String, /// Chain to sign the essence hash with - chain: Vec, + #[serde(with = "Bip44Def")] + chain: Bip44, }, /// Signs a message with an Ed25519 private key. SignEd25519 { /// The message to sign, hex encoded String message: String, /// Chain to sign the essence hash with - chain: Vec, + #[serde(with = "Bip44Def")] + chain: Bip44, }, /// Signs a message with an Secp256k1Ecdsa private key. SignSecp256k1Ecdsa { /// The message to sign, hex encoded String message: String, /// Chain to sign the message with - chain: Vec, + #[serde(with = "Bip44Def")] + chain: Bip44, }, /// Sign a transaction #[serde(rename_all = "camelCase")] @@ -62,3 +69,72 @@ pub enum SecretManagerMethod { mnemonic: String, }, } + +#[cfg(test)] +mod test { + #[test] + fn bip44_deserialization() { + let signature_unlock_method: super::SecretManagerMethod = serde_json::from_str( + r#"{"name": "signatureUnlock", "data": {"transactionEssenceHash": "txhash", "chain": {"addressIndex": 1}}}"#, + ) + .unwrap(); + + assert_eq!( + serde_json::to_value(&signature_unlock_method).unwrap(), + serde_json::json!({ + "name": "signatureUnlock", + "data": { + "transactionEssenceHash": "txhash", + "chain": { + "coinType": 4218, + "account": 0, + "change": 0, + "addressIndex": 1 + } + } + }) + ); + + let sign_ed25519_method: super::SecretManagerMethod = serde_json::from_str( + r#"{"name": "signEd25519", "data": {"message": "0xFFFFFFFF", "chain": {"coinType": 60, "change": 1}}}"#, + ) + .unwrap(); + + assert_eq!( + serde_json::to_value(&sign_ed25519_method).unwrap(), + serde_json::json!({ + "name": "signEd25519", + "data": { + "message": "0xFFFFFFFF", + "chain": { + "coinType": 60, + "account": 0, + "change": 1, + "addressIndex": 0 + } + } + }) + ); + + let sign_secp256k1_ecdsa_method: super::SecretManagerMethod = serde_json::from_str( + r#"{"name": "signSecp256k1Ecdsa", "data": {"message": "0xFFFFFFFF", "chain": {"account": 2, "addressIndex": 1}}}"#, + ) + .unwrap(); + + assert_eq!( + serde_json::to_value(&sign_secp256k1_ecdsa_method).unwrap(), + serde_json::json!({ + "name": "signSecp256k1Ecdsa", + "data": { + "message": "0xFFFFFFFF", + "chain": { + "coinType": 4218, + "account": 2, + "change": 0, + "addressIndex": 1 + } + } + }) + ); + } +} diff --git a/bindings/core/src/method_handler/secret_manager.rs b/bindings/core/src/method_handler/secret_manager.rs index 0655b06448..2500d9d123 100644 --- a/bindings/core/src/method_handler/secret_manager.rs +++ b/bindings/core/src/method_handler/secret_manager.rs @@ -1,7 +1,6 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crypto::keys::slip10::Chain; use iota_sdk::{ client::{ api::PreparedTransactionData, @@ -52,23 +51,19 @@ pub(crate) async fn call_secret_manager_method_internal( } => { let transaction_essence_hash: [u8; 32] = prefix_hex::decode(transaction_essence_hash)?; let unlock: Unlock = secret_manager - .signature_unlock(&transaction_essence_hash, &Chain::from_u32_hardened(chain)) + .signature_unlock(&transaction_essence_hash, chain) .await?; Response::SignatureUnlock((&unlock).into()) } SecretManagerMethod::SignEd25519 { message, chain } => { let msg: Vec = prefix_hex::decode(message)?; - let signature = secret_manager - .sign_ed25519(&msg, &Chain::from_u32_hardened(chain)) - .await?; + let signature = secret_manager.sign_ed25519(&msg, chain).await?; Response::Ed25519Signature(Ed25519SignatureDto::from(&signature)) } SecretManagerMethod::SignSecp256k1Ecdsa { message, chain } => { let msg: Vec = prefix_hex::decode(message)?; - let (public_key, signature) = secret_manager - .sign_secp256k1_ecdsa(&msg, &Chain::from_u32(chain)) - .await?; + let (public_key, signature) = secret_manager.sign_secp256k1_ecdsa(&msg, chain).await?; Response::Secp256k1EcdsaSignature { public_key: prefix_hex::encode(public_key.to_bytes()), signature: prefix_hex::encode(signature.to_bytes()), @@ -76,6 +71,7 @@ pub(crate) async fn call_secret_manager_method_internal( } #[cfg(feature = "stronghold")] SecretManagerMethod::StoreMnemonic { mnemonic } => { + let mnemonic = crypto::keys::bip39::Mnemonic::from(mnemonic); if let SecretManager::Stronghold(secret_manager) = &*secret_manager { secret_manager.store_mnemonic(mnemonic).await?; Response::Ok diff --git a/bindings/core/src/method_handler/utils.rs b/bindings/core/src/method_handler/utils.rs index 890a92bcf9..bd1bacc009 100644 --- a/bindings/core/src/method_handler/utils.rs +++ b/bindings/core/src/method_handler/utils.rs @@ -1,6 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use crypto::keys::bip39::Mnemonic; use iota_sdk::{ client::{hex_public_key_to_bech32_address, hex_to_bech32, verify_mnemonic, Client}, types::block::{ @@ -11,7 +12,6 @@ use iota_sdk::{ Block, }, }; -use zeroize::Zeroize; use crate::{method::UtilsMethod, response::Response, Result}; @@ -29,11 +29,10 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result Response::ParsedBech32Address(AddressDto::from(address.inner())), UtilsMethod::IsAddressValid { address } => Response::Bool(Address::is_valid_bech32(&address)), - UtilsMethod::GenerateMnemonic => Response::GeneratedMnemonic(Client::generate_mnemonic()?), - UtilsMethod::MnemonicToHexSeed { mut mnemonic } => { - let response = Response::MnemonicHexSeed(Client::mnemonic_to_hex_seed(&mnemonic)?); - mnemonic.zeroize(); - response + UtilsMethod::GenerateMnemonic => Response::GeneratedMnemonic(Client::generate_mnemonic()?.to_string()), + UtilsMethod::MnemonicToHexSeed { mnemonic } => { + let mnemonic = Mnemonic::from(mnemonic); + Response::MnemonicHexSeed(Client::mnemonic_to_hex_seed(mnemonic)?) } UtilsMethod::BlockId { block } => { let block = Block::try_from_dto_unverified(block)?; @@ -81,15 +80,15 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result { - verify_mnemonic(&mnemonic)?; - mnemonic.zeroize(); + UtilsMethod::VerifyMnemonic { mnemonic } => { + let mnemonic = Mnemonic::from(mnemonic); + verify_mnemonic(mnemonic)?; Response::Ok } UtilsMethod::VerifyEd25519Signature { signature, message } => { let signature = Ed25519Signature::try_from(signature)?; let message: Vec = prefix_hex::decode(message)?; - Response::Bool(signature.verify(&message)?) + Response::Bool(signature.verify(&message)) } UtilsMethod::VerifySecp256k1EcdsaSignature { public_key, @@ -103,7 +102,7 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result = prefix_hex::decode(message)?; - Response::Bool(public_key.verify(&signature, &message)) + Response::Bool(public_key.verify_keccak256(&signature, &message)) } }; Ok(response) diff --git a/bindings/core/src/method_handler/wallet.rs b/bindings/core/src/method_handler/wallet.rs index dbc138cc43..e015a7f9ab 100644 --- a/bindings/core/src/method_handler/wallet.rs +++ b/bindings/core/src/method_handler/wallet.rs @@ -162,7 +162,7 @@ pub(crate) async fn call_wallet_method_internal(wallet: &Wallet, method: WalletM } #[cfg(feature = "stronghold")] WalletMethod::StoreMnemonic { mnemonic } => { - wallet.store_mnemonic(mnemonic).await?; + wallet.store_mnemonic(mnemonic.into()).await?; Response::Ok } WalletMethod::StartBackgroundSync { diff --git a/bindings/nodejs-old/lib/Account.ts b/bindings/nodejs-old/lib/Account.ts index a49d841ea4..535d7f59ee 100644 --- a/bindings/nodejs-old/lib/Account.ts +++ b/bindings/nodejs-old/lib/Account.ts @@ -35,6 +35,7 @@ import type { GenerateAddressesOptions, Secp256k1EcdsaSignature, Ed25519Signature, + Bip44, } from '../types'; import type { SignedTransactionEssence } from '../types/signedTransactionEssence'; import type { @@ -441,7 +442,7 @@ export class Account { */ async signSecp256k1Ecdsa( message: HexEncodedString, - chain: number[], + chain: Bip44, ): Promise { const response = await this.messageHandler.callAccountMethod( this.meta.index, diff --git a/bindings/nodejs-old/types/bridge/account.ts b/bindings/nodejs-old/types/bridge/account.ts index 477248c66e..466ae330e2 100644 --- a/bindings/nodejs-old/types/bridge/account.ts +++ b/bindings/nodejs-old/types/bridge/account.ts @@ -33,7 +33,7 @@ import type { ParticipationEventRegistrationOptions, ParticipationEventType, } from '../participation'; -import { Ed25519Signature } from '../secretManager'; +import { Bip44, Ed25519Signature } from '../secretManager'; export type __BuildAliasOutputMethod__ = { name: 'buildAliasOutput'; @@ -163,7 +163,7 @@ export type __SignSecp256k1EcdsaMethod__ = { name: 'signSecp256k1Ecdsa'; data: { message: HexEncodedString; - chain: number[]; + chain: Bip44; }; }; diff --git a/bindings/nodejs-old/types/secretManager.ts b/bindings/nodejs-old/types/secretManager.ts index 8345d3c862..dfbd433dc2 100644 --- a/bindings/nodejs-old/types/secretManager.ts +++ b/bindings/nodejs-old/types/secretManager.ts @@ -78,3 +78,10 @@ export interface Ed25519Signature { */ signature: HexEncodedString; } + +export interface Bip44 { + coinType?: number; + account?: number; + change?: number; + addressIndex?: number; +} diff --git a/bindings/nodejs/CHANGELOG.md b/bindings/nodejs/CHANGELOG.md index 61d45b1580..91e065f40d 100644 --- a/bindings/nodejs/CHANGELOG.md +++ b/bindings/nodejs/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Rename `Client::listen` to `listenMqtt`, `Client::clearListeners` to `clearMqttListeners`; - Moved `minimumRequiredStorageDeposit()` from `Account` to `Client`; +- `SecretManagerMethod::SignEd25519`, `SignSecp256k1Ecdsa`, and `SignatureUnlock` now accept newly added `Bip44` type chains; ### Fixed diff --git a/bindings/nodejs/examples/how_tos/sign_and_verify_ed25519/sign-ed25519.ts b/bindings/nodejs/examples/how_tos/sign_and_verify_ed25519/sign-ed25519.ts index 5590a8f8b0..9fc3b99e6e 100644 --- a/bindings/nodejs/examples/how_tos/sign_and_verify_ed25519/sign-ed25519.ts +++ b/bindings/nodejs/examples/how_tos/sign_and_verify_ed25519/sign-ed25519.ts @@ -3,7 +3,6 @@ import { CoinType, - HD_WALLET_TYPE, initLogger, SecretManager, utf8ToHex, @@ -51,17 +50,16 @@ async function run() { // The mnemonic can't be retrieved from the Stronghold file, so make a backup in a secure place! await secretManager.storeMnemonic(process.env.MNEMONIC); - const bip32Chain = [ - HD_WALLET_TYPE, - CoinType.Shimmer, - ACCOUNT_INDEX, - INTERNAL_ADDRESS ? 1 : 0, - ADDRESS_INDEX, - ]; + const bip44Chain = { + coinType: CoinType.Shimmer, + account: ACCOUNT_INDEX, + change: INTERNAL_ADDRESS ? 1 : 0, + addressIndex: ADDRESS_INDEX, + }; const message = utf8ToHex(JSON.stringify(FOUNDRY_METADATA)); const ed25519Signature = await secretManager.signEd25519( message, - bip32Chain, + bip44Chain, ); console.log( `Public key: ${ed25519Signature.publicKey}\nSignature: ${ed25519Signature.signature}`, diff --git a/bindings/nodejs/examples/how_tos/sign_secp256k1_ecdsa/sign-secp256k1_ecdsa.ts b/bindings/nodejs/examples/how_tos/sign_secp256k1_ecdsa/sign-secp256k1_ecdsa.ts index 251d4eb0ed..b5998c76c8 100644 --- a/bindings/nodejs/examples/how_tos/sign_secp256k1_ecdsa/sign-secp256k1_ecdsa.ts +++ b/bindings/nodejs/examples/how_tos/sign_secp256k1_ecdsa/sign-secp256k1_ecdsa.ts @@ -1,14 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { - CoinType, - HD_WALLET_TYPE, - HARDEN_MASK, - initLogger, - SecretManager, - utf8ToHex, -} from '@iota/sdk'; +import { CoinType, initLogger, SecretManager, utf8ToHex } from '@iota/sdk'; require('dotenv').config({ path: '.env' }); // In this example we will sign with secp256k1_ecdsa. @@ -51,17 +44,16 @@ async function run() { // The mnemonic can't be retrieved from the Stronghold file, so make a backup in a secure place! await secretManager.storeMnemonic(process.env.MNEMONIC); - const bip32Chain = [ - (HD_WALLET_TYPE | HARDEN_MASK) >>> 0, - (CoinType.Ether | HARDEN_MASK) >>> 0, - (ACCOUNT_INDEX | HARDEN_MASK) >>> 0, - INTERNAL_ADDRESS ? 1 : 0, - ADDRESS_INDEX, - ]; + const bip44Chain = { + coinType: CoinType.Ether, + account: ACCOUNT_INDEX, + change: INTERNAL_ADDRESS ? 1 : 0, + addressIndex: ADDRESS_INDEX, + }; const message = utf8ToHex(JSON.stringify(FOUNDRY_METADATA)); const secp256k1EcdsaSignature = await secretManager.signSecp256k1Ecdsa( message, - bip32Chain, + bip44Chain, ); console.log(`Public key: ${secp256k1EcdsaSignature.publicKey}`); diff --git a/bindings/nodejs/lib/client/client.ts b/bindings/nodejs/lib/client/client.ts index 9345dcb92b..78fc4651f7 100644 --- a/bindings/nodejs/lib/client/client.ts +++ b/bindings/nodejs/lib/client/client.ts @@ -18,10 +18,12 @@ import { FoundryQueryParameter, NftQueryParameter, AliasQueryParameter, - IBip32Chain, } from '../types/client'; import type { INodeInfoWrapper } from '../types/client/nodeInfo'; -import { SecretManagerType } from '../types/secret_manager/secret-manager'; +import { + Bip44, + SecretManagerType, +} from '../types/secret_manager/secret-manager'; import { AliasOutput, BasicOutput, @@ -286,7 +288,7 @@ export class Client { async signatureUnlock( secretManager: SecretManagerType, transactionEssenceHash: HexEncodedString, - chain: IBip32Chain, + chain: Bip44, ): Promise { const response = await this.methodHandler.callMethod({ name: 'signatureUnlock', diff --git a/bindings/nodejs/lib/secret_manager/secret-manager.ts b/bindings/nodejs/lib/secret_manager/secret-manager.ts index 42c1d0179e..525947b775 100644 --- a/bindings/nodejs/lib/secret_manager/secret-manager.ts +++ b/bindings/nodejs/lib/secret_manager/secret-manager.ts @@ -6,9 +6,9 @@ import type { IGenerateAddressesOptions, PreparedTransactionData, LedgerNanoStatus, - IBip32Chain, } from '../types/client'; import { + Bip44, Secp256k1EcdsaSignature, SecretManagerType, } from '../types/secret_manager'; @@ -85,7 +85,7 @@ export class SecretManager { */ async signatureUnlock( transactionEssenceHash: HexEncodedString, - chain: IBip32Chain, + chain: Bip44, ): Promise { const response = await this.methodHandler.callMethod({ name: 'signatureUnlock', @@ -103,7 +103,7 @@ export class SecretManager { */ async signEd25519( message: HexEncodedString, - chain: IBip32Chain, + chain: Bip44, ): Promise { const response = await this.methodHandler.callMethod({ name: 'signEd25519', @@ -120,7 +120,7 @@ export class SecretManager { */ async signSecp256k1Ecdsa( message: HexEncodedString, - chain: IBip32Chain, + chain: Bip44, ): Promise { const response = await this.methodHandler.callMethod({ name: 'signSecp256k1Ecdsa', diff --git a/bindings/nodejs/lib/types/client/bridge/client.ts b/bindings/nodejs/lib/types/client/bridge/client.ts index 9ea3a3a9c4..63075eb4b7 100644 --- a/bindings/nodejs/lib/types/client/bridge/client.ts +++ b/bindings/nodejs/lib/types/client/bridge/client.ts @@ -1,14 +1,14 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import type { SecretManagerType } from '../../secret_manager/secret-manager'; +import type { + Bip44, + SecretManagerType, +} from '../../secret_manager/secret-manager'; import type { IGenerateAddressesOptions } from '../generate-addresses-options'; import type { IBuildBlockOptions } from '../build-block-options'; import type { Block, BlockId, Output, Payload } from '../../block'; -import type { - PreparedTransactionData, - IBip32Chain, -} from '../prepared-transaction-data'; +import type { PreparedTransactionData } from '../prepared-transaction-data'; import type { AliasQueryParameter, FoundryQueryParameter, @@ -121,7 +121,7 @@ export interface __SignatureUnlockMethod__ { data: { secretManager: SecretManagerType; transactionEssenceHash: HexEncodedString; - chain: IBip32Chain; + chain: Bip44; }; } diff --git a/bindings/nodejs/lib/types/client/prepared-transaction-data.ts b/bindings/nodejs/lib/types/client/prepared-transaction-data.ts index a9d924419f..64dc9354f5 100644 --- a/bindings/nodejs/lib/types/client/prepared-transaction-data.ts +++ b/bindings/nodejs/lib/types/client/prepared-transaction-data.ts @@ -9,6 +9,7 @@ import { TransactionEssenceDiscriminator, } from '../block/payload/transaction/essence'; import { IOutputMetadataResponse } from '../models/api'; +import { Bip44 } from '../secret_manager'; /** * Helper struct for offline signing @@ -49,7 +50,7 @@ export class InputSigningData { /** * The chain derived from seed, only for ed25519 addresses */ - chain?: IBip32Chain; + chain?: Bip44; } export class Remainder { @@ -63,7 +64,7 @@ export class Remainder { /** * The chain derived from seed, for the remainder addresses */ - chain?: IBip32Chain; + chain?: Bip44; /** * The remainder address */ @@ -72,8 +73,3 @@ export class Remainder { }) address!: Address; } - -/** - * BIP 32 chain. - */ -export type IBip32Chain = number[]; diff --git a/bindings/nodejs/lib/types/secret_manager/bridge/secret-manager.ts b/bindings/nodejs/lib/types/secret_manager/bridge/secret-manager.ts index 4544decab9..4f3552ab6d 100644 --- a/bindings/nodejs/lib/types/secret_manager/bridge/secret-manager.ts +++ b/bindings/nodejs/lib/types/secret_manager/bridge/secret-manager.ts @@ -2,11 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import type { IGenerateAddressesOptions } from '../../client/generate-addresses-options'; -import type { - PreparedTransactionData, - IBip32Chain, -} from '../../client/prepared-transaction-data'; +import type { PreparedTransactionData } from '../../client/prepared-transaction-data'; import { HexEncodedString } from '../../utils'; +import { Bip44 } from '../secret-manager'; export interface __GenerateEd25519AddressesMethod__ { name: 'generateEd25519Addresses'; @@ -33,7 +31,7 @@ export interface __SignatureUnlockMethod__ { name: 'signatureUnlock'; data: { transactionEssenceHash: HexEncodedString; - chain: IBip32Chain; + chain: Bip44; }; } @@ -48,7 +46,7 @@ export interface __SignEd25519Method__ { name: 'signEd25519'; data: { message: HexEncodedString; - chain: IBip32Chain; + chain: Bip44; }; } @@ -56,7 +54,7 @@ export interface __SignSecp256k1EcdsaMethod__ { name: 'signSecp256k1Ecdsa'; data: { message: HexEncodedString; - chain: IBip32Chain; + chain: Bip44; }; } diff --git a/bindings/nodejs/lib/types/secret_manager/secret-manager.ts b/bindings/nodejs/lib/types/secret_manager/secret-manager.ts index 78ee937c8c..9f42477673 100644 --- a/bindings/nodejs/lib/types/secret_manager/secret-manager.ts +++ b/bindings/nodejs/lib/types/secret_manager/secret-manager.ts @@ -47,3 +47,10 @@ export interface Secp256k1EcdsaSignature { */ signature: HexEncodedString; } + +export interface Bip44 { + coinType?: number; + account?: number; + change?: number; + addressIndex?: number; +} diff --git a/bindings/nodejs/tests/client/utilityMethods.spec.ts b/bindings/nodejs/tests/client/utilityMethods.spec.ts index ec67f9ce76..7e71351d82 100644 --- a/bindings/nodejs/tests/client/utilityMethods.spec.ts +++ b/bindings/nodejs/tests/client/utilityMethods.spec.ts @@ -119,8 +119,12 @@ describe('Client utility methods', () => { const message = '0x494f5441'; const signature = await new SecretManager(secretManager).signEd25519( message, - // HD-Wallet type, IOTA coin type, first account, public, first address - [44, 4218, 0, 0, 0], + { + coinType: 4218, + account: 0, + change: 0, + addressIndex: 0, + }, ); const validSignature = Utils.verifyEd25519Signature(signature, message); diff --git a/bindings/nodejs/tests/fixtures/sigUnlockPreparedTx.json b/bindings/nodejs/tests/fixtures/sigUnlockPreparedTx.json index 3664b61d69..9614f0d99b 100644 --- a/bindings/nodejs/tests/fixtures/sigUnlockPreparedTx.json +++ b/bindings/nodejs/tests/fixtures/sigUnlockPreparedTx.json @@ -63,13 +63,9 @@ "milestoneTimestampBooked": 1675248149, "ledgerIndex": 3527507 }, - "chain": [ - 44, - 4219, - 0, - 0, - 0 - ], + "chain": { + "coinType": 4219 + }, "bech32Address": "rms1qz0krpgy6xj6p7dkrg9x37uy08yr3n3fau02cdm7qalg7ynqcswywncmd9p" } ], @@ -87,16 +83,12 @@ } ] }, - "chain": [ - 44, - 4219, - 0, - 0, - 0 - ], + "chain": { + "coinType": 4219 + }, "address": { "type": 0, "pubKeyHash": "0x9f618504d1a5a0f9b61a0a68fb8479c838ce29ef1eac377e077e8f1260c41c47" } } -} \ No newline at end of file +} diff --git a/bindings/python/CHANGELOG.md b/bindings/python/CHANGELOG.md index f98993d071..5552859419 100644 --- a/bindings/python/CHANGELOG.md +++ b/bindings/python/CHANGELOG.md @@ -24,12 +24,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `Account::get_metadata()`; +- `Bip44` type; - `SendParams, SendNativeTokensParams, SendNftParams, CreateNativeTokenParams, MintNftParams, CreateAliasOutputParams`; ### Changed - Moved `minimum_required_storage_deposit()` from `Account` to `Client`; - `Wallet::create_account()` returns `Account` now; +- `SecretManager::{sign_ed25519, sign_secp256k1_ecdsa, signature_unlock}` now accept `Bip44` type chains; - Renamed `SendParams` to `AddressAndAmount`; - `Account::prepare_create_alias_output()` now accepts `CreateAliasOutputParams`; - `Account::prepare_create_native_token()` now accepts `CreateNativeTokenParams`; diff --git a/bindings/python/examples/how_tos/sign_and_verify_ed25519/sign_ed25519.py b/bindings/python/examples/how_tos/sign_and_verify_ed25519/sign_ed25519.py index c1bc074e68..79fc3a0982 100644 --- a/bindings/python/examples/how_tos/sign_and_verify_ed25519/sign_ed25519.py +++ b/bindings/python/examples/how_tos/sign_and_verify_ed25519/sign_ed25519.py @@ -1,4 +1,4 @@ -from iota_sdk import Client, StrongholdSecretManager, SecretManager, HD_WALLET_TYPE, CoinType, Utils, utf8_to_hex +from iota_sdk import Client, StrongholdSecretManager, SecretManager, Bip44, CoinType, Utils, utf8_to_hex from dotenv import load_dotenv import os @@ -25,16 +25,15 @@ secret_manager.store_mnemonic( os.environ['MNEMONIC']) -bip32_chain = [ - HD_WALLET_TYPE, +bip44_chain = Bip44( CoinType.SHIMMER, ACCOUNT_INDEX, 1 if INTERNAL_ADDRESS else 0, ADDRESS_INDEX, -] +) message = utf8_to_hex(FOUNDRY_METADATA) -ed25519_signature = secret_manager.sign_ed25519(message, bip32_chain) +ed25519_signature = secret_manager.sign_ed25519(message, bip44_chain) print( f'Public key: {ed25519_signature.publicKey}\nSignature: {ed25519_signature.signature}') diff --git a/bindings/python/examples/how_tos/sign_evm/sign_evm.py b/bindings/python/examples/how_tos/sign_evm/sign_evm.py index 3daa9eaf42..73dbb7e318 100644 --- a/bindings/python/examples/how_tos/sign_evm/sign_evm.py +++ b/bindings/python/examples/how_tos/sign_evm/sign_evm.py @@ -1,4 +1,4 @@ -from iota_sdk import Client, StrongholdSecretManager, SecretManager, HD_WALLET_TYPE, HARDEN_MASK, CoinType, Utils, utf8_to_hex +from iota_sdk import Client, StrongholdSecretManager, SecretManager, Bip44, CoinType, Utils, utf8_to_hex from dotenv import load_dotenv import os @@ -24,15 +24,14 @@ # The mnemonic can't be retrieved from the Stronghold file, so make a backup in a secure place! secret_manager.store_mnemonic(os.environ['MNEMONIC']) -bip32_chain = [ - HD_WALLET_TYPE | HARDEN_MASK, - CoinType.ETHER | HARDEN_MASK, - ACCOUNT_INDEX | HARDEN_MASK, +bip44_chain = Bip44( + CoinType.ETHER, + ACCOUNT_INDEX, 1 if INTERNAL_ADDRESS else 0, ADDRESS_INDEX, -] +) message = utf8_to_hex(FOUNDRY_METADATA) -secp256k1_ecdsa_signature = secret_manager.sign_secp256k1_ecdsa(message, bip32_chain) +secp256k1_ecdsa_signature = secret_manager.sign_secp256k1_ecdsa(message, bip44_chain) print(f'Public key: {secp256k1_ecdsa_signature["publicKey"]}') print(f'Signature: {secp256k1_ecdsa_signature["signature"]}') diff --git a/bindings/python/iota_sdk/secret_manager/secret_manager.py b/bindings/python/iota_sdk/secret_manager/secret_manager.py index 0162bbb782..2f18580d6b 100644 --- a/bindings/python/iota_sdk/secret_manager/secret_manager.py +++ b/bindings/python/iota_sdk/secret_manager/secret_manager.py @@ -3,7 +3,7 @@ from iota_sdk import create_secret_manager, call_secret_manager_method from iota_sdk.types.common import HexStr -from iota_sdk.types.signature import Ed25519Signature +from iota_sdk.types.signature import Ed25519Signature, Bip44 from json import dumps, loads import humps from typing import List, Optional @@ -217,20 +217,20 @@ def store_mnemonic(self, mnemonic: str): 'mnemonic': mnemonic }) - def sign_ed25519(self, message: HexStr, chain: List[int]) -> Ed25519Signature: + def sign_ed25519(self, message: HexStr, chain: Bip44) -> Ed25519Signature: """Signs a message with an Ed25519 private key. """ return from_dict(Ed25519Signature, self._call_method('signEd25519', { 'message': message, - 'chain': chain, + 'chain': chain.__dict__, })) - def sign_secp256k1_ecdsa(self, message: HexStr, chain: List[int]): + def sign_secp256k1_ecdsa(self, message: HexStr, chain: Bip44): """Signs a message with an Secp256k1Ecdsa private key. """ return self._call_method('signSecp256k1Ecdsa', { 'message': message, - 'chain': chain, + 'chain': chain.__dict__, }) def sign_transaction(self, prepared_transaction_data): @@ -240,10 +240,10 @@ def sign_transaction(self, prepared_transaction_data): 'preparedTransactionData': prepared_transaction_data }) - def signature_unlock(self, transaction_essence_hash: HexStr, chain: List[int]): + def signature_unlock(self, transaction_essence_hash: HexStr, chain: Bip44): """Sign a transaction essence hash. """ return self._call_method('signatureUnlock', { 'transactionEssenceHash': transaction_essence_hash, - 'chain': chain + 'chain': chain.__dict__, }) diff --git a/bindings/python/iota_sdk/types/signature.py b/bindings/python/iota_sdk/types/signature.py index 6da3b2a456..25a0cd3e94 100644 --- a/bindings/python/iota_sdk/types/signature.py +++ b/bindings/python/iota_sdk/types/signature.py @@ -2,10 +2,17 @@ # SPDX-License-Identifier: Apache-2.0 from dataclasses import dataclass -from iota_sdk.types.common import HexStr +from iota_sdk.types.common import HexStr, CoinType @dataclass class Ed25519Signature(): publicKey: HexStr signature: HexStr type: int = 0 + +@dataclass +class Bip44(): + coinType: int = CoinType.IOTA + account: int = 0 + change: int = 0 + addressIndex: int = 0 diff --git a/bindings/python/tests/test_offline.py b/bindings/python/tests/test_offline.py index 37a351f04b..028f9d8352 100644 --- a/bindings/python/tests/test_offline.py +++ b/bindings/python/tests/test_offline.py @@ -2,7 +2,7 @@ # Copyright 2023 IOTA Stiftung # SPDX-License-Identifier: Apache-2.0 -from iota_sdk import Block, Client, MnemonicSecretManager, Utils, SecretManager, OutputId, hex_to_utf8, utf8_to_hex +from iota_sdk import Block, Client, MnemonicSecretManager, Utils, SecretManager, OutputId, hex_to_utf8, utf8_to_hex, Bip44, CoinType import json import unittest @@ -40,8 +40,8 @@ def test_sign_verify_ed25519(): secret_manager = SecretManager(secret_manager) signature = secret_manager.sign_ed25519( message, - # HD-Wallet type, IOTA coin type, first account, public, first address - [44, 4218, 0, 0, 0], + # IOTA coin type + Bip44(CoinType.IOTA), ) assert signature.signature == '0x72bf2bc8fbc5dc56d657d7de8afa5208be1db025851e81031c754b371c7a29ce9f352d12df8207f9163316f81f59eb7725e5c0e4f3228e71ffe3764a9de6b10e' diff --git a/cli/src/helper.rs b/cli/src/helper.rs index edb177222a..f0915e31d9 100644 --- a/cli/src/helper.rs +++ b/cli/src/helper.rs @@ -8,12 +8,14 @@ use clap::Parser; use dialoguer::{console::Term, theme::ColorfulTheme, Input, Select}; use iota_sdk::{ client::{utils::Password, verify_mnemonic}, + crypto::keys::bip39::Mnemonic, wallet::{Account, Wallet}, }; use tokio::{ fs::{self, OpenOptions}, io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, }; +use zeroize::Zeroize; use crate::{ command::{account::AccountCli, wallet::WalletCli}, @@ -110,7 +112,7 @@ pub async fn bytes_from_hex_or_file(hex: Option, file: Option) - }) } -pub async fn enter_or_generate_mnemonic() -> Result { +pub async fn enter_or_generate_mnemonic() -> Result { let choices = ["Generate a new mnemonic", "Enter a mnemonic"]; let selected_choice = Select::with_theme(&ColorfulTheme::default()) .with_prompt("Select how to provide a mnemonic") @@ -127,7 +129,7 @@ pub async fn enter_or_generate_mnemonic() -> Result { Ok(mnemnonic) } -pub async fn generate_mnemonic() -> Result { +pub async fn generate_mnemonic() -> Result { let mnemonic = iota_sdk::client::generate_mnemonic()?; println_log_info!("Mnemonic has been generated."); let choices = [ @@ -144,7 +146,7 @@ pub async fn generate_mnemonic() -> Result { if [0, 2].contains(&selected_choice) { println!("YOUR MNEMONIC:"); - println!("{}", mnemonic); + println!("{}", mnemonic.as_ref()); } if [1, 2].contains(&selected_choice) { write_mnemonic_to_file(DEFAULT_MNEMONIC_FILE_PATH, &mnemonic).await?; @@ -160,12 +162,14 @@ pub async fn generate_mnemonic() -> Result { Ok(mnemonic) } -pub fn enter_mnemonic() -> Result { +pub fn enter_mnemonic() -> Result { loop { - let input = Input::::new() - .with_prompt("Enter your mnemonic") - .interact_text()?; - if verify_mnemonic(&input).is_err() { + let input = Mnemonic::from( + Input::::new() + .with_prompt("Enter your mnemonic") + .interact_text()?, + ); + if verify_mnemonic(&*input).is_err() { println_log_error!("Invalid mnemonic. Please enter a bip-39 conform mnemonic."); } else { return Ok(input); @@ -173,7 +177,7 @@ pub fn enter_mnemonic() -> Result { } } -pub async fn import_mnemonic(path: &str) -> Result { +pub async fn import_mnemonic(path: &str) -> Result { let mut mnemonics = read_mnemonics_from_file(path).await?; if mnemonics.is_empty() { println_log_error!("No valid mnemonics found in '{path}'."); @@ -210,16 +214,17 @@ async fn write_mnemonic_to_file(path: &str, mnemonic: &str) -> Result<(), Error> Ok(()) } -async fn read_mnemonics_from_file(path: &str) -> Result, Error> { +async fn read_mnemonics_from_file(path: &str) -> Result, Error> { let file = OpenOptions::new().read(true).open(path).await?; let mut lines = BufReader::new(file).lines(); let mut mnemonics = Vec::new(); let mut line_index = 1; - while let Some(line) = lines.next_line().await? { + while let Some(mut line) = lines.next_line().await? { // we allow surrounding whitespace in the file - let trimmed = line.trim(); - if verify_mnemonic(trimmed).is_ok() { - mnemonics.push(trimmed.to_string()); + let trimmed = Mnemonic::from(line.trim().to_owned()); + line.zeroize(); + if verify_mnemonic(&*trimmed).is_ok() { + mnemonics.push(trimmed); } else { return Err(Error::Miscellaneous(format!( "Invalid mnemonic in file '{path}' at line '{line_index}'." diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md index 60e2d885ce..256863ee54 100644 --- a/sdk/CHANGELOG.md +++ b/sdk/CHANGELOG.md @@ -117,6 +117,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Rename `Account::prepare_mint_native_token` to `prepare_create_native_token`, `Account::prepare_increase_native_token_supply` to `prepare_mint_native_token`, `Account::prepare_decrease_native_token_supply` to `prepare_melt_native_token`; - Rename `MintNativeTokenParams` to `CreateNativeTokenParams`; - Rename `MintNativeTokenTransaction` to `CreateNativeTokenTransaction` and `PreparedMintNativeTokenTransaction` to `PreparedCreateNativeTokenTransaction` (including their corresponding DTOs); +- `Signature::Ed25519` now holds a boxed type; +- `Ed25519Signature::new` renamed to `try_from_bytes` and returns a Result; +- `Ed25519Signature::new`, `public_key`, `signature` now use concrete types; +- `Ed25519Signature::verify` is no longer fallable; +- `Mnemonic` type used over Strings where possible; +- `SecretManage::sign_ed25519`, `sign_secp256k1_ecdsa`, and `signature_unlock` now accept Bip44 type chains; - Rename `SendAmountParams` to `SendParams`; - Rename `Account::send` to `send_outputs`, `Account::send_amount` to `send`, `Account::prepare_send_amount` to `prepare_send`; - Made `ManagerStorage` public and renamed it to `StorageKind`; @@ -149,6 +155,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `client::Error::InvalidBIP32ChainData`; - `BlockResponse`, `OutputResponse` and `MilestoneResponse`; - `ClientError::UnexpectedApiResponse`; +- `HD_WALLET_TYPE` constant; ### Fixed diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index a561dd568e..d2b8dfdc7c 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -25,7 +25,7 @@ derive_more = { version = "0.99.17", default-features = false, features = [ "fro getset = { version = "0.1.2", default-features = false } hashbrown = { version = "0.13.2", default-features = false, features = [ "ahash", "inline-more" ] } hex = { version = "0.4.3", default-features = false } -iota-crypto = { version = "0.20.1", default-features = false, features = [ "blake2b", "ed25519", "secp256k1", "ternary_encoding" ] } +iota-crypto = { version = "0.22.1", default-features = false, features = [ "blake2b", "ed25519", "secp256k1", "ternary_encoding" ] } iterator-sorted = { version = "0.1.0", default-features = false } itertools = { version = "0.11.0", default-features = false, features = [ "use_alloc" ] } packable = { version = "0.8.1", default-features = false, features = [ "primitive-types" ] } @@ -43,8 +43,7 @@ futures = { version = "0.3.28", default-features = false, features = [ "thread-p heck = { version = "0.4.1", default-features = false, optional = true } instant = { version = "0.1.12", default-features = false, optional = true } iota-ledger-nano = { version = "1.0.0-alpha.4", default-features = false, optional = true } -# iota_stronghold = { version = "1.0.5", default-features = false, optional = true } -iota_stronghold = { git = "https://github.com/iotaledger/stronghold.rs", rev = "9df6d19de325481771d69b47534d151e03eae623", default-features = false, optional = true } +iota_stronghold = { version = "2.0.0-rc.1", default-features = false, optional = true } log = { version = "0.4.18", default-features = false, optional = true } num_cpus = { version = "1.15.0", default-features = false, optional = true } once_cell = { version = "1.17.2", default-features = false, optional = true } @@ -53,7 +52,6 @@ regex = { version = "1.8.3", default-features = false, features = [ "unicode-per reqwest = { version = "0.11.18", default-features = false, features = [ "json" ], optional = true } rocksdb = { version = "0.21.0", default-features = false, features = [ "lz4" ], optional = true } rumqttc = { version = "0.21.0", default-features = false, features = [ "websocket" ], optional = true } -serde-big-array = { version = "0.5.1", default-features = false, optional = true } serde_repr = { version = "0.1.12", default-features = false, optional = true } thiserror = { version = "1.0.40", default-features = false, optional = true } time = { version = "0.3.21", default-features = false, features = [ "serde", "macros" ], optional = true } @@ -88,13 +86,13 @@ participation = [ "storage" ] pow = [ "std", "num_cpus", "iota-crypto/curl-p" ] rand = [ "dep:rand" ] rocksdb = [ "dep:rocksdb", "storage" ] -serde = [ "serde_repr", "serde-big-array", "hashbrown/serde", "packable/serde", "primitive-types/serde_no_std", "zeroize?/serde" ] +serde = [ "serde_repr", "hashbrown/serde", "packable/serde", "primitive-types/serde_no_std", "zeroize?/serde" ] std = [ "packable/std", "prefix-hex/std", "primitive-types/std", "bech32/std", "bitflags/std", "rand?/std_rng", "regex?/std", "derive_builder?/std", "iota_stronghold?/std", "iota-crypto/std", "once_cell?/std", "itertools/use_std" ] storage = [ "iota-crypto/chacha", "dep:time", "dep:anymap", "dep:once_cell", "dep:heck" ] stronghold = [ "iota_stronghold", "derive_builder", "iota-crypto/chacha", "dep:time", "dep:anymap", "dep:once_cell", "dep:heck" ] tls = [ "reqwest?/rustls-tls", "rumqttc?/use-rustls" ] -client = [ "pow", "tokio", "zeroize", "url", "reqwest", "async-trait", "log", "thiserror", "futures", "serde", "instant", "iota-crypto/bip39", "iota-crypto/bip39-en", "iota-crypto/slip10" ] +client = [ "pow", "tokio", "zeroize", "url", "reqwest", "async-trait", "log", "thiserror", "futures", "serde", "instant", "iota-crypto/bip39", "iota-crypto/bip39-en", "iota-crypto/slip10", "iota-crypto/keccak", "iota-crypto/bip44", "iota-crypto/random" ] wallet = [ "client" ] # Ed25519 Examples diff --git a/sdk/examples/client/stronghold.rs b/sdk/examples/client/stronghold.rs index 6e7a7d0d21..a16b52ad35 100644 --- a/sdk/examples/client/stronghold.rs +++ b/sdk/examples/client/stronghold.rs @@ -8,11 +8,14 @@ //! cargo run --release --all-features --example client_stronghold //! ``` -use iota_sdk::client::{ - api::GetAddressesOptions, - constants::{SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP}, - secret::{stronghold::StrongholdSecretManager, SecretManager}, - Result, +use iota_sdk::{ + client::{ + api::GetAddressesOptions, + constants::{SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP}, + secret::{stronghold::StrongholdSecretManager, SecretManager}, + Result, + }, + crypto::keys::bip39::Mnemonic, }; #[tokio::main] @@ -24,7 +27,7 @@ async fn main() -> Result<()> { // This example uses secrets in environment variables for simplicity which should not be done in production. dotenvy::dotenv().ok(); - let mnemonic = std::env::var("MNEMONIC").unwrap(); + let mnemonic = Mnemonic::from(std::env::var("MNEMONIC").unwrap()); // The mnemonic only needs to be stored the first time stronghold_secret_manager.store_mnemonic(mnemonic).await?; diff --git a/sdk/examples/how_tos/accounts_and_addresses/create_account.rs b/sdk/examples/how_tos/accounts_and_addresses/create_account.rs index d8613b0a0a..af62241b55 100644 --- a/sdk/examples/how_tos/accounts_and_addresses/create_account.rs +++ b/sdk/examples/how_tos/accounts_and_addresses/create_account.rs @@ -15,6 +15,7 @@ use iota_sdk::{ constants::SHIMMER_COIN_TYPE, secret::{stronghold::StrongholdSecretManager, SecretManager}, }, + crypto::keys::bip39::Mnemonic, wallet::{ClientOptions, Result, Wallet}, }; @@ -29,7 +30,7 @@ async fn main() -> Result<()> { .build(std::env::var("STRONGHOLD_SNAPSHOT_PATH").unwrap())?; // Only required the first time, can also be generated with `manager.generate_mnemonic()?` - let mnemonic = std::env::var("MNEMONIC").unwrap(); + let mnemonic = Mnemonic::from(std::env::var("MNEMONIC").unwrap()); // The mnemonic only needs to be stored the first time secret_manager.store_mnemonic(mnemonic).await?; diff --git a/sdk/examples/how_tos/accounts_and_addresses/create_mnemonic.rs b/sdk/examples/how_tos/accounts_and_addresses/create_mnemonic.rs index 1ed9bffdb1..b74bf41f03 100644 --- a/sdk/examples/how_tos/accounts_and_addresses/create_mnemonic.rs +++ b/sdk/examples/how_tos/accounts_and_addresses/create_mnemonic.rs @@ -13,7 +13,7 @@ use iota_sdk::client::{Client, Result}; async fn main() -> Result<()> { let mnemonic = Client::generate_mnemonic()?; - println!("Generated mnemonic:\n{mnemonic}"); + println!("Generated mnemonic:\n{}", mnemonic.as_ref()); Ok(()) } diff --git a/sdk/examples/how_tos/sign_and_verify_ed25519/sign_ed25519.rs b/sdk/examples/how_tos/sign_and_verify_ed25519/sign_ed25519.rs index e069986441..09eb573fef 100644 --- a/sdk/examples/how_tos/sign_and_verify_ed25519/sign_ed25519.rs +++ b/sdk/examples/how_tos/sign_and_verify_ed25519/sign_ed25519.rs @@ -8,13 +8,14 @@ //! cargo run --release --all-features --example sign_ed25519 //! ``` +use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ - constants::{HD_WALLET_TYPE, SHIMMER_COIN_TYPE}, + constants::SHIMMER_COIN_TYPE, hex_public_key_to_bech32_address, secret::{stronghold::StrongholdSecretManager, SecretManage, SecretManager}, }, - crypto::keys::slip10::Chain, + crypto::keys::bip39::Mnemonic, wallet::Result, }; @@ -33,28 +34,28 @@ async fn main() -> Result<()> { .password(std::env::var("STRONGHOLD_PASSWORD").unwrap()) .build("sign_ed25519.stronghold")?; - stronghold.store_mnemonic(std::env::var("MNEMONIC").unwrap()).await?; + stronghold + .store_mnemonic(Mnemonic::from(std::env::var("MNEMONIC").unwrap())) + .await?; - let bip32_chain = Chain::from_u32_hardened([ - HD_WALLET_TYPE, - SHIMMER_COIN_TYPE, - ACCOUNT_INDEX, - INTERNAL_ADDRESS as u32, - ADDRESS_INDEX, - ]); + let bip44_chain = Bip44::new() + .with_coin_type(SHIMMER_COIN_TYPE) + .with_account(ACCOUNT_INDEX) + .with_change(INTERNAL_ADDRESS as _) + .with_address_index(ADDRESS_INDEX); let message = FOUNDRY_METADATA.as_bytes(); let signature = SecretManager::Stronghold(stronghold) - .sign_ed25519(message, &bip32_chain) + .sign_ed25519(message, bip44_chain) .await?; println!( "Public key: {}\nSignature: {}", - prefix_hex::encode(signature.public_key()), - prefix_hex::encode(signature.signature()), + prefix_hex::encode(signature.public_key().as_ref()), + prefix_hex::encode(signature.signature().to_bytes()), ); // Hash the public key to get the address - let bech32_address = hex_public_key_to_bech32_address(&prefix_hex::encode(signature.public_key()), "rms")?; + let bech32_address = hex_public_key_to_bech32_address(&prefix_hex::encode(signature.public_key().as_ref()), "rms")?; println!("Address: {bech32_address}"); Ok(()) diff --git a/sdk/examples/wallet/getting_started.rs b/sdk/examples/wallet/getting_started.rs index 6a214cad1c..abecbe039a 100644 --- a/sdk/examples/wallet/getting_started.rs +++ b/sdk/examples/wallet/getting_started.rs @@ -44,7 +44,7 @@ async fn main() -> Result<()> { // INFO: It is best practice to back up the mnemonic somewhere secure let mnemonic = wallet.generate_mnemonic()?; wallet.store_mnemonic(mnemonic.clone()).await?; - println!("Created a wallet from the mnemonic:\n'{mnemonic}'"); + println!("Created a wallet from the mnemonic:\n'{}'", mnemonic.as_ref()); // Create an account let alias = "Alice"; diff --git a/sdk/examples/wallet/offline_signing/0_generate_addresses.rs b/sdk/examples/wallet/offline_signing/0_generate_addresses.rs index da66fae60b..138bc23fb2 100644 --- a/sdk/examples/wallet/offline_signing/0_generate_addresses.rs +++ b/sdk/examples/wallet/offline_signing/0_generate_addresses.rs @@ -13,6 +13,7 @@ use iota_sdk::{ constants::{SHIMMER_BECH32_HRP, SHIMMER_COIN_TYPE}, secret::{stronghold::StrongholdSecretManager, SecretManager}, }, + crypto::keys::bip39::Mnemonic, wallet::{Account, ClientOptions, Result, Wallet}, }; @@ -32,7 +33,7 @@ async fn main() -> Result<()> { .password(std::env::var("STRONGHOLD_PASSWORD").unwrap()) .build(STRONGHOLD_SNAPSHOT_PATH)?; - let mnemonic = std::env::var("MNEMONIC").unwrap(); + let mnemonic = Mnemonic::from(std::env::var("MNEMONIC").unwrap()); // The mnemonic only needs to be stored the first time secret_manager.store_mnemonic(mnemonic).await?; diff --git a/sdk/src/client/api/block_builder/input_selection/automatic.rs b/sdk/src/client/api/block_builder/input_selection/automatic.rs index 4fc5cd6ac4..e31675f3be 100644 --- a/sdk/src/client/api/block_builder/input_selection/automatic.rs +++ b/sdk/src/client/api/block_builder/input_selection/automatic.rs @@ -5,7 +5,7 @@ use std::collections::HashSet; -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use itertools::Itertools; use crate::{ @@ -15,7 +15,6 @@ use crate::{ input_selection::is_alias_transition, ClientBlockBuilder, GetAddressesOptions, ADDRESS_GAP_RANGE, }, - constants::HD_WALLET_TYPE, node_api::indexer::query_parameters::QueryParameter, secret::types::InputSigningData, Error, Result, @@ -176,13 +175,13 @@ impl<'a> ClientBlockBuilder<'a> { available_inputs.push(InputSigningData { output: output_with_meta.output, output_metadata: output_with_meta.metadata, - chain: Some(Chain::from_u32_hardened([ - HD_WALLET_TYPE, - self.coin_type, - account_index, - internal as u32, - address_index, - ])), + chain: Some( + Bip44::new() + .with_coin_type(self.coin_type) + .with_account(account_index) + .with_change(internal as _) + .with_address_index(address_index), + ), }); } } diff --git a/sdk/src/client/api/block_builder/input_selection/core/remainder.rs b/sdk/src/client/api/block_builder/input_selection/core/remainder.rs index fcc040d4c9..b4abf79145 100644 --- a/sdk/src/client/api/block_builder/input_selection/core/remainder.rs +++ b/sdk/src/client/api/block_builder/input_selection/core/remainder.rs @@ -1,7 +1,7 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use super::{ requirement::{ @@ -21,7 +21,7 @@ use crate::{ impl InputSelection { // Gets the remainder address from configuration of finds one from the inputs. - fn get_remainder_address(&self) -> Option<(Address, Option)> { + fn get_remainder_address(&self) -> Option<(Address, Option)> { if self.remainder_address.is_some() { return self.remainder_address.map(|address| (address, None)); } @@ -41,7 +41,7 @@ impl InputSelection { .0; if required_address.is_ed25519() { - return Some((required_address, input.chain.clone())); + return Some((required_address, input.chain)); } } diff --git a/sdk/src/client/api/block_builder/input_selection/manual.rs b/sdk/src/client/api/block_builder/input_selection/manual.rs index 87525f875c..5f40b40f0f 100644 --- a/sdk/src/client/api/block_builder/input_selection/manual.rs +++ b/sdk/src/client/api/block_builder/input_selection/manual.rs @@ -5,7 +5,7 @@ use std::collections::HashSet; -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use crate::{ client::{ @@ -15,7 +15,6 @@ use crate::{ input_selection::is_alias_transition, ClientBlockBuilder, }, - constants::HD_WALLET_TYPE, secret::types::InputSigningData, Result, }, @@ -81,13 +80,11 @@ impl<'a> ClientBlockBuilder<'a> { output: output_with_meta.output, output_metadata: output_with_meta.metadata, chain: address_index_internal.map(|(address_index, internal)| { - Chain::from_u32_hardened([ - HD_WALLET_TYPE, - self.coin_type, - self.account_index, - internal as u32, - address_index, - ]) + Bip44::new() + .with_coin_type(self.coin_type) + .with_account(self.account_index) + .with_change(internal as _) + .with_address_index(address_index) }), }); } diff --git a/sdk/src/client/api/block_builder/input_selection/sender_issuer.rs b/sdk/src/client/api/block_builder/input_selection/sender_issuer.rs index b81b8a835b..e76234d984 100644 --- a/sdk/src/client/api/block_builder/input_selection/sender_issuer.rs +++ b/sdk/src/client/api/block_builder/input_selection/sender_issuer.rs @@ -5,7 +5,7 @@ use std::collections::HashSet; -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use crate::{ client::{ @@ -16,7 +16,6 @@ use crate::{ }, ClientBlockBuilder, }, - constants::HD_WALLET_TYPE, secret::types::InputSigningData, Error, Result, }, @@ -71,13 +70,13 @@ impl<'a> ClientBlockBuilder<'a> { required_inputs.push(InputSigningData { output: output_with_meta.output().to_owned(), output_metadata: output_with_meta.metadata().to_owned(), - chain: Some(Chain::from_u32_hardened([ - HD_WALLET_TYPE, - self.coin_type, - self.account_index, - internal as u32, - address_index, - ])), + chain: Some( + Bip44::new() + .with_coin_type(self.coin_type) + .with_account(self.account_index) + .with_change(internal as _) + .with_address_index(address_index), + ), }); found_output = true; break; @@ -131,13 +130,11 @@ impl<'a> ClientBlockBuilder<'a> { output: output_with_meta.output().to_owned(), output_metadata: output_with_meta.metadata().to_owned(), chain: address_index_internal.map(|(address_index, internal)| { - Chain::from_u32_hardened([ - HD_WALLET_TYPE, - self.coin_type, - self.account_index, - internal as u32, - address_index, - ]) + Bip44::new() + .with_coin_type(self.coin_type) + .with_account(self.account_index) + .with_change(internal as _) + .with_address_index(address_index) }), }); } @@ -188,13 +185,11 @@ impl<'a> ClientBlockBuilder<'a> { output: output_with_meta.output, output_metadata: output_with_meta.metadata, chain: address_index_internal.map(|(address_index, internal)| { - Chain::from_u32_hardened([ - HD_WALLET_TYPE, - self.coin_type, - self.account_index, - internal as u32, - address_index, - ]) + Bip44::new() + .with_coin_type(self.coin_type) + .with_account(self.account_index) + .with_change(internal as _) + .with_address_index(address_index) }), }); } diff --git a/sdk/src/client/api/block_builder/input_selection/utxo_chains.rs b/sdk/src/client/api/block_builder/input_selection/utxo_chains.rs index 3c28a1abbb..94c99f70d2 100644 --- a/sdk/src/client/api/block_builder/input_selection/utxo_chains.rs +++ b/sdk/src/client/api/block_builder/input_selection/utxo_chains.rs @@ -3,12 +3,11 @@ //! input selection for utxo chains -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use crate::{ client::{ api::{block_builder::ClientBlockBuilder, search_address}, - constants::HD_WALLET_TYPE, secret::types::InputSigningData, Client, Result, }, @@ -190,13 +189,11 @@ impl<'a> ClientBlockBuilder<'a> { output: output_with_meta.output, output_metadata: output_with_meta.metadata, chain: address_index_internal.map(|(address_index, internal)| { - Chain::from_u32_hardened([ - HD_WALLET_TYPE, - self.coin_type, - self.account_index, - internal as u32, - address_index, - ]) + Bip44::new() + .with_coin_type(self.coin_type) + .with_account(self.account_index) + .with_change(internal as _) + .with_address_index(address_index) }), }); } diff --git a/sdk/src/client/api/types.rs b/sdk/src/client/api/types.rs index cd94d907d5..ba9c9351e3 100644 --- a/sdk/src/client/api/types.rs +++ b/sdk/src/client/api/types.rs @@ -1,7 +1,7 @@ // Copyright 2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use serde::{Deserialize, Serialize}; use crate::{ @@ -19,11 +19,11 @@ use crate::{ protocol::ProtocolParameters, Error, }, + utils::serde::bip44::option_bip44, }; /// Helper struct for offline signing -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct PreparedTransactionData { /// Transaction essence pub essence: TransactionEssence, @@ -164,12 +164,12 @@ impl SignedTransactionData { } /// Data for a remainder output, used for ledger nano -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct RemainderData { /// The remainder output pub output: Output, /// The chain derived from seed, for the remainder addresses - pub chain: Option, + pub chain: Option, /// The remainder address pub address: Address, } @@ -180,7 +180,8 @@ pub struct RemainderDataDto { /// The remainder output pub output: OutputDto, /// The chain derived from seed, for the remainder addresses - pub chain: Option, + #[serde(with = "option_bip44")] + pub chain: Option, /// The remainder address pub address: AddressDto, } @@ -189,7 +190,7 @@ impl RemainderData { pub(crate) fn try_from_dto(remainder: RemainderDataDto, token_supply: u64) -> crate::client::Result { Ok(Self { output: Output::try_from_dto(remainder.output, token_supply)?, - chain: remainder.chain.clone(), + chain: remainder.chain, address: Address::try_from(remainder.address)?, }) } @@ -197,7 +198,7 @@ impl RemainderData { pub(crate) fn try_from_dto_unverified(remainder: RemainderDataDto) -> crate::client::Result { Ok(Self { output: Output::try_from_dto_unverified(remainder.output)?, - chain: remainder.chain.clone(), + chain: remainder.chain, address: Address::try_from(remainder.address)?, }) } @@ -207,7 +208,7 @@ impl From<&RemainderData> for RemainderDataDto { fn from(remainder: &RemainderData) -> Self { Self { output: OutputDto::from(&remainder.output), - chain: remainder.chain.clone(), + chain: remainder.chain, address: AddressDto::from(&remainder.address), } } diff --git a/sdk/src/client/constants.rs b/sdk/src/client/constants.rs index 39153ff185..6b1ceffdf6 100644 --- a/sdk/src/client/constants.rs +++ b/sdk/src/client/constants.rs @@ -37,8 +37,6 @@ pub const SHIMMER_BECH32_HRP: Hrp = Hrp::from_str_unchecked("smr"); /// Bech32 hrp for the Shimmer testnet pub const SHIMMER_TESTNET_BECH32_HRP: Hrp = Hrp::from_str_unchecked("rms"); -/// BIP-0044 defines a logical hierarchy for deterministic wallets -pub const HD_WALLET_TYPE: u32 = 44; /// IOTA coin type pub const IOTA_COIN_TYPE: u32 = 4218; /// Shimmer coin type diff --git a/sdk/src/client/secret/ledger_nano.rs b/sdk/src/client/secret/ledger_nano.rs index 24b01051f2..546c0a4103 100644 --- a/sdk/src/client/secret/ledger_nano.rs +++ b/sdk/src/client/secret/ledger_nano.rs @@ -9,7 +9,7 @@ use std::{collections::HashMap, ops::Range}; use async_trait::async_trait; use crypto::{ - keys::slip10::{Chain, Segment}, + keys::{bip44::Bip44, slip10::Segment}, signatures::secp256k1_ecdsa::{self, EvmAddress}, }; use iota_ledger_nano::{ @@ -141,11 +141,11 @@ impl SecretManage for LedgerSecretManager { options: impl Into> + Send, ) -> Result, Self::Error> { let options = options.into().unwrap_or_default(); - let bip32_account = account_index | Segment::HARDEN_MASK; + let bip32_account = account_index.harden().into(); let bip32 = LedgerBIP32Index { - bip32_index: address_indexes.start | Segment::HARDEN_MASK, - bip32_change: u32::from(options.internal) | Segment::HARDEN_MASK, + bip32_index: address_indexes.start.harden().into(), + bip32_change: u32::from(options.internal).harden().into(), }; // lock the mutex to prevent multiple simultaneous requests to a ledger @@ -177,25 +177,18 @@ impl SecretManage for LedgerSecretManager { } /// Ledger only allows signing messages of 32 bytes, anything else is unsupported and will result in an error. - async fn sign_ed25519(&self, msg: &[u8], chain: &Chain) -> Result { + async fn sign_ed25519(&self, msg: &[u8], chain: Bip44) -> Result { if msg.len() != 32 { return Err(Error::UnsupportedOperation.into()); } let msg = msg.to_vec(); - let bip32_chain = chain - .segments() - .iter() - // XXX: "ser32(i)". RTFSC: [crypto::keys::slip10::Segment::from_u32()] - .map(|seg| u32::from_be_bytes(seg.bs())) - .collect::>(); - - let coin_type = bip32_chain[1] & !Segment::HARDEN_MASK; - let account_index = bip32_chain[2] | Segment::HARDEN_MASK; + let coin_type = chain.coin_type; + let account_index = chain.account.harden().into(); let bip32_index = LedgerBIP32Index { - bip32_change: bip32_chain[3] | Segment::HARDEN_MASK, - bip32_index: bip32_chain[4] | Segment::HARDEN_MASK, + bip32_change: chain.change.harden().into(), + bip32_index: chain.address_index.harden().into(), }; // Lock the mutex to prevent multiple simultaneous requests to a ledger. @@ -226,7 +219,7 @@ impl SecretManage for LedgerSecretManager { // Unpack and return signature. return match Unlock::unpack::<_, true>(&mut unpacker, &())? { - Unlock::Signature(SignatureUnlock(Signature::Ed25519(signature))) => Ok(signature), + Unlock::Signature(SignatureUnlock(Signature::Ed25519(signature))) => Ok(*signature), _ => Err(Error::UnsupportedOperation.into()), }; } @@ -234,8 +227,8 @@ impl SecretManage for LedgerSecretManager { async fn sign_secp256k1_ecdsa( &self, _msg: &[u8], - _chain: &Chain, - ) -> Result<(secp256k1_ecdsa::PublicKey, secp256k1_ecdsa::Signature), Self::Error> { + _chain: Bip44, + ) -> Result<(secp256k1_ecdsa::PublicKey, secp256k1_ecdsa::RecoverableSignature), Self::Error> { Err(Error::UnsupportedOperation.into()) } @@ -251,39 +244,26 @@ impl SecretManage for LedgerSecretManager { let input_len = prepared_transaction.inputs_data.len(); for input in &prepared_transaction.inputs_data { - let bip32_indices = match &input.chain { - Some(chain) => { - chain - .segments() - .iter() - // XXX: "ser32(i)". RTFSC: [crypto::keys::slip10::Segment::from_u32()] - .map(|seg| u32::from_be_bytes(seg.bs())) - .collect::>() - } - None => return Err(Error::MissingBip32Chain)?, - }; + let chain = input.chain.ok_or(Error::MissingBip32Chain)?; // coin_type and account_index should be the same in each output - if (coin_type.is_some() && coin_type != Some(bip32_indices[1])) - || (account_index.is_some() && account_index != Some(bip32_indices[2])) + if (coin_type.is_some() && coin_type != Some(chain.coin_type)) + || (account_index.is_some() && account_index != Some(chain.account)) { return Err(Error::Bip32ChainMismatch.into()); } - coin_type = Some(bip32_indices[1]); - account_index = Some(bip32_indices[2]); + coin_type = Some(chain.coin_type); + account_index = Some(chain.account); input_bip32_indices.push(LedgerBIP32Index { - bip32_change: bip32_indices[3] | Segment::HARDEN_MASK, - bip32_index: bip32_indices[4] | Segment::HARDEN_MASK, + bip32_change: chain.change.harden().into(), + bip32_index: chain.address_index.harden().into(), }); } - if coin_type.is_none() || account_index.is_none() { - return Err(Error::NoAvailableInputsProvided)?; - } + let (coin_type, account_index) = coin_type.zip(account_index).ok_or(Error::NoAvailableInputsProvided)?; - let coin_type = coin_type.unwrap() & !Segment::HARDEN_MASK; - let bip32_account = account_index.unwrap() | Segment::HARDEN_MASK; + let bip32_account = account_index.harden().into(); // pack essence and hash into vec let essence_bytes = prepared_transaction.essence.pack_to_vec(); @@ -312,22 +292,12 @@ impl SecretManage for LedgerSecretManager { let (remainder_address, remainder_bip32): (Option<&Address>, LedgerBIP32Index) = match &prepared_transaction.remainder { Some(a) => { - let remainder_bip32_indices = match &a.chain { - Some(chain) => { - chain - .segments() - .iter() - // XXX: "ser32(i)". RTFSC: [crypto::keys::slip10::Segment::from_u32()] - .map(|seg| u32::from_be_bytes(seg.bs())) - .collect::>() - } - None => return Err(Error::MissingBip32Chain.into()), - }; + let chain = a.chain.ok_or(Error::MissingBip32Chain)?; ( Some(&a.address), LedgerBIP32Index { - bip32_change: remainder_bip32_indices[3] | Segment::HARDEN_MASK, - bip32_index: remainder_bip32_indices[4] | Segment::HARDEN_MASK, + bip32_change: chain.change.harden().into(), + bip32_index: chain.address_index.harden().into(), }, ) } diff --git a/sdk/src/client/secret/mnemonic.rs b/sdk/src/client/secret/mnemonic.rs index 5d47618d81..09aab95466 100644 --- a/sdk/src/client/secret/mnemonic.rs +++ b/sdk/src/client/secret/mnemonic.rs @@ -8,7 +8,7 @@ use std::ops::Range; use async_trait::async_trait; use crypto::{ hashes::{blake2b::Blake2b256, Digest}, - keys::slip10::{Chain, Seed}, + keys::{bip39::Mnemonic, bip44::Bip44, slip10::Seed}, signatures::{ ed25519, secp256k1_ecdsa::{self, EvmAddress}, @@ -18,7 +18,7 @@ use zeroize::Zeroizing; use super::{GenerateAddressOptions, SecretManage}; use crate::{ - client::{api::PreparedTransactionData, constants::HD_WALLET_TYPE, Client, Error}, + client::{api::PreparedTransactionData, Client, Error}, types::block::{address::Ed25519Address, payload::Payload, signature::Ed25519Signature, unlock::Unlocks}, }; @@ -48,17 +48,14 @@ impl SecretManage for MnemonicSecretManager { Ok(address_indexes .map(|address_index| { - let chain = Chain::from_u32_hardened([ - HD_WALLET_TYPE, - coin_type, - account_index, - internal as u32, - address_index, - ]); - - let public_key = self - .0 - .derive::(&chain)? + let chain = Bip44::new() + .with_coin_type(coin_type) + .with_account(account_index) + .with_change(internal as _) + .with_address_index(address_index); + + let public_key = chain + .derive(&self.0.to_master_key::()) .secret_key() .public_key() .to_bytes(); @@ -84,25 +81,27 @@ impl SecretManage for MnemonicSecretManager { Ok(address_indexes .map(|address_index| { - let chain = Chain::from_u32_hardened([HD_WALLET_TYPE, coin_type, account_index]) - .join(Chain::from_u32([internal as u32, address_index])); - - let public_key = self - .0 - .derive::(&chain)? + let chain = Bip44::new() + .with_coin_type(coin_type) + .with_account(account_index) + .with_change(internal as _) + .with_address_index(address_index); + + let public_key = chain + .derive(&self.0.to_master_key::()) .secret_key() .public_key(); - crate::client::Result::Ok(public_key.to_evm_address()) + crate::client::Result::Ok(public_key.evm_address()) }) .collect::>()?) } - async fn sign_ed25519(&self, msg: &[u8], chain: &Chain) -> Result { + async fn sign_ed25519(&self, msg: &[u8], chain: Bip44) -> Result { // Get the private and public key for this Ed25519 address - let private_key = self.0.derive::(chain)?.secret_key(); - let public_key = private_key.public_key().to_bytes(); - let signature = private_key.sign(msg).to_bytes(); + let private_key = chain.derive(&self.0.to_master_key::()).secret_key(); + let public_key = private_key.public_key(); + let signature = private_key.sign(msg); Ok(Ed25519Signature::new(public_key, signature)) } @@ -110,12 +109,14 @@ impl SecretManage for MnemonicSecretManager { async fn sign_secp256k1_ecdsa( &self, msg: &[u8], - chain: &Chain, - ) -> Result<(secp256k1_ecdsa::PublicKey, secp256k1_ecdsa::Signature), Self::Error> { + chain: Bip44, + ) -> Result<(secp256k1_ecdsa::PublicKey, secp256k1_ecdsa::RecoverableSignature), Self::Error> { // Get the private and public key for this secp256k1_ecdsa key - let private_key = self.0.derive::(chain)?.secret_key(); + let private_key = chain + .derive(&self.0.to_master_key::()) + .secret_key(); let public_key = private_key.public_key(); - let signature = private_key.sign(msg); + let signature = private_key.try_sign_keccak256(msg)?; Ok((public_key, signature)) } @@ -140,8 +141,8 @@ impl MnemonicSecretManager { /// Create a new [`MnemonicSecretManager`] from a BIP-39 mnemonic in English. /// /// For more information, see . - pub fn try_from_mnemonic(mnemonic: impl Into>) -> Result { - Ok(Self(Client::mnemonic_to_seed(mnemonic.into())?)) + pub fn try_from_mnemonic(mnemonic: impl Into) -> Result { + Ok(Self(Client::mnemonic_to_seed(mnemonic.into())?.into())) } /// Create a new [`MnemonicSecretManager`] from a hex-encoded raw seed string. diff --git a/sdk/src/client/secret/mod.rs b/sdk/src/client/secret/mod.rs index b22d14fe28..81330f3596 100644 --- a/sdk/src/client/secret/mod.rs +++ b/sdk/src/client/secret/mod.rs @@ -21,7 +21,7 @@ use std::{collections::HashMap, fmt::Debug, ops::Range, str::FromStr}; use async_trait::async_trait; use crypto::{ - keys::slip10::Chain, + keys::{bip39::Mnemonic, bip44::Bip44}, signatures::secp256k1_ecdsa::{self, EvmAddress}, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -80,18 +80,18 @@ pub trait SecretManage: Send + Sync { ) -> Result, Self::Error>; /// Signs msg using the given [`Chain`] using Ed25519. - async fn sign_ed25519(&self, msg: &[u8], chain: &Chain) -> Result; + async fn sign_ed25519(&self, msg: &[u8], chain: Bip44) -> Result; /// Signs msg using the given [`Chain`] using Secp256k1. async fn sign_secp256k1_ecdsa( &self, msg: &[u8], - chain: &Chain, - ) -> Result<(secp256k1_ecdsa::PublicKey, secp256k1_ecdsa::Signature), Self::Error>; + chain: Bip44, + ) -> Result<(secp256k1_ecdsa::PublicKey, secp256k1_ecdsa::RecoverableSignature), Self::Error>; /// Signs `essence_hash` using the given `chain`, returning an [`Unlock`]. - async fn signature_unlock(&self, essence_hash: &[u8; 32], chain: &Chain) -> Result { - Ok(Unlock::Signature(SignatureUnlock::new(Signature::Ed25519( + async fn signature_unlock(&self, essence_hash: &[u8; 32], chain: Bip44) -> Result { + Ok(Unlock::Signature(SignatureUnlock::new(Signature::from( self.sign_ed25519(essence_hash, chain).await?, )))) } @@ -210,7 +210,9 @@ impl TryFrom for SecretManager { #[cfg(feature = "ledger_nano")] SecretManagerDto::LedgerNano(is_simulator) => Self::LedgerNano(LedgerSecretManager::new(is_simulator)), - SecretManagerDto::Mnemonic(mnemonic) => Self::Mnemonic(MnemonicSecretManager::try_from_mnemonic(mnemonic)?), + SecretManagerDto::Mnemonic(mnemonic) => { + Self::Mnemonic(MnemonicSecretManager::try_from_mnemonic(mnemonic.as_str().to_owned())?) + } SecretManagerDto::HexSeed(hex_seed) => { // `SecretManagerDto` is `ZeroizeOnDrop` so it will take care of zeroizing the original. @@ -303,7 +305,7 @@ impl SecretManage for SecretManager { } } - async fn sign_ed25519(&self, msg: &[u8], chain: &Chain) -> crate::client::Result { + async fn sign_ed25519(&self, msg: &[u8], chain: Bip44) -> crate::client::Result { match self { #[cfg(feature = "stronghold")] Self::Stronghold(secret_manager) => Ok(secret_manager.sign_ed25519(msg, chain).await?), @@ -317,8 +319,8 @@ impl SecretManage for SecretManager { async fn sign_secp256k1_ecdsa( &self, msg: &[u8], - chain: &Chain, - ) -> Result<(secp256k1_ecdsa::PublicKey, secp256k1_ecdsa::Signature), Self::Error> { + chain: Bip44, + ) -> Result<(secp256k1_ecdsa::PublicKey, secp256k1_ecdsa::RecoverableSignature), Self::Error> { match self { #[cfg(feature = "stronghold")] Self::Stronghold(secret_manager) => Ok(secret_manager.sign_secp256k1_ecdsa(msg, chain).await?), @@ -401,7 +403,7 @@ impl SecretManagerConfig for SecretManager { Self::Mnemonic(MnemonicSecretManager::try_from_hex_seed(hex_seed.clone())?) } SecretManagerDto::Mnemonic(mnemonic) => { - Self::Mnemonic(MnemonicSecretManager::try_from_mnemonic(mnemonic.clone())?) + Self::Mnemonic(MnemonicSecretManager::try_from_mnemonic(mnemonic.as_str().to_owned())?) } SecretManagerDto::Placeholder => Self::Placeholder, }) @@ -410,7 +412,7 @@ impl SecretManagerConfig for SecretManager { impl SecretManager { /// Tries to create a [`SecretManager`] from a mnemonic string. - pub fn try_from_mnemonic(mnemonic: impl Into>) -> crate::client::Result { + pub fn try_from_mnemonic(mnemonic: impl Into) -> crate::client::Result { Ok(Self::Mnemonic(MnemonicSecretManager::try_from_mnemonic(mnemonic)?)) } @@ -462,7 +464,7 @@ where Err(InputSelectionError::MissingInputWithEd25519Address)?; } - let chain = input.chain.as_ref().ok_or(Error::MissingBip32Chain)?; + let chain = input.chain.ok_or(Error::MissingBip32Chain)?; let block = secret_manager.signature_unlock(&hashed_essence, chain).await?; blocks.push(block); diff --git a/sdk/src/client/secret/types.rs b/sdk/src/client/secret/types.rs index dd47a12e9b..912619ecef 100644 --- a/sdk/src/client/secret/types.rs +++ b/sdk/src/client/secret/types.rs @@ -3,7 +3,7 @@ //! Miscellaneous types for secret managers. -use crypto::keys::slip10::{Chain, Segment}; +use crypto::keys::bip44::Bip44; use serde::{Deserialize, Serialize}; use crate::{ @@ -15,6 +15,7 @@ use crate::{ Output, OutputId, OutputMetadata, }, }, + utils::serde::bip44::option_bip44, }; /// Stronghold DTO to allow the creation of a Stronghold secret manager from bindings @@ -159,7 +160,7 @@ pub struct InputSigningData { /// The output metadata pub output_metadata: OutputMetadata, /// The chain derived from seed, only for ed25519 addresses - pub chain: Option, + pub chain: Option, } impl InputSigningData { @@ -178,7 +179,8 @@ pub struct InputSigningDataDto { /// The output metadata pub output_metadata: OutputMetadataDto, /// The chain derived from seed, only for ed25519 addresses - pub chain: Option>, + #[serde(with = "option_bip44")] + pub chain: Option, } #[allow(missing_docs)] @@ -187,7 +189,7 @@ impl InputSigningData { Ok(Self { output: Output::try_from_dto(input.output, token_supply)?, output_metadata: OutputMetadata::try_from(input.output_metadata)?, - chain: input.chain.map(Chain::from_u32_hardened), + chain: input.chain, }) } @@ -195,7 +197,7 @@ impl InputSigningData { Ok(Self { output: Output::try_from_dto_unverified(input.output)?, output_metadata: OutputMetadata::try_from(input.output_metadata)?, - chain: input.chain.map(Chain::from_u32_hardened), + chain: input.chain, }) } } @@ -205,14 +207,7 @@ impl From<&InputSigningData> for InputSigningDataDto { Self { output: OutputDto::from(&input.output), output_metadata: OutputMetadataDto::from(&input.output_metadata), - chain: input.chain.as_ref().map(|chain| { - chain - .segments() - .iter() - // TODO: get the value direct when https://github.com/iotaledger/crypto.rs/issues/192 is done - .map(|seg| u32::from_be_bytes(seg.bs()) & !Segment::HARDEN_MASK) - .collect::>() - }), + chain: input.chain, } } } diff --git a/sdk/src/client/stronghold/secret.rs b/sdk/src/client/stronghold/secret.rs index b649ccf88b..161f84a25f 100644 --- a/sdk/src/client/stronghold/secret.rs +++ b/sdk/src/client/stronghold/secret.rs @@ -3,19 +3,27 @@ //! The [SecretManage] implementation for [StrongholdAdapter]. +use core::borrow::Borrow; use std::ops::Range; use async_trait::async_trait; use crypto::{ hashes::{blake2b::Blake2b256, Digest}, - signatures::secp256k1_ecdsa::{self, EvmAddress}, + keys::{ + bip39::{Mnemonic, MnemonicRef}, + bip44::Bip44, + slip10::Segment, + }, + signatures::{ + ed25519, + secp256k1_ecdsa::{self, EvmAddress}, + }, }; use instant::Duration; use iota_stronghold::{ - procedures::{self, Chain, Curve, KeyType, Slip10DeriveInput}, + procedures::{self, Curve, KeyType, Slip10DeriveInput}, Location, }; -use zeroize::Zeroize; use super::{ common::{DERIVE_OUTPUT_RECORD_PATH, PRIVATE_DATA_CLIENT_PATH, SECRET_VAULT_PATH, SEED_RECORD_PATH}, @@ -24,7 +32,6 @@ use super::{ use crate::{ client::{ api::PreparedTransactionData, - constants::HD_WALLET_TYPE, secret::{types::StrongholdDto, GenerateAddressOptions, SecretManage, SecretManagerConfig}, stronghold::Error, }, @@ -59,14 +66,21 @@ impl SecretManage for StrongholdAdapter { let internal = options.into().map(|o| o.internal).unwrap_or_default(); for address_index in address_indexes { - let bip_path = [HD_WALLET_TYPE, coin_type, account_index, internal as u32, address_index]; - let chain = Chain::from_u32_hardened(bip_path); + let chain = Bip44::new() + .with_coin_type(coin_type) + .with_account(account_index) + .with_change(internal as _) + .with_address_index(address_index); let derive_location = Location::generic( SECRET_VAULT_PATH, [ DERIVE_OUTPUT_RECORD_PATH, - &chain.segments().iter().flat_map(|seg| seg.bs()).collect::>(), + &chain + .to_chain::() + .into_iter() + .flat_map(|seg| seg.ser32()) + .collect::>(), ] .concat(), ); @@ -125,14 +139,21 @@ impl SecretManage for StrongholdAdapter { let internal = options.into().map(|o| o.internal).unwrap_or_default(); for address_index in address_indexes { - let chain = Chain::from_u32_hardened([HD_WALLET_TYPE, coin_type, account_index]) - .join(Chain::from_u32([internal as u32, address_index])); + let chain = Bip44::new() + .with_coin_type(coin_type) + .with_account(account_index) + .with_change(internal as _) + .with_address_index(address_index); let derive_location = Location::generic( SECRET_VAULT_PATH, [ DERIVE_OUTPUT_RECORD_PATH, - &chain.segments().iter().flat_map(|seg| seg.bs()).collect::>(), + &chain + .to_chain::() + .into_iter() + .flat_map(|seg| seg.ser32()) + .collect::>(), ] .concat(), ); @@ -155,13 +176,13 @@ impl SecretManage for StrongholdAdapter { .map_err(Error::from)?; // Collect it. - addresses.push(public_key.to_evm_address()); + addresses.push(public_key.evm_address()); } Ok(addresses) } - async fn sign_ed25519(&self, msg: &[u8], chain: &Chain) -> Result { + async fn sign_ed25519(&self, msg: &[u8], chain: Bip44) -> Result { // Prevent the method from being invoked when the key has been cleared from the memory. Do note that Stronghold // only asks for a key for reading / writing a snapshot, so without our cached key this method is invocable, but // it doesn't make sense when it comes to our user (signing transactions / generating addresses without a key). @@ -178,13 +199,17 @@ impl SecretManage for StrongholdAdapter { SECRET_VAULT_PATH, [ DERIVE_OUTPUT_RECORD_PATH, - &chain.segments().iter().flat_map(|seg| seg.bs()).collect::>(), + &chain + .to_chain::() + .into_iter() + .flat_map(|seg| seg.ser32()) + .collect::>(), ] .concat(), ); // Derive a SLIP-10 private key in the vault. - self.slip10_derive(Curve::Ed25519, chain.clone(), seed_location, derive_location.clone()) + self.slip10_derive(Curve::Ed25519, chain, seed_location, derive_location.clone()) .await?; // Get the Ed25519 public key from the derived SLIP-10 private key in the vault. @@ -207,8 +232,8 @@ impl SecretManage for StrongholdAdapter { async fn sign_secp256k1_ecdsa( &self, msg: &[u8], - chain: &Chain, - ) -> Result<(secp256k1_ecdsa::PublicKey, secp256k1_ecdsa::Signature), Self::Error> { + chain: Bip44, + ) -> Result<(secp256k1_ecdsa::PublicKey, secp256k1_ecdsa::RecoverableSignature), Self::Error> { // Prevent the method from being invoked when the key has been cleared from the memory. Do note that Stronghold // only asks for a key for reading / writing a snapshot, so without our cached key this method is invocable, but // it doesn't make sense when it comes to our user (signing transactions / generating addresses without a key). @@ -225,13 +250,17 @@ impl SecretManage for StrongholdAdapter { SECRET_VAULT_PATH, [ DERIVE_OUTPUT_RECORD_PATH, - &chain.segments().iter().flat_map(|seg| seg.bs()).collect::>(), + &chain + .to_chain::() + .into_iter() + .flat_map(|seg| seg.ser32()) + .collect::>(), ] .concat(), ); // Derive a SLIP-10 private key in the vault. - self.slip10_derive(Curve::Secp256k1, chain.clone(), seed_location, derive_location.clone()) + self.slip10_derive(Curve::Secp256k1, chain, seed_location, derive_location.clone()) .await?; // Get the public key from the derived SLIP-10 private key in the vault. @@ -296,14 +325,19 @@ impl SecretManagerConfig for StrongholdAdapter { /// Private methods for the secret manager implementation. impl StrongholdAdapter { /// Execute [Procedure::BIP39Recover] in Stronghold to put a mnemonic into the Stronghold vault. - async fn bip39_recover(&self, mnemonic: String, passphrase: Option, output: Location) -> Result<(), Error> { + async fn bip39_recover( + &self, + mnemonic: Mnemonic, + passphrase: Option, + output: Location, + ) -> Result<(), Error> { self.stronghold .lock() .await .get_client(PRIVATE_DATA_CLIENT_PATH)? .execute_procedure(procedures::BIP39Recover { mnemonic, - passphrase, + passphrase: passphrase.map(Into::into).unwrap_or_default(), output, })?; @@ -314,10 +348,18 @@ impl StrongholdAdapter { async fn slip10_derive( &self, curve: Curve, - chain: Chain, + chain: Bip44, input: Slip10DeriveInput, output: Location, ) -> Result<(), Error> { + let chain = match curve { + Curve::Ed25519 => chain + .to_chain::() + .into_iter() + .map(Into::into) + .collect(), + Curve::Secp256k1 => chain.to_chain::().to_vec(), + }; if let Err(err) = self .stronghold .lock() @@ -351,31 +393,33 @@ impl StrongholdAdapter { /// Execute [Procedure::PublicKey] in Stronghold to get an Ed25519 public key from the SLIP-10 /// private key located in `private_key`. - async fn ed25519_public_key(&self, private_key: Location) -> Result<[u8; 32], Error> { - Ok(self - .stronghold - .lock() - .await - .get_client(PRIVATE_DATA_CLIENT_PATH)? - .execute_procedure(procedures::PublicKey { - ty: KeyType::Ed25519, - private_key, - })? - .try_into() - .unwrap()) + async fn ed25519_public_key(&self, private_key: Location) -> Result { + Ok(ed25519::PublicKey::try_from_bytes( + self.stronghold + .lock() + .await + .get_client(PRIVATE_DATA_CLIENT_PATH)? + .execute_procedure(procedures::PublicKey { + ty: KeyType::Ed25519, + private_key, + })? + .try_into() + .unwrap(), + )?) } /// Execute [Procedure::Ed25519Sign] in Stronghold to sign `msg` with `private_key` stored in the Stronghold vault. - async fn ed25519_sign(&self, private_key: Location, msg: &[u8]) -> Result<[u8; 64], Error> { - Ok(self - .stronghold - .lock() - .await - .get_client(PRIVATE_DATA_CLIENT_PATH)? - .execute_procedure(procedures::Ed25519Sign { - private_key, - msg: msg.to_vec(), - })?) + async fn ed25519_sign(&self, private_key: Location, msg: &[u8]) -> Result { + Ok(ed25519::Signature::from_bytes( + self.stronghold + .lock() + .await + .get_client(PRIVATE_DATA_CLIENT_PATH)? + .execute_procedure(procedures::Ed25519Sign { + private_key, + msg: msg.to_vec(), + })?, + )) } /// Execute [Procedure::Secp256k1EcdsaSign] in Stronghold to sign `msg` with `private_key` stored in the Stronghold @@ -384,8 +428,8 @@ impl StrongholdAdapter { &self, private_key: Location, msg: &[u8], - ) -> Result { - Ok(secp256k1_ecdsa::Signature::try_from_bytes( + ) -> Result { + Ok(secp256k1_ecdsa::RecoverableSignature::try_from_bytes( &self .stronghold .lock() @@ -394,6 +438,7 @@ impl StrongholdAdapter { .execute_procedure(procedures::Secp256k1EcdsaSign { private_key, msg: msg.to_vec(), + flavor: procedures::Secp256k1EcdsaFlavor::Keccak256, })?, )?) } @@ -414,7 +459,7 @@ impl StrongholdAdapter { } /// Store a mnemonic into the Stronghold vault. - pub async fn store_mnemonic(&self, mut mnemonic: String) -> Result<(), Error> { + pub async fn store_mnemonic(&self, mnemonic: impl Borrow + Send) -> Result<(), Error> { // The key needs to be supplied first. if self.key_provider.lock().await.is_none() { return Err(Error::KeyCleared); @@ -424,8 +469,7 @@ impl StrongholdAdapter { let output = Location::generic(SECRET_VAULT_PATH, SEED_RECORD_PATH); // Trim the mnemonic, in case it hasn't been, as otherwise the restored seed would be wrong. - let trimmed_mnemonic = mnemonic.trim().to_string(); - mnemonic.zeroize(); + let trimmed_mnemonic = Mnemonic::from(mnemonic.borrow().trim().to_owned()); // Check if the mnemonic is valid. crypto::keys::bip39::wordlist::verify(&trimmed_mnemonic, &crypto::keys::bip39::wordlist::ENGLISH) @@ -467,8 +511,8 @@ mod tests { let stronghold_path = "test_ed25519_address_generation.stronghold"; // Remove potential old stronghold file std::fs::remove_file(stronghold_path).ok(); - let mnemonic = String::from( - "giant dynamic museum toddler six deny defense ostrich bomb access mercy blood explain muscle shoot shallow glad autumn author calm heavy hawk abuse rally", + let mnemonic = Mnemonic::from( + "giant dynamic museum toddler six deny defense ostrich bomb access mercy blood explain muscle shoot shallow glad autumn author calm heavy hawk abuse rally".to_owned(), ); let stronghold_adapter = StrongholdAdapter::builder() .password("drowssap".to_owned()) @@ -499,8 +543,8 @@ mod tests { let stronghold_path = "test_evm_address_generation.stronghold"; // Remove potential old stronghold file std::fs::remove_file(stronghold_path).ok(); - let mnemonic = String::from( - "endorse answer radar about source reunion marriage tag sausage weekend frost daring base attack because joke dream slender leisure group reason prepare broken river", + let mnemonic = Mnemonic::from( + "endorse answer radar about source reunion marriage tag sausage weekend frost daring base attack because joke dream slender leisure group reason prepare broken river".to_owned(), ); let stronghold_adapter = StrongholdAdapter::builder() .password("drowssap".to_owned()) @@ -531,8 +575,8 @@ mod tests { let stronghold_path = "test_key_cleared.stronghold"; // Remove potential old stronghold file std::fs::remove_file(stronghold_path).ok(); - let mnemonic = String::from( - "giant dynamic museum toddler six deny defense ostrich bomb access mercy blood explain muscle shoot shallow glad autumn author calm heavy hawk abuse rally", + let mnemonic = Mnemonic::from( + "giant dynamic museum toddler six deny defense ostrich bomb access mercy blood explain muscle shoot shallow glad autumn author calm heavy hawk abuse rally".to_owned(), ); let stronghold_adapter = StrongholdAdapter::builder() .password("drowssap".to_owned()) diff --git a/sdk/src/client/utils.rs b/sdk/src/client/utils.rs index 609d98e9c8..2500b5e48f 100644 --- a/sdk/src/client/utils.rs +++ b/sdk/src/client/utils.rs @@ -3,15 +3,16 @@ //! Utility functions for IOTA +use core::borrow::Borrow; use std::collections::HashMap; use crypto::{ hashes::{blake2b::Blake2b256, Digest}, - keys::{bip39::wordlist, slip10::Seed}, + keys::bip39::{wordlist, Mnemonic, MnemonicRef, Passphrase, Seed}, utils, }; use serde::{Deserialize, Serialize}; -use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; +use zeroize::{Zeroize, ZeroizeOnDrop}; use super::{Client, ClientInner}; use crate::{ @@ -51,7 +52,7 @@ pub fn hex_public_key_to_bech32_address(hex: &str, bech32_hrp: impl ConvertTo
Result { +pub fn generate_mnemonic() -> Result { let mut entropy = [0u8; 32]; utils::rand::fill(&mut entropy)?; let mnemonic = wordlist::encode(&entropy, &crypto::keys::bip39::wordlist::ENGLISH) @@ -61,30 +62,23 @@ pub fn generate_mnemonic() -> Result { } /// Returns a hex encoded seed for a mnemonic. -pub fn mnemonic_to_hex_seed(mnemonic: &str) -> Result { - // trim because empty spaces could create a different seed https://github.com/iotaledger/crypto.rs/issues/125 - let mnemonic = mnemonic.trim(); - // first we check if the mnemonic is valid to give meaningful errors - verify_mnemonic(mnemonic)?; - let mut mnemonic_seed = [0u8; 64]; - crypto::keys::bip39::mnemonic_to_seed(mnemonic, "", &mut mnemonic_seed); - Ok(prefix_hex::encode(mnemonic_seed)) +pub fn mnemonic_to_hex_seed(mnemonic: impl Borrow) -> Result { + Ok(prefix_hex::encode(mnemonic_to_seed(mnemonic)?.as_ref())) } /// Returns a seed for a mnemonic. -pub fn mnemonic_to_seed(mnemonic: Zeroizing) -> Result { - // trim because empty spaces could create a different seed https://github.com/iotaledger/crypto.rs/issues/125 - let mnemonic = mnemonic.as_str().trim(); +pub fn mnemonic_to_seed(mnemonic: impl Borrow) -> Result { // first we check if the mnemonic is valid to give meaningful errors - verify_mnemonic(mnemonic)?; - let mut mnemonic_seed = Zeroizing::new([0u8; 64]); - crypto::keys::bip39::mnemonic_to_seed(mnemonic, "", &mut mnemonic_seed); - Ok(Seed::from_bytes(mnemonic_seed.as_ref())) + verify_mnemonic(mnemonic.borrow())?; + Ok(crypto::keys::bip39::mnemonic_to_seed( + mnemonic.borrow(), + &Passphrase::default(), + )) } /// Verifies that a &str is a valid mnemonic. -pub fn verify_mnemonic(mnemonic: &str) -> Result<()> { - crypto::keys::bip39::wordlist::verify(mnemonic, &crypto::keys::bip39::wordlist::ENGLISH) +pub fn verify_mnemonic(mnemonic: impl Borrow) -> Result<()> { + crypto::keys::bip39::wordlist::verify(mnemonic.borrow(), &crypto::keys::bip39::wordlist::ENGLISH) .map_err(|e| crate::client::Error::InvalidMnemonic(format!("{e:?}")))?; Ok(()) } @@ -164,17 +158,17 @@ impl Client { } /// Generates a new mnemonic. - pub fn generate_mnemonic() -> Result { + pub fn generate_mnemonic() -> Result { generate_mnemonic() } /// Returns a seed for a mnemonic. - pub fn mnemonic_to_seed(mnemonic: Zeroizing) -> Result { + pub fn mnemonic_to_seed(mnemonic: impl Borrow) -> Result { mnemonic_to_seed(mnemonic) } /// Returns a hex encoded seed for a mnemonic. - pub fn mnemonic_to_hex_seed(mnemonic: &str) -> Result { + pub fn mnemonic_to_hex_seed(mnemonic: impl Borrow) -> Result { mnemonic_to_hex_seed(mnemonic) } diff --git a/sdk/src/types/block/payload/milestone/mod.rs b/sdk/src/types/block/payload/milestone/mod.rs index c008ff213c..a831686096 100644 --- a/sdk/src/types/block/payload/milestone/mod.rs +++ b/sdk/src/types/block/payload/milestone/mod.rs @@ -14,7 +14,7 @@ pub mod option; use alloc::{string::String, vec::Vec}; use core::{fmt::Debug, ops::RangeInclusive}; -use crypto::{signatures::ed25519, Error as CryptoError}; +use crypto::Error as CryptoError; use iterator_sorted::is_unique_sorted; pub(crate) use option::{MilestoneOptionCount, ReceiptFundsCount}; use packable::{bounded::BoundedU8, prefix::VecPrefix, Packable}; @@ -123,18 +123,14 @@ impl MilestonePayload { if !applicable_public_keys.contains(&hex::encode(signature.public_key())) { return Err(MilestoneValidationError::UnapplicablePublicKey(prefix_hex::encode( - *signature.public_key(), + signature.public_key().as_ref(), ))); } - let ed25519_public_key = ed25519::PublicKey::try_from_bytes(*signature.public_key()) - .map_err(MilestoneValidationError::Crypto)?; - let ed25519_signature = ed25519::Signature::from_bytes(*signature.signature()); - - if !ed25519_public_key.verify(&ed25519_signature, &essence_hash) { + if !signature.verify(&essence_hash) { return Err(MilestoneValidationError::InvalidSignature( index, - prefix_hex::encode(signature.public_key()), + prefix_hex::encode(signature.public_key().as_ref()), )); } } diff --git a/sdk/src/types/block/rand/mod.rs b/sdk/src/types/block/rand/mod.rs index 45f9f0f55d..4c72d95cb0 100644 --- a/sdk/src/types/block/rand/mod.rs +++ b/sdk/src/types/block/rand/mod.rs @@ -27,6 +27,8 @@ pub mod parents; pub mod payload; /// Module providing random receipt generation utilities. pub mod receipt; +/// Module providing random signature generation utilities. +pub mod signature; /// Module providing random string generation utilities. pub mod string; /// Module providing random transaction generation utilities. diff --git a/sdk/src/types/block/rand/payload.rs b/sdk/src/types/block/rand/payload.rs index e93e2d30cb..0f25ebcd12 100644 --- a/sdk/src/types/block/rand/payload.rs +++ b/sdk/src/types/block/rand/payload.rs @@ -9,14 +9,14 @@ use crate::types::block::{ Payload, }, rand::{ - bytes::{rand_bytes, rand_bytes_array}, + bytes::rand_bytes, input::rand_treasury_input, milestone::{rand_merkle_root, rand_milestone_id, rand_milestone_index}, number::{rand_number, rand_number_range}, output::rand_treasury_output, parents::rand_parents, + signature::rand_signature, }, - signature::{Ed25519Signature, Signature}, }; /// Generates a random tagged data payload. @@ -47,10 +47,7 @@ pub fn rand_milestone_payload(protocol_version: u8) -> MilestonePayload { MilestoneOptions::from_vec(Vec::new()).unwrap(), ) .unwrap(); - let signatures = [Signature::from(Ed25519Signature::new( - rand_bytes_array(), - rand_bytes_array(), - ))]; + let signatures = [rand_signature()]; MilestonePayload::new(essence, signatures).unwrap() } diff --git a/sdk/src/types/block/rand/signature.rs b/sdk/src/types/block/rand/signature.rs new file mode 100644 index 0000000000..7ffd5d5508 --- /dev/null +++ b/sdk/src/types/block/rand/signature.rs @@ -0,0 +1,30 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crypto::{ + keys::{ + bip39::{mnemonic_to_seed, wordlist, Passphrase}, + slip10::{Seed, Segment}, + }, + signatures::ed25519, +}; + +use super::bytes::{rand_bytes, rand_bytes_array}; +use crate::types::block::signature::{Ed25519Signature, Signature}; + +pub fn rand_ed25519_signature() -> Ed25519Signature { + let mnemonic = wordlist::encode(&rand_bytes_array::<32>(), &wordlist::ENGLISH).unwrap(); + let seed = Seed::from(mnemonic_to_seed(&mnemonic, &Passphrase::default())); + let chain = [0; 5]; + let private_key = seed + .derive::(chain.into_iter().map(Segment::harden)) + .secret_key(); + let public_key = private_key.public_key(); + let signature = private_key.sign(&rand_bytes(64)); + + Ed25519Signature::new(public_key, signature) +} + +pub fn rand_signature() -> Signature { + Signature::from(rand_ed25519_signature()) +} diff --git a/sdk/src/types/block/signature/ed25519.rs b/sdk/src/types/block/signature/ed25519.rs index 2c05d892b0..6dc4d6ecdf 100644 --- a/sdk/src/types/block/signature/ed25519.rs +++ b/sdk/src/types/block/signature/ed25519.rs @@ -7,16 +7,21 @@ use crypto::{ hashes::{blake2b::Blake2b256, Digest}, signatures::ed25519::{PublicKey, Signature}, }; +use packable::{ + error::{UnpackError, UnpackErrorExt}, + packer::Packer, + unpacker::Unpacker, + Packable, +}; use crate::types::block::{address::Ed25519Address, Error}; /// An Ed25519 signature. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, packable::Packable)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Ed25519Signature { - public_key: [u8; Self::PUBLIC_KEY_LENGTH], - #[cfg_attr(feature = "serde", serde(with = "serde_big_array::BigArray"))] - signature: [u8; Self::SIGNATURE_LENGTH], + public_key: PublicKey, + signature: Signature, } impl Ed25519Signature { @@ -28,22 +33,33 @@ impl Ed25519Signature { pub const SIGNATURE_LENGTH: usize = Signature::LENGTH; /// Creates a new [`Ed25519Signature`]. - pub fn new(public_key: [u8; Self::PUBLIC_KEY_LENGTH], signature: [u8; Self::SIGNATURE_LENGTH]) -> Self { + pub fn new(public_key: PublicKey, signature: Signature) -> Self { Self { public_key, signature } } + /// Creates a new [`Ed25519Signature`] from bytes. + pub fn try_from_bytes( + public_key: [u8; Self::PUBLIC_KEY_LENGTH], + signature: [u8; Self::SIGNATURE_LENGTH], + ) -> Result { + Ok(Self::new( + PublicKey::try_from_bytes(public_key)?, + Signature::from_bytes(signature), + )) + } + /// Returns the public key of an [`Ed25519Signature`]. - pub fn public_key(&self) -> &[u8; Self::PUBLIC_KEY_LENGTH] { + pub fn public_key(&self) -> &PublicKey { &self.public_key } /// Return the actual signature of an [`Ed25519Signature`]. - pub fn signature(&self) -> &[u8; Self::SIGNATURE_LENGTH] { + pub fn signature(&self) -> &Signature { &self.signature } - pub fn verify(&self, message: &[u8]) -> Result { - Ok(PublicKey::try_from_bytes(self.public_key)?.verify(&Signature::from_bytes(self.signature), message)) + pub fn verify(&self, message: &[u8]) -> bool { + self.public_key.verify(&self.signature, message) } /// Verifies the [`Ed25519Signature`] for a message against an [`Ed25519Address`]. @@ -57,7 +73,7 @@ impl Ed25519Signature { }); } - if !PublicKey::try_from_bytes(self.public_key)?.verify(&Signature::from_bytes(self.signature), message) { + if !self.verify(message) { return Err(Error::InvalidSignature); } @@ -77,12 +93,41 @@ impl fmt::Debug for Ed25519Signature { } f.debug_struct("Ed25519Signature") - .field("public_key", &UnquotedStr(&prefix_hex::encode(self.public_key))) - .field("signature", &UnquotedStr(&prefix_hex::encode(self.signature))) + .field( + "public_key", + &UnquotedStr(&prefix_hex::encode(self.public_key.as_slice())), + ) + .field( + "signature", + &UnquotedStr(&prefix_hex::encode(self.signature.to_bytes())), + ) .finish() } } +impl Packable for Ed25519Signature { + type UnpackError = Error; + type UnpackVisitor = (); + + fn pack(&self, packer: &mut P) -> Result<(), P::Error> { + self.public_key.to_bytes().pack(packer)?; + self.signature.to_bytes().pack(packer)?; + + Ok(()) + } + + fn unpack( + unpacker: &mut U, + visitor: &Self::UnpackVisitor, + ) -> Result> { + let public_key = <[u8; Self::PUBLIC_KEY_LENGTH]>::unpack::<_, VERIFY>(unpacker, visitor).coerce()?; + let signature = <[u8; Self::SIGNATURE_LENGTH]>::unpack::<_, VERIFY>(unpacker, visitor).coerce()?; + Self::try_from_bytes(public_key, signature) + .map_err(UnpackError::Packable) + .coerce() + } +} + #[allow(missing_docs)] pub mod dto { use alloc::string::String; @@ -106,8 +151,8 @@ pub mod dto { fn from(value: &Ed25519Signature) -> Self { Self { kind: Ed25519Signature::KIND, - public_key: prefix_hex::encode(value.public_key), - signature: prefix_hex::encode(value.signature), + public_key: prefix_hex::encode(value.public_key.as_slice()), + signature: prefix_hex::encode(value.signature.to_bytes()), } } } @@ -116,10 +161,10 @@ pub mod dto { type Error = Error; fn try_from(value: Ed25519SignatureDto) -> Result { - Ok(Self::new( + Self::try_from_bytes( prefix_hex::decode(&value.public_key).map_err(|_| Error::InvalidField("publicKey"))?, prefix_hex::decode(&value.signature).map_err(|_| Error::InvalidField("signature"))?, - )) + ) } } } diff --git a/sdk/src/types/block/signature/mod.rs b/sdk/src/types/block/signature/mod.rs index 8082dc5eb6..99f074ba8a 100644 --- a/sdk/src/types/block/signature/mod.rs +++ b/sdk/src/types/block/signature/mod.rs @@ -3,6 +3,8 @@ mod ed25519; +use alloc::boxed::Box; + use derive_more::From; pub use self::ed25519::Ed25519Signature; @@ -13,7 +15,7 @@ use crate::types::block::Error; /// This is defined as part of the Unspent Transaction Output (UTXO) transaction protocol. /// /// RFC: -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)] +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, packable::Packable)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize), @@ -24,7 +26,13 @@ use crate::types::block::Error; pub enum Signature { /// An Ed25519 signature. #[packable(tag = Ed25519Signature::KIND)] - Ed25519(Ed25519Signature), + Ed25519(Box), +} + +impl From for Signature { + fn from(value: Ed25519Signature) -> Self { + Self::Ed25519(Box::new(value)) + } } impl core::fmt::Debug for Signature { @@ -56,13 +64,13 @@ pub mod dto { #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, From)] #[serde(untagged)] pub enum SignatureDto { - Ed25519(Ed25519SignatureDto), + Ed25519(Box), } impl From<&Signature> for SignatureDto { fn from(value: &Signature) -> Self { match value { - Signature::Ed25519(s) => Self::Ed25519(s.into()), + Signature::Ed25519(s) => Self::Ed25519(Box::new(s.as_ref().into())), } } } @@ -72,7 +80,7 @@ pub mod dto { fn try_from(value: SignatureDto) -> Result { match value { - SignatureDto::Ed25519(s) => Ok(Self::Ed25519(s.try_into()?)), + SignatureDto::Ed25519(s) => Ok(Self::Ed25519(Box::new((*s).try_into()?))), } } } diff --git a/sdk/src/types/block/unlock/mod.rs b/sdk/src/types/block/unlock/mod.rs index 1167742a1d..67cb57036f 100644 --- a/sdk/src/types/block/unlock/mod.rs +++ b/sdk/src/types/block/unlock/mod.rs @@ -156,10 +156,7 @@ pub mod dto { signature::dto::SignatureUnlockDto, }; use crate::types::block::{ - signature::{ - dto::{Ed25519SignatureDto, SignatureDto}, - Ed25519Signature, Signature, - }, + signature::{dto::SignatureDto, Ed25519Signature, Signature}, Error, }; @@ -178,11 +175,7 @@ pub mod dto { Unlock::Signature(signature) => match signature.signature() { Signature::Ed25519(ed) => Self::Signature(SignatureUnlockDto { kind: SignatureUnlock::KIND, - signature: SignatureDto::Ed25519(Ed25519SignatureDto { - kind: Ed25519Signature::KIND, - public_key: prefix_hex::encode(ed.public_key()), - signature: prefix_hex::encode(ed.signature()), - }), + signature: SignatureDto::Ed25519(Box::new(ed.as_ref().into())), }), }, Unlock::Reference(r) => Self::Reference(ReferenceUnlockDto { @@ -207,15 +200,9 @@ pub mod dto { fn try_from(value: UnlockDto) -> Result { match value { UnlockDto::Signature(s) => match s.signature { - SignatureDto::Ed25519(ed) => { - let public_key = - prefix_hex::decode(&ed.public_key).map_err(|_| Error::InvalidField("publicKey"))?; - let signature = - prefix_hex::decode(&ed.signature).map_err(|_| Error::InvalidField("signature"))?; - Ok(Self::Signature(SignatureUnlock::from(Signature::Ed25519( - Ed25519Signature::new(public_key, signature), - )))) - } + SignatureDto::Ed25519(ed) => Ok(Self::Signature(SignatureUnlock::from(Signature::Ed25519( + Ed25519Signature::try_from(*ed)?.into(), + )))), }, UnlockDto::Reference(r) => Ok(Self::Reference(ReferenceUnlock::new(r.index)?)), UnlockDto::Alias(a) => Ok(Self::Alias(AliasUnlock::new(a.index)?)), diff --git a/sdk/src/utils/serde.rs b/sdk/src/utils/serde.rs index 2dfeb15aff..eac478f854 100644 --- a/sdk/src/utils/serde.rs +++ b/sdk/src/utils/serde.rs @@ -78,3 +78,50 @@ pub mod option_prefix_hex_vec { .transpose() } } + +#[cfg(feature = "client")] +pub mod bip44 { + use crypto::keys::bip44::Bip44; + use serde::{Deserialize, Serialize}; + + #[derive(Default, Serialize, Deserialize)] + #[serde(default, rename_all = "camelCase", remote = "Bip44")] + pub struct Bip44Def { + #[serde(default = "default_coin_type")] + coin_type: u32, + account: u32, + change: u32, + address_index: u32, + } + + const fn default_coin_type() -> u32 { + crate::client::constants::IOTA_COIN_TYPE + } + + pub mod option_bip44 { + use serde::{Deserializer, Serializer}; + + use super::{Bip44Def, *}; + + pub fn serialize(value: &Option, serializer: S) -> Result + where + S: Serializer, + { + #[derive(Serialize)] + struct Helper<'a>(#[serde(with = "Bip44Def")] &'a Bip44); + + value.as_ref().map(Helper).serialize(serializer) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct Helper(#[serde(with = "Bip44Def")] Bip44); + + let helper = Option::deserialize(deserializer)?; + Ok(helper.map(|Helper(external)| external)) + } + } +} diff --git a/sdk/src/wallet/account/mod.rs b/sdk/src/wallet/account/mod.rs index 925345e8fb..f99b8c484e 100644 --- a/sdk/src/wallet/account/mod.rs +++ b/sdk/src/wallet/account/mod.rs @@ -593,10 +593,10 @@ fn serialize() { .unwrap(), ); - let pub_key_bytes: [u8; 32] = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); - let sig_bytes: [u8; 64] = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); - let signature = Ed25519Signature::new(pub_key_bytes, sig_bytes); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::Ed25519(signature))); + let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); + let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); + let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); + let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); let ref_unlock = Unlock::Reference(ReferenceUnlock::new(0).unwrap()); let unlocks = Unlocks::new([sig_unlock, ref_unlock]).unwrap(); diff --git a/sdk/src/wallet/account/operations/syncing/outputs.rs b/sdk/src/wallet/account/operations/syncing/outputs.rs index 340fab7d3a..5fbec2341c 100644 --- a/sdk/src/wallet/account/operations/syncing/outputs.rs +++ b/sdk/src/wallet/account/operations/syncing/outputs.rs @@ -1,11 +1,11 @@ // Copyright 2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use instant::Instant; use crate::{ - client::{constants::HD_WALLET_TYPE, secret::SecretManage, Client}, + client::{secret::SecretManage, Client}, types::{ api::core::response::OutputWithMetadataResponse, block::{ @@ -48,14 +48,12 @@ where .get(output_with_meta.metadata().transaction_id()) .map_or(false, |tx| !tx.incoming); - // 44 is for BIP 44 (HD wallets) and 4218 is the registered index for IOTA https://github.com/satoshilabs/slips/blob/master/slip-0044.md - let chain = Chain::from_u32_hardened([ - HD_WALLET_TYPE, - account_details.coin_type, - account_details.index, - associated_address.internal as u32, - associated_address.key_index, - ]); + // BIP 44 (HD wallets) and 4218 is the registered index for IOTA https://github.com/satoshilabs/slips/blob/master/slip-0044.md + let chain = Bip44::new() + .with_coin_type(account_details.coin_type) + .with_account(account_details.index) + .with_change(associated_address.internal as _) + .with_address_index(associated_address.key_index); OutputData { output_id: output_with_meta.metadata().output_id().to_owned(), diff --git a/sdk/src/wallet/account/operations/transaction/high_level/minting/create_native_token.rs b/sdk/src/wallet/account/operations/transaction/high_level/minting/create_native_token.rs index 36f9cae1f9..8f4654c37a 100644 --- a/sdk/src/wallet/account/operations/transaction/high_level/minting/create_native_token.rs +++ b/sdk/src/wallet/account/operations/transaction/high_level/minting/create_native_token.rs @@ -63,8 +63,7 @@ impl From<&CreateNativeTokenTransaction> for CreateNativeTokenTransactionDto { } /// The result of preparing a transaction to create a native token -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug)] pub struct PreparedCreateNativeTokenTransaction { pub token_id: TokenId, pub transaction: PreparedTransactionData, diff --git a/sdk/src/wallet/account/types/mod.rs b/sdk/src/wallet/account/types/mod.rs index e5d21df7f5..be8a8dab4c 100644 --- a/sdk/src/wallet/account/types/mod.rs +++ b/sdk/src/wallet/account/types/mod.rs @@ -9,7 +9,7 @@ pub mod participation; use std::str::FromStr; -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use serde::{Deserialize, Deserializer, Serialize}; pub use self::{ @@ -17,7 +17,7 @@ pub use self::{ balance::{Balance, BaseCoinBalance, NativeTokensBalance, RequiredStorageDeposit}, }; use crate::{ - client::{constants::HD_WALLET_TYPE, secret::types::InputSigningData}, + client::secret::types::InputSigningData, types::{ api::core::response::OutputWithMetadataResponse, block::{ @@ -30,6 +30,7 @@ use crate::{ BlockId, }, }, + utils::serde::bip44::option_bip44, wallet::account::AccountDetails, }; @@ -49,8 +50,9 @@ pub struct OutputData { /// Network ID pub network_id: u64, pub remainder: bool, - // bip32 path - pub chain: Option, + // bip44 path + #[serde(with = "option_bip44")] + pub chain: Option, } impl OutputData { @@ -65,20 +67,20 @@ impl OutputData { .required_and_unlocked_address(current_time, &self.output_id, alias_transition)?; let chain = if unlock_address == self.address { - self.chain.clone() + self.chain } else if let Address::Ed25519(_) = unlock_address { if let Some(address) = account .addresses_with_unspent_outputs .iter() .find(|a| a.address.inner == unlock_address) { - Some(Chain::from_u32_hardened([ - HD_WALLET_TYPE, - account.coin_type, - account.index, - address.internal as u32, - address.key_index, - ])) + Some( + Bip44::new() + .with_coin_type(account.coin_type) + .with_account(account.index) + .with_change(address.internal as _) + .with_address_index(address.key_index), + ) } else { return Ok(None); } @@ -114,7 +116,8 @@ pub struct OutputDataDto { /// Remainder pub remainder: bool, /// Bip32 path - pub chain: Option, + #[serde(with = "option_bip44")] + pub chain: Option, } impl From<&OutputData> for OutputDataDto { @@ -127,7 +130,7 @@ impl From<&OutputData> for OutputDataDto { address: AddressDto::from(&value.address), network_id: value.network_id.to_string(), remainder: value.remainder, - chain: value.chain.clone(), + chain: value.chain, } } } diff --git a/sdk/src/wallet/core/mod.rs b/sdk/src/wallet/core/mod.rs index 661001ed53..f007ac896a 100644 --- a/sdk/src/wallet/core/mod.rs +++ b/sdk/src/wallet/core/mod.rs @@ -9,6 +9,7 @@ use std::sync::{ Arc, }; +use crypto::keys::bip39::{Mnemonic, MnemonicRef}; use tokio::sync::RwLock; pub use self::builder::WalletBuilder; @@ -195,12 +196,12 @@ impl WalletInner { } /// Generates a new random mnemonic. - pub fn generate_mnemonic(&self) -> crate::wallet::Result { + pub fn generate_mnemonic(&self) -> crate::wallet::Result { Ok(Client::generate_mnemonic()?) } /// Verify that a &str is a valid mnemonic. - pub fn verify_mnemonic(&self, mnemonic: &str) -> crate::wallet::Result<()> { + pub fn verify_mnemonic(&self, mnemonic: &MnemonicRef) -> crate::wallet::Result<()> { verify_mnemonic(mnemonic)?; Ok(()) } diff --git a/sdk/src/wallet/core/operations/stronghold.rs b/sdk/src/wallet/core/operations/stronghold.rs index 8e7d14359f..b3d997fc03 100644 --- a/sdk/src/wallet/core/operations/stronghold.rs +++ b/sdk/src/wallet/core/operations/stronghold.rs @@ -3,6 +3,8 @@ use std::time::Duration; +use crypto::keys::bip39::Mnemonic; + use crate::{ client::{secret::SecretManager, stronghold::StrongholdAdapter, utils::Password}, wallet::Wallet, @@ -44,7 +46,7 @@ impl Wallet { } /// Stores a mnemonic into the Stronghold vault - pub async fn store_mnemonic(&self, mnemonic: String) -> crate::wallet::Result<()> { + pub async fn store_mnemonic(&self, mnemonic: Mnemonic) -> crate::wallet::Result<()> { if let SecretManager::Stronghold(stronghold) = &mut *self.secret_manager.write().await { stronghold.store_mnemonic(mnemonic).await?; } @@ -98,7 +100,7 @@ impl Wallet { } /// Stores a mnemonic into the Stronghold vault - pub async fn store_mnemonic(&self, mnemonic: String) -> crate::wallet::Result<()> { + pub async fn store_mnemonic(&self, mnemonic: Mnemonic) -> crate::wallet::Result<()> { Ok(self.secret_manager.write().await.store_mnemonic(mnemonic).await?) } diff --git a/sdk/src/wallet/migration/migrate_2.rs b/sdk/src/wallet/migration/migrate_2.rs new file mode 100644 index 0000000000..21cf8dd254 --- /dev/null +++ b/sdk/src/wallet/migration/migrate_2.rs @@ -0,0 +1,109 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use super::*; + +pub struct Migrate; + +fn migrate_account(account: &mut serde_json::Value) -> Result<()> { + for output_data in account["outputs"] + .as_object_mut() + .ok_or(Error::Storage("malformatted outputs".to_owned()))? + .values_mut() + { + if let Some(chain) = output_data.get_mut("chain") { + ConvertChain::check(chain)?; + } + } + + for output_data in account["unspentOutputs"] + .as_object_mut() + .ok_or(Error::Storage("malformatted unspent outputs".to_owned()))? + .values_mut() + { + if let Some(chain) = output_data.get_mut("chain") { + ConvertChain::check(chain)?; + } + } + Ok(()) +} + +#[async_trait] +impl MigrationData for Migrate { + const ID: usize = 2; + const SDK_VERSION: &'static str = "0.4.0"; + const DATE: time::Date = time::macros::date!(2023 - 07 - 13); +} + +#[async_trait] +#[cfg(feature = "storage")] +impl Migration for Migrate { + async fn migrate(storage: &crate::wallet::storage::Storage) -> Result<()> { + use crate::wallet::storage::constants::{ACCOUNTS_INDEXATION_KEY, ACCOUNT_INDEXATION_KEY}; + + if let Some(account_indexes) = storage.get::>(ACCOUNTS_INDEXATION_KEY).await? { + for account_index in account_indexes { + if let Some(mut account) = storage + .get::(&format!("{ACCOUNT_INDEXATION_KEY}{account_index}")) + .await? + { + migrate_account(&mut account)?; + + storage + .set(&format!("{ACCOUNT_INDEXATION_KEY}{account_index}"), &account) + .await?; + } + } + } + Ok(()) + } +} + +#[async_trait] +#[cfg(feature = "stronghold")] +impl Migration for Migrate { + async fn migrate(storage: &crate::client::stronghold::StrongholdAdapter) -> Result<()> { + use crate::{ + client::storage::StorageAdapter, + wallet::core::operations::stronghold_backup::stronghold_snapshot::ACCOUNTS_KEY, + }; + + if let Some(mut accounts) = storage.get::>(ACCOUNTS_KEY).await? { + for account in &mut accounts { + migrate_account(account)?; + } + storage.set(ACCOUNTS_KEY, &accounts).await?; + } + Ok(()) + } +} + +mod types { + use serde::{Deserialize, Serialize}; + + pub const HARDEN_MASK: u32 = 1 << 31; + + #[derive(Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct Bip44 { + pub coin_type: u32, + pub account: u32, + pub change: u32, + pub address_index: u32, + } +} + +struct ConvertChain; +impl Convert for ConvertChain { + type New = types::Bip44; + type Old = [u32; 5]; + + fn convert(old: Self::Old) -> crate::wallet::Result { + Ok(Self::New { + coin_type: old[1] & !types::HARDEN_MASK, + account: old[2] & !types::HARDEN_MASK, + change: old[3] & !types::HARDEN_MASK, + address_index: old[4] & !types::HARDEN_MASK, + }) + } +} diff --git a/sdk/src/wallet/migration/mod.rs b/sdk/src/wallet/migration/mod.rs index a409d96db6..789c1cb81d 100644 --- a/sdk/src/wallet/migration/mod.rs +++ b/sdk/src/wallet/migration/mod.rs @@ -3,6 +3,7 @@ mod migrate_0; mod migrate_1; +mod migrate_2; use std::collections::HashMap; @@ -26,26 +27,28 @@ static MIGRATIONS: Lazy> = Lazy::new(|| #[cfg(feature = "storage")] { use super::storage::Storage; - const STORAGE_MIGRATIONS: [(Option, &'static dyn DynMigration); 2] = [ + const STORAGE_MIGRATIONS: [(Option, &'static dyn DynMigration); 3] = [ // In order to add a new storage migration, add an entry at the bottom of this list // and change the list length above. // The entry should be in the form of a key-value pair, from previous migration to next. // i.e. (Some(migrate_::Migrate::ID), &migrate_::Migrate) (None, &migrate_0::Migrate), (Some(migrate_0::Migrate::ID), &migrate_1::Migrate), + (Some(migrate_1::Migrate::ID), &migrate_2::Migrate), ]; migrations.insert(std::collections::HashMap::from(STORAGE_MIGRATIONS)); } #[cfg(feature = "stronghold")] { use crate::client::stronghold::StrongholdAdapter; - const BACKUP_MIGRATIONS: [(Option, &'static dyn DynMigration); 2] = [ + const BACKUP_MIGRATIONS: [(Option, &'static dyn DynMigration); 3] = [ // In order to add a new backup migration, and add an entry at the bottom of this list // and change the list length above. // The entry should be in the form of a key-value pair, from previous migration to next. // i.e. (Some(migrate_::Migrate::ID), &migrate_::Migrate) (None, &migrate_0::Migrate), (Some(migrate_0::Migrate::ID), &migrate_1::Migrate), + (Some(migrate_1::Migrate::ID), &migrate_2::Migrate), ]; migrations.insert(LatestBackupMigration(BACKUP_MIGRATIONS.last().unwrap().1.version())); migrations.insert(std::collections::HashMap::from(BACKUP_MIGRATIONS)); diff --git a/sdk/tests/client/addresses.rs b/sdk/tests/client/addresses.rs index b78982ed4d..26c0e2e752 100644 --- a/sdk/tests/client/addresses.rs +++ b/sdk/tests/client/addresses.rs @@ -1,6 +1,7 @@ // Copyright 2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use crypto::keys::bip39::Mnemonic; #[cfg(feature = "stronghold")] use iota_sdk::client::secret::stronghold::StrongholdSecretManager; use iota_sdk::{ @@ -199,7 +200,7 @@ async fn address_generation() { .unwrap(); stronghold_secret_manager - .store_mnemonic(address.mnemonic.clone()) + .store_mnemonic(Mnemonic::from(address.mnemonic.as_str())) .await .unwrap(); diff --git a/sdk/tests/client/common/mod.rs b/sdk/tests/client/common/mod.rs index a260681c8e..5a0ff66a34 100644 --- a/sdk/tests/client/common/mod.rs +++ b/sdk/tests/client/common/mod.rs @@ -3,6 +3,7 @@ mod constants; +use crypto::keys::bip39::Mnemonic; use iota_sdk::client::{ api::GetAddressesOptions, constants::SHIMMER_COIN_TYPE, node_api::indexer::query_parameters::QueryParameter, request_funds_from_faucet, secret::SecretManager, Client, Result, @@ -23,7 +24,9 @@ pub async fn setup_client_with_node_health_ignored() -> Client { /// Create a client with `DEFAULT_DEVNET_NODE_URL` and a random mnemonic, request funds from the faucet to the first /// address and wait until they arrived. -pub async fn create_client_and_secret_manager_with_funds(mnemonic: Option) -> Result<(Client, SecretManager)> { +pub async fn create_client_and_secret_manager_with_funds( + mnemonic: Option, +) -> Result<(Client, SecretManager)> { let client = Client::builder().with_node(NODE_LOCAL)?.finish().await?; let secret_manager = SecretManager::try_from_mnemonic(mnemonic.unwrap_or(Client::generate_mnemonic().unwrap()))?; diff --git a/sdk/tests/client/input_signing_data.rs b/sdk/tests/client/input_signing_data.rs index 255b368a2e..e4696e4dfc 100644 --- a/sdk/tests/client/input_signing_data.rs +++ b/sdk/tests/client/input_signing_data.rs @@ -3,10 +3,10 @@ use std::str::FromStr; -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ - constants::{HD_WALLET_TYPE, SHIMMER_COIN_TYPE}, + constants::SHIMMER_COIN_TYPE, secret::types::{InputSigningData, InputSigningDataDto}, }, types::block::{ @@ -21,7 +21,7 @@ use iota_sdk::{ fn input_signing_data_conversion() { let protocol_parameters = protocol_parameters(); - let bip32_chain = [HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0]; + let bip44_chain = Bip44::new().with_coin_type(SHIMMER_COIN_TYPE); let output = BasicOutput::build_with_amount(1_000_000) .add_unlock_condition(AddressUnlockCondition::new( @@ -43,17 +43,17 @@ fn input_signing_data_conversion() { 0, 0, ), - chain: Some(Chain::from_u32_hardened(bip32_chain)), + chain: Some(bip44_chain), }; let input_signing_data_dto = InputSigningDataDto::from(&input_signing_data); - assert_eq!(input_signing_data_dto.chain.as_deref(), Some(&bip32_chain[..])); + assert_eq!(input_signing_data_dto.chain.as_ref(), Some(&bip44_chain)); let restored_input_signing_data = InputSigningData::try_from_dto(input_signing_data_dto.clone(), protocol_parameters.token_supply()).unwrap(); assert_eq!(input_signing_data, restored_input_signing_data); - let input_signing_data_dto_str = r#"{"output":{"type":3,"amount":"1000000","unlockConditions":[{"type":0,"address":{"type":0,"pubKeyHash":"0x7ffec9e1233204d9c6dce6812b1539ee96af691ca2e4d9065daa85907d33e5d3"}}]},"outputMetadata":{"blockId":"0xedf5f572c58ddf4b4f9567d82bf96689cc68b730df796d822b4b9fb643f5efda","transactionId":"0xbce525324af12eda02bf7927e92cea3a8e8322d0f41966271443e6c3b245a440","outputIndex":0,"isSpent":false,"milestoneIndexBooked":0,"milestoneTimestampBooked":0,"ledgerIndex":0},"chain":[44,4219,0,0,0]}"#; + let input_signing_data_dto_str = r#"{"output":{"type":3,"amount":"1000000","unlockConditions":[{"type":0,"address":{"type":0,"pubKeyHash":"0x7ffec9e1233204d9c6dce6812b1539ee96af691ca2e4d9065daa85907d33e5d3"}}]},"outputMetadata":{"blockId":"0xedf5f572c58ddf4b4f9567d82bf96689cc68b730df796d822b4b9fb643f5efda","transactionId":"0xbce525324af12eda02bf7927e92cea3a8e8322d0f41966271443e6c3b245a440","outputIndex":0,"isSpent":false,"milestoneIndexBooked":0,"milestoneTimestampBooked":0,"ledgerIndex":0},"chain":{"coinType":4219,"account":0,"change":0,"addressIndex":0}}"#; assert_eq!( serde_json::to_string(&input_signing_data_dto).unwrap(), input_signing_data_dto_str @@ -61,13 +61,10 @@ fn input_signing_data_conversion() { let restored_input_signing_data_dto = serde_json::from_str::(input_signing_data_dto_str).unwrap(); - assert_eq!(restored_input_signing_data_dto.chain.as_deref(), Some(&bip32_chain[..])); + assert_eq!(restored_input_signing_data_dto.chain.as_ref(), Some(&bip44_chain)); let restored_input_signing_data = InputSigningData::try_from_dto(restored_input_signing_data_dto, protocol_parameters.token_supply()).unwrap(); assert!(restored_input_signing_data.output.is_basic()); - assert_eq!( - restored_input_signing_data.chain, - Some(Chain::from_u32_hardened(bip32_chain)) - ); + assert_eq!(restored_input_signing_data.chain, Some(bip44_chain)); } diff --git a/sdk/tests/client/mnemonic.rs b/sdk/tests/client/mnemonic.rs index ddc6d9f996..8fa86d69ab 100644 --- a/sdk/tests/client/mnemonic.rs +++ b/sdk/tests/client/mnemonic.rs @@ -1,21 +1,15 @@ // Copyright 2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use crypto::keys::bip39::Mnemonic; use iota_sdk::client::{Client, Result}; #[tokio::test] async fn mnemonic() -> Result<()> { let mnemonic = Client::generate_mnemonic()?; - assert!(Client::mnemonic_to_hex_seed(&mnemonic).is_ok()); - assert!(Client::mnemonic_to_hex_seed("until fire hat mountain zoo grocery real deny advance change marble taste goat ivory wheat bubble panic banner tattoo client ticket action race rocket").is_ok()); - assert!(Client::mnemonic_to_hex_seed("fire until hat mountain zoo grocery real deny advance change marble taste goat ivory wheat bubble panic banner tattoo client ticket action race rocket").is_err()); - assert!(Client::mnemonic_to_hex_seed("invalid mnemonic").is_err()); - // mnemonic with space at the beginning or end should return the same as without - let mnemonic = "until fire hat mountain zoo grocery real deny advance change marble taste goat ivory wheat bubble panic banner tattoo client ticket action race rocket"; - let mnemonic_with_spaces = " until fire hat mountain zoo grocery real deny advance change marble taste goat ivory wheat bubble panic banner tattoo client ticket action race rocket "; - assert_eq!( - Client::mnemonic_to_hex_seed(mnemonic).unwrap(), - Client::mnemonic_to_hex_seed(mnemonic_with_spaces).unwrap() - ); + assert!(Client::mnemonic_to_hex_seed(mnemonic).is_ok()); + assert!(Client::mnemonic_to_hex_seed(Mnemonic::from("until fire hat mountain zoo grocery real deny advance change marble taste goat ivory wheat bubble panic banner tattoo client ticket action race rocket".to_owned())).is_ok()); + assert!(Client::mnemonic_to_hex_seed(Mnemonic::from("fire until hat mountain zoo grocery real deny advance change marble taste goat ivory wheat bubble panic banner tattoo client ticket action race rocket".to_owned())).is_err()); + assert!(Client::mnemonic_to_hex_seed(Mnemonic::from("invalid mnemonic".to_owned())).is_err()); Ok(()) } diff --git a/sdk/tests/client/mod.rs b/sdk/tests/client/mod.rs index 36a237abbe..047be11c11 100644 --- a/sdk/tests/client/mod.rs +++ b/sdk/tests/client/mod.rs @@ -22,7 +22,7 @@ use std::{ str::FromStr, }; -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use iota_sdk::{ client::secret::types::InputSigningData, types::block::{ @@ -71,7 +71,7 @@ enum Build<'a> { Option<(&'a str, u64)>, Option, Option<(&'a str, u32)>, - Option, + Option, ), Nft( u64, @@ -82,7 +82,7 @@ enum Build<'a> { Option<&'a str>, Option<(&'a str, u64)>, Option<(&'a str, u32)>, - Option, + Option, ), Alias( u64, @@ -93,7 +93,7 @@ enum Build<'a> { Option>, Option<&'a str>, Option<&'a str>, - Option, + Option, ), Foundry(u64, AliasId, u32, SimpleTokenScheme, Option>), } @@ -236,7 +236,7 @@ fn build_foundry_output( builder.finish_output(TOKEN_SUPPLY).unwrap() } -fn build_output_inner(build: Build) -> (Output, Option) { +fn build_output_inner(build: Build) -> (Output, Option) { match build { Build::Basic(amount, bech32_address, native_tokens, bech32_sender, sdruc, timelock, expiration, chain) => ( build_basic_output( diff --git a/sdk/tests/client/secret_manager.rs b/sdk/tests/client/secret_manager.rs index ab67f5cc16..1147e2bd7d 100644 --- a/sdk/tests/client/secret_manager.rs +++ b/sdk/tests/client/secret_manager.rs @@ -34,8 +34,8 @@ async fn stronghold_secret_manager_dto() -> Result<()> { iota_stronghold::engine::snapshot::try_set_encrypt_work_factor(0).unwrap(); let dto = r#"{"stronghold": {"password": "some_hopefully_secure_password", "snapshotPath": "snapshot_test_dir/test.stronghold"}}"#; - let mnemonic = String::from( - "acoustic trophy damage hint search taste love bicycle foster cradle brown govern endless depend situate athlete pudding blame question genius transfer van random vast", + let mnemonic = crypto::keys::bip39::Mnemonic::from( + "acoustic trophy damage hint search taste love bicycle foster cradle brown govern endless depend situate athlete pudding blame question genius transfer van random vast".to_owned(), ); let mut secret_manager: SecretManager = dto.parse()?; diff --git a/sdk/tests/client/signing/alias.rs b/sdk/tests/client/signing/alias.rs index 023d4616d4..c3d00e4cc9 100644 --- a/sdk/tests/client/signing/alias.rs +++ b/sdk/tests/client/signing/alias.rs @@ -3,14 +3,14 @@ use std::str::FromStr; -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ api::{ transaction::validate_transaction_payload_length, verify_semantic, GetAddressesOptions, PreparedTransactionData, }, - constants::{HD_WALLET_TYPE, SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP}, + constants::{SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP}, secret::{SecretManage, SecretManager}, Client, Result, }, @@ -67,7 +67,7 @@ async fn sign_alias_state_transition() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), )]); let outputs = build_outputs([Alias( @@ -158,7 +158,7 @@ async fn sign_alias_governance_transition() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 1])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE).with_address_index(1)), )]); let outputs = build_outputs([Alias( @@ -251,7 +251,7 @@ async fn alias_reference_unlocks() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), ), Basic( 1_000_000, diff --git a/sdk/tests/client/signing/basic.rs b/sdk/tests/client/signing/basic.rs index a741ff8272..14718408fb 100644 --- a/sdk/tests/client/signing/basic.rs +++ b/sdk/tests/client/signing/basic.rs @@ -1,14 +1,14 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ api::{ transaction::validate_transaction_payload_length, verify_semantic, GetAddressesOptions, PreparedTransactionData, }, - constants::{HD_WALLET_TYPE, SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP}, + constants::{SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP}, secret::{SecretManage, SecretManager}, Client, Result, }, @@ -51,7 +51,7 @@ async fn single_ed25519_unlock() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), )]); let outputs = build_outputs([Basic( @@ -62,7 +62,7 @@ async fn single_ed25519_unlock() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), )]); let essence = TransactionEssence::Regular( @@ -132,7 +132,7 @@ async fn ed25519_reference_unlocks() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), ), Basic( 1_000_000, @@ -142,7 +142,7 @@ async fn ed25519_reference_unlocks() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), ), Basic( 1_000_000, @@ -152,7 +152,7 @@ async fn ed25519_reference_unlocks() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), ), ]); @@ -164,7 +164,7 @@ async fn ed25519_reference_unlocks() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), )]); let essence = TransactionEssence::Regular( @@ -254,7 +254,7 @@ async fn two_signature_unlocks() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), ), Basic( 1_000_000, @@ -264,7 +264,7 @@ async fn two_signature_unlocks() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 1])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE).with_address_index(1)), ), ]); @@ -276,7 +276,7 @@ async fn two_signature_unlocks() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), )]); let essence = TransactionEssence::Regular( diff --git a/sdk/tests/client/signing/mod.rs b/sdk/tests/client/signing/mod.rs index a8516461cd..4298561969 100644 --- a/sdk/tests/client/signing/mod.rs +++ b/sdk/tests/client/signing/mod.rs @@ -7,14 +7,14 @@ mod nft; use std::str::FromStr; -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ api::{ input_selection::InputSelection, transaction::validate_transaction_payload_length, verify_semantic, GetAddressesOptions, PreparedTransactionData, }, - constants::{HD_WALLET_TYPE, SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP}, + constants::{SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP}, secret::{SecretManage, SecretManager}, Result, }, @@ -93,7 +93,7 @@ async fn all_combined() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), ), Basic( 1_000_000, @@ -163,7 +163,7 @@ async fn all_combined() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), ), Basic( 1_000_000, @@ -173,7 +173,7 @@ async fn all_combined() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 1])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE).with_address_index(1)), ), Basic( 1_000_000, @@ -183,7 +183,7 @@ async fn all_combined() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 2])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE).with_address_index(2)), ), Basic( 1_000_000, @@ -193,7 +193,7 @@ async fn all_combined() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 2])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE).with_address_index(2)), ), Nft( 1_000_000, @@ -204,7 +204,7 @@ async fn all_combined() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), ), Nft( 1_000_000, @@ -246,7 +246,7 @@ async fn all_combined() -> Result<()> { None, None, Some((&nft_3_bech32_address.to_string(), 150)), - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), ), Nft( 1_000_000, diff --git a/sdk/tests/client/signing/nft.rs b/sdk/tests/client/signing/nft.rs index b835b3786f..4204a8e725 100644 --- a/sdk/tests/client/signing/nft.rs +++ b/sdk/tests/client/signing/nft.rs @@ -3,14 +3,14 @@ use std::str::FromStr; -use crypto::keys::slip10::Chain; +use crypto::keys::bip44::Bip44; use iota_sdk::{ client::{ api::{ transaction::validate_transaction_payload_length, verify_semantic, GetAddressesOptions, PreparedTransactionData, }, - constants::{HD_WALLET_TYPE, SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP}, + constants::{SHIMMER_COIN_TYPE, SHIMMER_TESTNET_BECH32_HRP}, secret::{SecretManage, SecretManager}, Client, Result, }, @@ -61,7 +61,7 @@ async fn nft_reference_unlocks() -> Result<()> { None, None, None, - Some(Chain::from_u32_hardened([HD_WALLET_TYPE, SHIMMER_COIN_TYPE, 0, 0, 0])), + Some(Bip44::new().with_coin_type(SHIMMER_COIN_TYPE)), ), Basic( 1_000_000, diff --git a/sdk/tests/types/ed25519_signature.rs b/sdk/tests/types/ed25519_signature.rs index ecd7acaa99..1e51e876dc 100644 --- a/sdk/tests/types/ed25519_signature.rs +++ b/sdk/tests/types/ed25519_signature.rs @@ -16,7 +16,7 @@ fn kind() { fn packed_len() { let pub_key_bytes: [u8; 32] = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); let sig_bytes: [u8; 64] = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); - let sig = Ed25519Signature::new(pub_key_bytes, sig_bytes); + let sig = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); assert_eq!(sig.packed_len(), 32 + 64); assert_eq!(sig.pack_to_vec().len(), 32 + 64); @@ -26,7 +26,7 @@ fn packed_len() { fn pack_unpack_valid() { let pub_key_bytes: [u8; 32] = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); let sig_bytes: [u8; 64] = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); - let sig = Ed25519Signature::new(pub_key_bytes, sig_bytes); + let sig = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); let sig_packed = sig.pack_to_vec(); assert_eq!(sig, PackableExt::unpack_verified(sig_packed.as_slice(), &()).unwrap()); diff --git a/sdk/tests/types/milestone_payload.rs b/sdk/tests/types/milestone_payload.rs index a6ed0f024a..9b9fbaa45e 100644 --- a/sdk/tests/types/milestone_payload.rs +++ b/sdk/tests/types/milestone_payload.rs @@ -10,8 +10,8 @@ use iota_sdk::types::block::{ milestone::{rand_merkle_root, rand_milestone_id, rand_milestone_index}, number::rand_number, parents::rand_parents, + signature::rand_signature, }, - signature::{Ed25519Signature, Signature}, Error, }; use packable::{bounded::TryIntoBoundedU8Error, PackableExt}; @@ -37,7 +37,7 @@ fn new_valid() { MilestoneOptions::from_vec(vec![]).unwrap(), ) .unwrap(), - vec![Signature::from(Ed25519Signature::new([0; 32], [0; 64]))] + [rand_signature()] ) .is_ok() ); @@ -59,7 +59,7 @@ fn new_invalid_no_signature() { MilestoneOptions::from_vec(vec![]).unwrap(), ) .unwrap(), - vec![] + [] ), Err(Error::MilestoneInvalidSignatureCount(TryIntoBoundedU8Error::Invalid(0))) )); @@ -81,7 +81,7 @@ fn new_invalid_too_many_signatures() { MilestoneOptions::from_vec(vec![]).unwrap(), ) .unwrap(), - vec![Signature::from(Ed25519Signature::new([0; 32], [0; 64])); 300] + vec![rand_signature(); 300] ), Err(Error::MilestoneInvalidSignatureCount(TryIntoBoundedU8Error::Truncated( 300 @@ -104,10 +104,7 @@ fn packed_len() { MilestoneOptions::from_vec(vec![]).unwrap(), ) .unwrap(), - vec![ - Signature::from(Ed25519Signature::new([0; 32], [0; 64])), - Signature::from(Ed25519Signature::new([1; 32], [1; 64])), - ], + [rand_signature(), rand_signature()], ) .unwrap(); @@ -131,7 +128,7 @@ fn pack_unpack_valid() { MilestoneOptions::from_vec(vec![]).unwrap(), ) .unwrap(), - vec![Signature::from(Ed25519Signature::new([0; 32], [0; 64]))], + [rand_signature()], ) .unwrap(); @@ -158,7 +155,7 @@ fn getters() { MilestoneOptions::from_vec(vec![]).unwrap(), ) .unwrap(); - let signatures = vec![Signature::from(Ed25519Signature::new([0; 32], [0; 64]))]; + let signatures = [rand_signature()]; let milestone = MilestonePayload::new(essence.clone(), signatures.clone()).unwrap(); assert_eq!(essence, *milestone.essence()); diff --git a/sdk/tests/types/payload.rs b/sdk/tests/types/payload.rs index 2a0aed88be..eed0c6db16 100644 --- a/sdk/tests/types/payload.rs +++ b/sdk/tests/types/payload.rs @@ -53,10 +53,10 @@ fn transaction() { .unwrap(), ); - let pub_key_bytes: [u8; 32] = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); - let sig_bytes: [u8; 64] = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); - let signature = Ed25519Signature::new(pub_key_bytes, sig_bytes); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::Ed25519(signature))); + let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); + let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); + let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); + let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); let ref_unlock = Unlock::Reference(ReferenceUnlock::new(0).unwrap()); let unlocks = Unlocks::new(vec![sig_unlock, ref_unlock]).unwrap(); @@ -77,6 +77,8 @@ fn transaction() { #[test] fn milestone() { let protocol_parameters = protocol_parameters(); + let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); + let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); let payload: Payload = MilestonePayload::new( MilestoneEssence::new( MilestoneIndex(0), @@ -90,7 +92,9 @@ fn milestone() { MilestoneOptions::from_vec(vec![]).unwrap(), ) .unwrap(), - vec![Signature::from(Ed25519Signature::new([0; 32], [0; 64]))], + vec![Signature::from( + Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(), + )], ) .unwrap() .into(); diff --git a/sdk/tests/types/signature_unlock.rs b/sdk/tests/types/signature_unlock.rs index f0d6094bf6..20d3466f17 100644 --- a/sdk/tests/types/signature_unlock.rs +++ b/sdk/tests/types/signature_unlock.rs @@ -1,12 +1,7 @@ // Copyright 2020-2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::types::block::{ - rand::bytes::{rand_bytes, rand_bytes_array}, - signature::{Ed25519Signature, Signature}, - unlock::SignatureUnlock, - Error, -}; +use iota_sdk::types::block::{rand::signature::rand_signature, unlock::SignatureUnlock, Error}; use packable::{error::UnpackError, PackableExt}; #[test] @@ -16,37 +11,12 @@ fn unlock_kind() { #[test] fn signature_kind() { - assert_eq!( - SignatureUnlock::from(Signature::from(Ed25519Signature::new( - rand_bytes_array(), - rand_bytes(64).try_into().unwrap() - ))) - .kind(), - 0 - ); -} - -#[test] -fn from_ed25519() { - let public_key_bytes = rand_bytes_array(); - let signature_bytes: [u8; 64] = rand_bytes(64).try_into().unwrap(); - let signature = SignatureUnlock::from(Signature::from(Ed25519Signature::new( - public_key_bytes, - signature_bytes, - ))); - - assert!(matches!(signature.signature(), Signature::Ed25519(signature) - if signature.public_key() == &public_key_bytes - && signature.signature() == signature_bytes.as_ref() - )); + assert_eq!(SignatureUnlock::from(rand_signature()).kind(), 0); } #[test] fn packed_len() { - let signature = SignatureUnlock::from(Signature::from(Ed25519Signature::new( - rand_bytes_array(), - rand_bytes(64).try_into().unwrap(), - ))); + let signature = SignatureUnlock::from(rand_signature()); assert_eq!(signature.packed_len(), 1 + 32 + 64); assert_eq!(signature.pack_to_vec().len(), 1 + 32 + 64); @@ -54,10 +24,7 @@ fn packed_len() { #[test] fn pack_unpack_valid_ed25519() { - let signature_1 = SignatureUnlock::from(Signature::from(Ed25519Signature::new( - rand_bytes_array(), - rand_bytes(64).try_into().unwrap(), - ))); + let signature_1 = SignatureUnlock::from(rand_signature()); let signature_bytes = signature_1.pack_to_vec(); let signature_2 = SignatureUnlock::unpack_verified(signature_bytes.as_slice(), &()).unwrap(); diff --git a/sdk/tests/types/transaction_payload.rs b/sdk/tests/types/transaction_payload.rs index d1c4809cc8..5436a38119 100644 --- a/sdk/tests/types/transaction_payload.rs +++ b/sdk/tests/types/transaction_payload.rs @@ -50,10 +50,10 @@ fn builder_no_essence_too_few_unlocks() { ); // Construct a list with a single unlock, whereas we have 2 tx inputs. - let pub_key_bytes: [u8; 32] = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); - let sig_bytes: [u8; 64] = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); - let signature = Ed25519Signature::new(pub_key_bytes, sig_bytes); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::Ed25519(signature))); + let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); + let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); + let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); + let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); let unlocks = Unlocks::new([sig_unlock]).unwrap(); assert!(matches!( @@ -87,10 +87,10 @@ fn builder_no_essence_too_many_unlocks() { ); // Construct a list of two unlocks, whereas we only have 1 tx input. - let pub_key_bytes: [u8; 32] = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); - let sig_bytes: [u8; 64] = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); - let signature = Ed25519Signature::new(pub_key_bytes, sig_bytes); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::Ed25519(signature))); + let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); + let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); + let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); + let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); let ref_unlock = Unlock::Reference(ReferenceUnlock::new(0).unwrap()); let unlocks = Unlocks::new([sig_unlock, ref_unlock]).unwrap(); @@ -127,10 +127,10 @@ fn pack_unpack_valid() { ); // Construct a list of two unlocks, whereas we only have 1 tx input. - let pub_key_bytes: [u8; 32] = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); - let sig_bytes: [u8; 64] = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); - let signature = Ed25519Signature::new(pub_key_bytes, sig_bytes); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::Ed25519(signature))); + let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); + let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); + let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); + let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); let ref_unlock = Unlock::Reference(ReferenceUnlock::new(0).unwrap()); let unlocks = Unlocks::new([sig_unlock, ref_unlock]).unwrap(); @@ -169,10 +169,10 @@ fn getters() { ); // Construct a list of two unlocks, whereas we only have 1 tx input. - let pub_key_bytes: [u8; 32] = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); - let sig_bytes: [u8; 64] = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); - let signature = Ed25519Signature::new(pub_key_bytes, sig_bytes); - let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::Ed25519(signature))); + let pub_key_bytes = prefix_hex::decode(ED25519_PUBLIC_KEY).unwrap(); + let sig_bytes = prefix_hex::decode(ED25519_SIGNATURE).unwrap(); + let signature = Ed25519Signature::try_from_bytes(pub_key_bytes, sig_bytes).unwrap(); + let sig_unlock = Unlock::Signature(SignatureUnlock::from(Signature::from(signature))); let ref_unlock = Unlock::Reference(ReferenceUnlock::new(0).unwrap()); let unlocks = Unlocks::new([sig_unlock, ref_unlock]).unwrap(); diff --git a/sdk/tests/types/unlocks.rs b/sdk/tests/types/unlocks.rs index 2f636f5e1b..27761d2cfc 100644 --- a/sdk/tests/types/unlocks.rs +++ b/sdk/tests/types/unlocks.rs @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use iota_sdk::types::block::{ - rand::bytes::{rand_bytes, rand_bytes_array}, - signature::{Ed25519Signature, Signature}, + rand::signature::rand_signature, unlock::{ReferenceUnlock, SignatureUnlock, Unlock, Unlocks}, Error, }; @@ -11,14 +10,7 @@ use packable::bounded::TryIntoBoundedU16Error; #[test] fn kind() { - assert_eq!( - Unlock::from(SignatureUnlock::from(Signature::from(Ed25519Signature::new( - rand_bytes_array(), - rand_bytes(64).try_into().unwrap(), - )))) - .kind(), - 0 - ); + assert_eq!(Unlock::from(SignatureUnlock::from(rand_signature())).kind(), 0); assert_eq!(Unlock::from(ReferenceUnlock::new(0).unwrap()).kind(), 1); } @@ -34,7 +26,7 @@ fn new_invalid_first_reference() { fn new_invalid_self_reference() { assert!(matches!( Unlocks::new([ - SignatureUnlock::from(Signature::from(Ed25519Signature::new([0; 32], [0; 64]))).into(), + SignatureUnlock::from(rand_signature()).into(), ReferenceUnlock::new(1).unwrap().into() ]), Err(Error::InvalidUnlockReference(1)), @@ -45,9 +37,9 @@ fn new_invalid_self_reference() { fn new_invalid_future_reference() { assert!(matches!( Unlocks::new([ - SignatureUnlock::from(Signature::from(Ed25519Signature::new([0; 32], [0; 64]))).into(), + SignatureUnlock::from(rand_signature()).into(), ReferenceUnlock::new(2).unwrap().into(), - SignatureUnlock::from(Signature::from(Ed25519Signature::new([1; 32], [1; 64]))).into(), + SignatureUnlock::from(rand_signature()).into(), ]), Err(Error::InvalidUnlockReference(1)), )); @@ -57,7 +49,7 @@ fn new_invalid_future_reference() { fn new_invalid_reference_reference() { assert!(matches!( Unlocks::new([ - SignatureUnlock::from(Signature::from(Ed25519Signature::new([0; 32], [0; 64]))).into(), + SignatureUnlock::from(rand_signature()).into(), ReferenceUnlock::new(0).unwrap().into(), ReferenceUnlock::new(1).unwrap().into() ]), @@ -67,15 +59,16 @@ fn new_invalid_reference_reference() { #[test] fn new_invalid_duplicate_signature() { + let dup = rand_signature(); assert!(matches!( Unlocks::new([ - SignatureUnlock::from(Signature::from(Ed25519Signature::new([0; 32], [0; 64]))).into(), + SignatureUnlock::from(rand_signature()).into(), ReferenceUnlock::new(0).unwrap().into(), ReferenceUnlock::new(0).unwrap().into(), - SignatureUnlock::from(Signature::from(Ed25519Signature::new([1; 32], [1; 64]))).into(), - SignatureUnlock::from(Signature::from(Ed25519Signature::new([2; 32], [2; 64]))).into(), - SignatureUnlock::from(Signature::from(Ed25519Signature::new([2; 32], [2; 64]))).into(), - SignatureUnlock::from(Signature::from(Ed25519Signature::new([3; 32], [3; 64]))).into(), + SignatureUnlock::from(rand_signature()).into(), + SignatureUnlock::from(dup.clone()).into(), + SignatureUnlock::from(dup).into(), + SignatureUnlock::from(rand_signature()).into(), ReferenceUnlock::new(3).unwrap().into() ]), Err(Error::DuplicateSignatureUnlock(5)), @@ -94,20 +87,20 @@ fn new_invalid_too_many_blocks() { fn new_valid() { assert!( Unlocks::new([ - SignatureUnlock::from(Signature::from(Ed25519Signature::new([0; 32], [0; 64]))).into(), + SignatureUnlock::from(rand_signature()).into(), ReferenceUnlock::new(0).unwrap().into(), ReferenceUnlock::new(0).unwrap().into(), - SignatureUnlock::from(Signature::from(Ed25519Signature::new([1; 32], [1; 64]))).into(), - SignatureUnlock::from(Signature::from(Ed25519Signature::new([2; 32], [2; 64]))).into(), - SignatureUnlock::from(Signature::from(Ed25519Signature::new([3; 32], [3; 64]))).into(), + SignatureUnlock::from(rand_signature()).into(), + SignatureUnlock::from(rand_signature()).into(), + SignatureUnlock::from(rand_signature()).into(), ReferenceUnlock::new(3).unwrap().into(), ReferenceUnlock::new(4).unwrap().into(), ReferenceUnlock::new(3).unwrap().into(), ReferenceUnlock::new(4).unwrap().into(), ReferenceUnlock::new(5).unwrap().into(), - SignatureUnlock::from(Signature::from(Ed25519Signature::new([4; 32], [4; 64]))).into(), + SignatureUnlock::from(rand_signature()).into(), ReferenceUnlock::new(11).unwrap().into(), - SignatureUnlock::from(Signature::from(Ed25519Signature::new([5; 32], [5; 64]))).into(), + SignatureUnlock::from(rand_signature()).into(), ]) .is_ok() ); @@ -116,7 +109,7 @@ fn new_valid() { #[test] fn get_none() { assert!( - Unlocks::new([SignatureUnlock::from(Signature::from(Ed25519Signature::new([0; 32], [0; 64]))).into()]) + Unlocks::new([SignatureUnlock::from(rand_signature()).into()]) .unwrap() .get(42) .is_none() @@ -125,18 +118,14 @@ fn get_none() { #[test] fn get_signature() { - let signature = Unlock::from(SignatureUnlock::from(Signature::from(Ed25519Signature::new( - [0; 32], [0; 64], - )))); + let signature = Unlock::from(SignatureUnlock::from(rand_signature())); assert_eq!(Unlocks::new([signature.clone()]).unwrap().get(0), Some(&signature)); } #[test] fn get_signature_through_reference() { - let signature = Unlock::from(SignatureUnlock::from(Signature::from(Ed25519Signature::new( - [0; 32], [0; 64], - )))); + let signature = Unlock::from(SignatureUnlock::from(rand_signature())); assert_eq!( Unlocks::new([signature.clone(), ReferenceUnlock::new(0).unwrap().into()]) diff --git a/sdk/tests/wallet/accounts.rs b/sdk/tests/wallet/accounts.rs index 32f38e7a36..a6ee7baac5 100644 --- a/sdk/tests/wallet/accounts.rs +++ b/sdk/tests/wallet/accounts.rs @@ -164,14 +164,14 @@ async fn account_creation_stronghold() -> Result<()> { setup(storage_path)?; let client_options = ClientOptions::new().with_node("http://localhost:14265")?; - let mnemonic = "inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak"; + let mnemonic = crypto::keys::bip39::Mnemonic::from("inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak".to_owned()); // Create directory before, because stronghold would panic otherwise std::fs::create_dir_all(storage_path).ok(); let stronghold_secret_manager = StrongholdSecretManager::builder() .password("some_hopefully_secure_password".to_owned()) .build("test-storage/account_creation_stronghold/test.stronghold")?; - stronghold_secret_manager.store_mnemonic(mnemonic.to_string()).await?; + stronghold_secret_manager.store_mnemonic(mnemonic).await?; let secret_manager = SecretManager::Stronghold(stronghold_secret_manager); #[allow(unused_mut)] diff --git a/sdk/tests/wallet/backup_restore.rs b/sdk/tests/wallet/backup_restore.rs index 832145040b..00d236b84f 100644 --- a/sdk/tests/wallet/backup_restore.rs +++ b/sdk/tests/wallet/backup_restore.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; +use crypto::keys::bip39::Mnemonic; use iota_sdk::{ client::{ constants::{IOTA_COIN_TYPE, SHIMMER_COIN_TYPE}, @@ -33,7 +34,7 @@ async fn backup_and_restore() -> Result<()> { .password(stronghold_password.clone()) .build("test-storage/backup_and_restore/1.stronghold")?; - stronghold.store_mnemonic("inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak".to_string()).await.unwrap(); + stronghold.store_mnemonic(Mnemonic::from("inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak".to_string())).await.unwrap(); let wallet = Wallet::builder() .with_secret_manager(SecretManager::Stronghold(stronghold)) @@ -209,7 +210,7 @@ async fn backup_and_restore_different_coin_type() -> Result<()> { .password(stronghold_password.clone()) .build("test-storage/backup_and_restore_different_coin_type/1.stronghold")?; - stronghold.store_mnemonic("inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak".to_string()).await.unwrap(); + stronghold.store_mnemonic(Mnemonic::from("inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak".to_string())).await.unwrap(); let wallet = Wallet::builder() .with_secret_manager(SecretManager::Stronghold(stronghold)) @@ -293,7 +294,7 @@ async fn backup_and_restore_same_coin_type() -> Result<()> { .password(stronghold_password.clone()) .build("test-storage/backup_and_restore_same_coin_type/1.stronghold")?; - stronghold.store_mnemonic("inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak".to_string()).await.unwrap(); + stronghold.store_mnemonic(Mnemonic::from("inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak".to_string())).await.unwrap(); let wallet = Wallet::builder() .with_secret_manager(SecretManager::Stronghold(stronghold)) @@ -375,7 +376,7 @@ async fn backup_and_restore_different_coin_type_dont_ignore() -> Result<()> { .password(stronghold_password.clone()) .build("test-storage/backup_and_restore_different_coin_type_dont_ignore/1.stronghold")?; - stronghold.store_mnemonic("inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak".to_string()).await.unwrap(); + stronghold.store_mnemonic(Mnemonic::from("inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak".to_string())).await.unwrap(); let wallet = Wallet::builder() .with_secret_manager(SecretManager::Stronghold(stronghold)) @@ -462,7 +463,7 @@ async fn backup_and_restore_bech32_hrp_mismatch() -> Result<()> { .password(stronghold_password.clone()) .build("test-storage/backup_and_restore_bech32_hrp_mismatch/1.stronghold")?; - stronghold.store_mnemonic("inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak".to_string()).await.unwrap(); + stronghold.store_mnemonic(Mnemonic::from("inhale gorilla deny three celery song category owner lottery rent author wealth penalty crawl hobby obtain glad warm early rain clutch slab august bleak".to_string())).await.unwrap(); let wallet = Wallet::builder() .with_secret_manager(SecretManager::Stronghold(stronghold)) diff --git a/sdk/tests/wallet/common/mod.rs b/sdk/tests/wallet/common/mod.rs index 157108f013..f93d5850ab 100644 --- a/sdk/tests/wallet/common/mod.rs +++ b/sdk/tests/wallet/common/mod.rs @@ -3,6 +3,7 @@ mod constants; +use crypto::keys::bip39::Mnemonic; use iota_sdk::{ client::{ constants::SHIMMER_COIN_TYPE, @@ -28,7 +29,7 @@ pub use self::constants::*; /// /// An Wallet #[allow(dead_code, unused_variables)] -pub(crate) async fn make_wallet(storage_path: &str, mnemonic: Option, node: Option<&str>) -> Result { +pub(crate) async fn make_wallet(storage_path: &str, mnemonic: Option, node: Option<&str>) -> Result { let client_options = ClientOptions::new().with_node(node.unwrap_or(NODE_LOCAL))?; let secret_manager = MnemonicSecretManager::try_from_mnemonic(mnemonic.unwrap_or(Client::generate_mnemonic().unwrap()))?; diff --git a/sdk/tests/wallet/core.rs b/sdk/tests/wallet/core.rs index 48b74fb9fd..2e50bf4765 100644 --- a/sdk/tests/wallet/core.rs +++ b/sdk/tests/wallet/core.rs @@ -1,6 +1,7 @@ // Copyright 2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use crypto::keys::bip39::Mnemonic; #[cfg(feature = "stronghold")] use iota_sdk::client::secret::stronghold::StrongholdSecretManager; #[cfg(feature = "storage")] @@ -83,7 +84,7 @@ async fn changed_coin_type() -> Result<()> { let storage_path = "test-storage/changed_coin_type"; setup(storage_path)?; - let mnemonic = DEFAULT_MNEMONIC.to_owned(); + let mnemonic = Mnemonic::from(DEFAULT_MNEMONIC.to_owned()); let wallet = make_wallet(storage_path, Some(mnemonic.clone()), None).await?; let _account = wallet.create_account().with_alias("Alice").finish().await?; @@ -129,7 +130,7 @@ async fn shimmer_coin_type() -> Result<()> { let storage_path = "test-storage/shimmer_coin_type"; setup(storage_path)?; - let wallet = make_wallet(storage_path, Some(DEFAULT_MNEMONIC.to_owned()), None).await?; + let wallet = make_wallet(storage_path, Some(Mnemonic::from(DEFAULT_MNEMONIC.to_owned())), None).await?; let account = wallet.create_account().finish().await?; // Creating a new account with providing a coin type will use the Shimmer coin type with shimmer testnet bech32 hrp @@ -211,7 +212,9 @@ async fn wallet_address_generation() -> Result<()> { let secret_manager = StrongholdSecretManager::builder() .password("some_hopefully_secure_password".to_owned()) .build("test-storage/wallet_address_generation/test.stronghold")?; - secret_manager.store_mnemonic(DEFAULT_MNEMONIC.to_string()).await?; + secret_manager + .store_mnemonic(Mnemonic::from(DEFAULT_MNEMONIC.to_string())) + .await?; let client_options = ClientOptions::new().with_node(NODE_LOCAL)?; #[allow(unused_mut)]