diff --git a/Cargo.lock b/Cargo.lock index 844a3f46e..110b6182e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bech32" version = "0.9.1" @@ -63,7 +69,7 @@ version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", @@ -76,7 +82,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.18", + "syn 2.0.79", "which", ] @@ -129,6 +135,42 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bls-dash-sys" +version = "1.2.5" +source = "git+https://github.com/dashpay/bls-signatures?tag=1.3.3#4e070243aed142bc458472f8807ab77527dd879a" +dependencies = [ + "bindgen", + "cc", + "glob", +] + +[[package]] +name = "bls-signatures" +version = "1.2.5" +source = "git+https://github.com/dashpay/bls-signatures?tag=1.3.3#4e070243aed142bc458472f8807ab77527dd879a" +dependencies = [ + "bls-dash-sys", + "hex", + "rand", + "serde", +] + [[package]] name = "bumpalo" version = "3.13.0" @@ -183,6 +225,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "core2" version = "0.3.3" @@ -192,6 +240,52 @@ dependencies = [ "memchr", ] +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "dash-fuzz" version = "0.0.1" @@ -205,7 +299,7 @@ dependencies = [ [[package]] name = "dashcore" -version = "0.30.0" +version = "0.32.0" dependencies = [ "anyhow", "base64-compat", @@ -214,17 +308,23 @@ dependencies = [ "bincode 2.0.0-rc.3", "bip39", "bitcoinconsensus", + "bitflags 2.6.0", + "bls-signatures", "core2", "dashcore-private", "dashcore_hashes", + "ed25519-dalek", "hex", "hex_lit", + "lazy_static", "rustversion", "secp256k1", "serde", "serde_derive", "serde_json", + "serde_repr", "serde_test", + "strum", ] [[package]] @@ -233,7 +333,7 @@ version = "0.1.0" [[package]] name = "dashcore_hashes" -version = "0.12.0" +version = "0.14.0" dependencies = [ "core2", "dashcore-private", @@ -247,18 +347,79 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dyn-clone" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435217f1b2d06f00513185fb991cd426271d9e4c3a9d9b25b919b8e5a03b282d" +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -282,6 +443,12 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hex" version = "0.4.3" @@ -331,9 +498,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -343,9 +510,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.146" +version = "0.2.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +checksum = "f0b21006cd1874ae9e650973c565615676dc4a274c965bb0a73796dac838ce4f" [[package]] name = "libloading" @@ -406,6 +573,16 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -419,23 +596,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b69d39aab54d069e7f2fe8cb970493e7834601ca2d8c65fd7bbd183578080d1" dependencies = [ "proc-macro2", - "syn 2.0.18", + "syn 2.0.79", ] [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.28" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -608,7 +785,7 @@ checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.79", ] [[package]] @@ -633,6 +810,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "serde_test" version = "1.0.164" @@ -642,12 +830,70 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.79", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -661,9 +907,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -685,6 +931,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.9" @@ -700,6 +952,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "virtue" version = "0.0.13" @@ -733,7 +991,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -767,7 +1025,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -844,3 +1102,9 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/dash/Cargo.toml b/dash/Cargo.toml index 0c48dce24..9c8d13d68 100644 --- a/dash/Cargo.toml +++ b/dash/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dashcore" -version = "0.30.0" +version = "0.32.0" authors = [ "Samuel Westrich ", "Anton Suprunchuk ", @@ -28,6 +28,8 @@ secp-lowmemory = ["secp256k1/lowmemory"] secp-recovery = ["secp256k1/recovery"] signer = ["secp-recovery", "rand"] core-block-hash-use-x11 = ["dashcore_hashes/x11"] +bls = ["bls-signatures"] +eddsa = ["ed25519-dalek"] # At least one of std, no-std must be enabled. # @@ -58,6 +60,12 @@ hex_lit = "0.1.1" anyhow = { version= "1.0" } hex = { version= "0.4" } bincode = { version= "2.0.0-rc.3", optional = true } +bitflags = "2.6.0" +bls-signatures = { git = "https://github.com/dashpay/bls-signatures", tag = "1.3.3", optional = true } +serde_repr = "0.1.19" +strum = { version = "0.26", features = ["derive"] } +lazy_static = "1.5.0" +ed25519-dalek = { version = "2.1", features = ["rand_core"], optional = true } [dev-dependencies] serde_json = "1.0.96" @@ -65,7 +73,7 @@ serde_test = "1.0.19" serde_derive = "1.0.103" secp256k1 = { features = [ "recovery", "rand", "hashes" ], version="0.30.0" } bip39 = "2.0.0" -bincode = "1.3.1" +bincode_test = {package = "bincode", version= "1.3.3" } [[example]] name = "bip32" diff --git a/dash/examples/taproot-psbt.rs b/dash/examples/taproot-psbt.rs index a7a2b3b9a..ca88d6f0b 100644 --- a/dash/examples/taproot-psbt.rs +++ b/dash/examples/taproot-psbt.rs @@ -738,7 +738,7 @@ fn sign_psbt_taproot( hash_ty: TapSighashType, secp: &Secp256k1, ) { - let keypair = secp256k1::KeyPair::from_seckey_slice(secp, secret_key.as_ref()).unwrap(); + let keypair = secp256k1::Keypair::from_seckey_slice(secp, secret_key.as_ref()).unwrap(); let keypair = match leaf_hash { None => keypair.tap_tweak(secp, psbt_input.tap_merkle_root).to_inner(), Some(_) => keypair, // no tweak for script spend diff --git a/dash/src/base58.rs b/dash/src/base58.rs index e437ab92b..38dd87fbe 100644 --- a/dash/src/base58.rs +++ b/dash/src/base58.rs @@ -51,6 +51,16 @@ pub enum Error { /// Hex decoding error // TODO: Remove this as part of crate-smashing, there should not be any key related errors in this module Hex(hex::Error), + + /// bls signatures related error + #[cfg(feature = "bls-signatures")] + BLSError(String), + /// edwards 25519 related error + #[cfg(feature = "ed25519-dalek")] + Ed25519Dalek(String), + + /// A feature is not supported + NotSupported(String), } impl fmt::Display for Error { @@ -70,6 +80,11 @@ impl fmt::Display for Error { Error::TooShort(_) => write!(f, "base58ck data not even long enough for a checksum"), Error::Secp256k1(ref e) => fmt::Display::fmt(&e, f), Error::Hex(ref e) => write!(f, "Hexadecimal decoding error: {}", e), + #[cfg(feature = "bls-signatures")] + Error::BLSError(ref e) => write!(f, "BLS error: {}", e), + #[cfg(feature = "ed25519-dalek")] + Error::Ed25519Dalek(ref e) => write!(f, "Ed25519-Dalek error: {}", e), + Error::NotSupported(ref e) => write!(f, "Not supported: {}", e), } } } @@ -397,6 +412,11 @@ impl From for Error { key::Error::InvalidKeyPrefix(_) => Error::Secp256k1(secp256k1::Error::InvalidPublicKey), key::Error::Hex(e) => Error::Hex(e), key::Error::InvalidHexLength(size) => Error::InvalidLength(size), + #[cfg(feature = "bls-signatures")] + key::Error::BLSError(e) => Error::BLSError(e), + #[cfg(feature = "ed25519-dalek")] + key::Error::Ed25519Dalek(e) => Error::Ed25519Dalek(e), + key::Error::NotSupported(e) => Error::NotSupported(e), } } } diff --git a/dash/src/bip32.rs b/dash/src/bip32.rs index 3f64a010a..1ed8b66c9 100644 --- a/dash/src/bip32.rs +++ b/dash/src/bip32.rs @@ -28,7 +28,7 @@ use core::str::FromStr; #[cfg(feature = "std")] use std::error; -use hashes::{Hash, HashEngine, Hmac, HmacEngine, hex, sha512}; +use hashes::{Hash, HashEngine, Hmac, HmacEngine, hex as hashesHex, sha512}; use internals::impl_array_newtype; use secp256k1::{self, Secp256k1, XOnlyPublicKey}; #[cfg(feature = "serde")] @@ -36,6 +36,12 @@ use serde; use crate::base58; use crate::crypto::key::{self, Keypair, PrivateKey, PublicKey}; +use crate::dip9::{ + DASH_BIP44_PATH_MAINNET, DASH_BIP44_PATH_TESTNET, IDENTITY_AUTHENTICATION_PATH_MAINNET, + IDENTITY_AUTHENTICATION_PATH_TESTNET, IDENTITY_INVITATION_PATH_MAINNET, + IDENTITY_INVITATION_PATH_TESTNET, IDENTITY_REGISTRATION_PATH_MAINNET, + IDENTITY_REGISTRATION_PATH_TESTNET, IDENTITY_TOPUP_PATH_MAINNET, IDENTITY_TOPUP_PATH_TESTNET, +}; use crate::hash_types::XpubIdentifier; use crate::internal_macros::impl_bytes_newtype; use crate::io::Write; @@ -126,6 +132,18 @@ pub enum ChildNumber { /// Key index, within [0, 2^31 - 1] index: u32, }, + + /// Non-hardened key + Normal256 { + /// Key index, within [0, 2^256 - 1] + index: [u8; 32], + }, + + /// Hardened key + Hardened256 { + /// Key index, within [0, 2^256 - 1] + index: [u8; 32], + }, } impl ChildNumber { @@ -153,6 +171,14 @@ impl ChildNumber { } } + /// Create a non-hardened `ChildNumber` from a 256-bit index. + pub fn from_normal_idx_256(index: [u8; 32]) -> ChildNumber { ChildNumber::Normal256 { index } } + + /// Create a hardened `ChildNumber` from a 256-bit index. + pub fn from_hardened_idx_256(index: [u8; 32]) -> ChildNumber { + ChildNumber::Hardened256 { index } + } + /// Returns `true` if the child number is a [`Normal`] value. /// /// [`Normal`]: #variant.Normal @@ -165,6 +191,18 @@ impl ChildNumber { match self { ChildNumber::Hardened { .. } => true, ChildNumber::Normal { .. } => false, + ChildNumber::Normal256 { .. } => false, + ChildNumber::Hardened256 { .. } => true, + } + } + + /// Returns `true` if the child number is a 256 bit value. + pub fn is_256_bits(&self) -> bool { + match self { + ChildNumber::Hardened { .. } => false, + ChildNumber::Normal { .. } => false, + ChildNumber::Normal256 { .. } => true, + ChildNumber::Hardened256 { .. } => true, } } @@ -173,6 +211,40 @@ impl ChildNumber { match self { ChildNumber::Normal { index: idx } => ChildNumber::from_normal_idx(idx + 1), ChildNumber::Hardened { index: idx } => ChildNumber::from_hardened_idx(idx + 1), + ChildNumber::Normal256 { mut index } => { + // Increment the 256-bit big-endian number represented by index + let mut carry = 1u8; + for byte in index.iter_mut().rev() { + let (new_byte, overflow) = byte.overflowing_add(carry); + *byte = new_byte; + carry = if overflow { 1 } else { 0 }; + if carry == 0 { + break; + } + } + if carry != 0 { + // Overflow occurred + return Err(Error::InvalidChildNumber(0)); // Or define a suitable error + } + Ok(ChildNumber::Normal256 { index }) + } + ChildNumber::Hardened256 { mut index } => { + // Increment the 256-bit big-endian number represented by index + let mut carry = 1u8; + for byte in index.iter_mut().rev() { + let (new_byte, overflow) = byte.overflowing_add(carry); + *byte = new_byte; + carry = if overflow { 1 } else { 0 }; + if carry == 0 { + break; + } + } + if carry != 0 { + // Overflow occurred + return Err(Error::InvalidChildNumber(0)); // Or define a suitable error + } + Ok(ChildNumber::Hardened256 { index }) + } } } } @@ -192,6 +264,8 @@ impl From for u32 { match cnum { ChildNumber::Normal { index } => index, ChildNumber::Hardened { index } => index | (1 << 31), + ChildNumber::Normal256 { .. } => u32::MAX, + ChildNumber::Hardened256 { .. } => u32::MAX, } } } @@ -205,6 +279,12 @@ impl fmt::Display for ChildNumber { f.write_str(if alt { "h" } else { "'" }) } ChildNumber::Normal { index } => fmt::Display::fmt(&index, f), + ChildNumber::Hardened256 { index } => { + write!(f, "0x{}{}", hex::encode(index), if f.alternate() { "h" } else { "'" }) + } + ChildNumber::Normal256 { index } => { + write!(f, "0x{}", hex::encode(index)) + } } } } @@ -213,14 +293,32 @@ impl FromStr for ChildNumber { type Err = Error; fn from_str(inp: &str) -> Result { - let is_hardened = inp.chars().last().map_or(false, |l| l == '\'' || l == 'h'); - Ok(if is_hardened { - ChildNumber::from_hardened_idx( - inp[0..inp.len() - 1].parse().map_err(|_| Error::InvalidChildNumberFormat)?, - )? + let is_hardened = inp.ends_with('\'') || inp.ends_with('h'); + let index_str = if is_hardened { &inp[..inp.len() - 1] } else { inp }; + + if index_str.starts_with("0x") || index_str.starts_with("0X") { + // Parse as a 256-bit hex number + let hex_str = &index_str[2..]; + let hex_bytes = hex::decode(hex_str).map_err(|_| Error::InvalidChildNumberFormat)?; + if hex_bytes.len() != 32 { + return Err(Error::InvalidChildNumberFormat); + } + let mut index_bytes = [0u8; 32]; + index_bytes[32 - hex_bytes.len()..].copy_from_slice(&hex_bytes); + if is_hardened { + Ok(ChildNumber::Hardened256 { index: index_bytes }) + } else { + Ok(ChildNumber::Normal256 { index: index_bytes }) + } } else { - ChildNumber::from_normal_idx(inp.parse().map_err(|_| Error::InvalidChildNumberFormat)?)? - }) + // Parse as a u32 number + let index = index_str.parse::().map_err(|_| Error::InvalidChildNumberFormat)?; + if is_hardened { + ChildNumber::from_hardened_idx(index) + } else { + ChildNumber::from_normal_idx(index) + } + } } } @@ -255,6 +353,121 @@ pub trait IntoDerivationPath { #[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct DerivationPath(Vec); +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +#[repr(u32)] +pub enum KeyDerivationType { + ECDSA = 0, + BLS = 1, +} + +impl Into for KeyDerivationType { + fn into(self) -> u32 { + match self { + KeyDerivationType::ECDSA => 0, + KeyDerivationType::BLS => 1, + } + } +} + +impl DerivationPath { + pub fn bip_44_account(network: Network, account: u32) -> Self { + let mut root_derivation_path: DerivationPath = match network { + Network::Dash => DASH_BIP44_PATH_MAINNET, + _ => DASH_BIP44_PATH_TESTNET, + } + .into(); + root_derivation_path.0.extend(&[ChildNumber::Hardened { index: account }]); + root_derivation_path + } + pub fn bip_44_payment_path( + network: Network, + account: u32, + change: bool, + address_index: u32, + ) -> Self { + let mut root_derivation_path: DerivationPath = match network { + Network::Dash => DASH_BIP44_PATH_MAINNET, + _ => DASH_BIP44_PATH_TESTNET, + } + .into(); + root_derivation_path.0.extend(&[ + ChildNumber::Hardened { index: account }, + ChildNumber::Normal { index: change.into() }, + ChildNumber::Normal { index: address_index }, + ]); + root_derivation_path + } + + pub fn identity_registration_path(network: Network, index: u32) -> Self { + let mut root_derivation_path: DerivationPath = match network { + Network::Dash => IDENTITY_REGISTRATION_PATH_MAINNET, + _ => IDENTITY_REGISTRATION_PATH_TESTNET, + } + .into(); + root_derivation_path.0.extend(&[ChildNumber::Normal { index }]); + root_derivation_path + } + + pub fn identity_top_up_path(network: Network, index: u32) -> Self { + let mut root_derivation_path: DerivationPath = match network { + Network::Dash => IDENTITY_TOPUP_PATH_MAINNET, + _ => IDENTITY_TOPUP_PATH_TESTNET, + } + .into(); + root_derivation_path.0.extend(&[ChildNumber::Normal { index }]); + root_derivation_path + } + + pub fn identity_invitation_path(network: Network, index: u32) -> Self { + let mut root_derivation_path: DerivationPath = match network { + Network::Dash => IDENTITY_INVITATION_PATH_MAINNET, + _ => IDENTITY_INVITATION_PATH_TESTNET, + } + .into(); + root_derivation_path.0.extend(&[ChildNumber::Normal { index }]); + root_derivation_path + } + + pub fn identity_authentication_path( + network: Network, + key_type: KeyDerivationType, + identity_index: u32, + key_index: u32, + ) -> Self { + let mut root_derivation_path: DerivationPath = match network { + Network::Dash => IDENTITY_AUTHENTICATION_PATH_MAINNET, + _ => IDENTITY_AUTHENTICATION_PATH_TESTNET, + } + .into(); + root_derivation_path.0.extend(&[ + ChildNumber::Hardened { index: key_type.into() }, + ChildNumber::Hardened { index: identity_index }, + ChildNumber::Hardened { index: key_index }, + ]); + root_derivation_path + } + + pub fn derive_priv_ecdsa_for_master_seed( + &self, + seed: &[u8], + network: Network, + ) -> Result { + let secp = Secp256k1::new(); + let sk = ExtendedPrivKey::new_master(network, seed)?; + sk.derive_priv(&secp, &self) + } + + pub fn derive_pub_ecdsa_for_master_seed( + &self, + seed: &[u8], + network: Network, + ) -> Result { + let secp = Secp256k1::new(); + let sk = self.derive_priv_ecdsa_for_master_seed(seed, network)?; + Ok(ExtendedPubKey::from_priv(&secp, &sk)) + } +} + #[cfg(feature = "serde")] crate::serde_utils::serde_string_impl!(DerivationPath, "a BIP-32 derivation path"); @@ -465,9 +678,17 @@ pub enum Error { /// Base58 encoding error Base58(base58::Error), /// Hexadecimal decoding error - Hex(hex::Error), + Hex(hashesHex::Error), /// `PublicKey` hex should be 66 or 130 digits long. InvalidPublicKeyHexLength(usize), + /// bls signatures related error + #[cfg(feature = "bls-signatures")] + BLSError(String), + /// edwards 25519 related error + #[cfg(feature = "ed25519-dalek")] + Ed25519Dalek(String), + /// Something is not supported based on active features + NotSupported(String), } impl fmt::Display for Error { @@ -492,6 +713,11 @@ impl fmt::Display for Error { Error::InvalidPublicKeyHexLength(got) => { write!(f, "PublicKey hex should be 66 or 130 digits long, got: {}", got) } + #[cfg(feature = "bls-signatures")] + Error::BLSError(ref msg) => write!(f, "BLS signature error: {}", msg), + #[cfg(feature = "ed25519-dalek")] + Error::Ed25519Dalek(ref msg) => write!(f, "Ed25519 error: {}", msg), + Error::NotSupported(ref msg) => write!(f, "Not supported: {}", msg), } } } @@ -511,6 +737,11 @@ impl From for Error { key::Error::InvalidKeyPrefix(_) => Error::Secp256k1(secp256k1::Error::InvalidPublicKey), key::Error::Hex(e) => Error::Hex(e), key::Error::InvalidHexLength(got) => Error::InvalidPublicKeyHexLength(got), + #[cfg(feature = "bls-signatures")] + key::Error::BLSError(e) => Error::BLSError(e), + #[cfg(feature = "ed25519-dalek")] + key::Error::Ed25519Dalek(e) => Error::Ed25519Dalek(e), + key::Error::NotSupported(e) => Error::NotSupported(e), } } } @@ -567,7 +798,6 @@ impl ExtendedPrivKey { Ok(sk) } - /// Private->Private child key derivation /// Private->Private child key derivation pub fn ckd_priv( &self, @@ -576,20 +806,33 @@ impl ExtendedPrivKey { ) -> Result { let mut hmac_engine: HmacEngine = HmacEngine::new(&self.chain_code[..]); match i { - ChildNumber::Normal { .. } => { + ChildNumber::Normal { index } => { // Non-hardened key: compute public data and use that hmac_engine.input( &secp256k1::PublicKey::from_secret_key(secp, &self.private_key).serialize()[..], ); + hmac_engine.input(&index.to_be_bytes()); } - ChildNumber::Hardened { .. } => { + ChildNumber::Hardened { index } => { // Hardened key: use only secret data to prevent public derivation hmac_engine.input(&[0u8]); hmac_engine.input(&self.private_key[..]); + hmac_engine.input(&(index | (1 << 31)).to_be_bytes()); + } + ChildNumber::Normal256 { index } => { + // Non-hardened key with 256-bit index + hmac_engine.input( + &secp256k1::PublicKey::from_secret_key(secp, &self.private_key).serialize()[..], + ); + hmac_engine.input(&index); + } + ChildNumber::Hardened256 { index } => { + // Hardened key with 256-bit index + hmac_engine.input(&[0u8]); + hmac_engine.input(&self.private_key[..]); + hmac_engine.input(&index); } } - - hmac_engine.input(&u32::from(i).to_be_bytes()); let hmac_result: Hmac = Hmac::from_engine(hmac_engine); let sk = secp256k1::SecretKey::from_slice(&hmac_result[..32]) .expect("statistically impossible to hit"); @@ -606,8 +849,26 @@ impl ExtendedPrivKey { }) } + /// Extended private key binary encoding according to BIP 32 + fn encode(&self) -> Vec { + if self.child_number.is_256_bits() { + self.encode_256().to_vec() + } else { + self.encode_32().to_vec() + } + } + + /// Decoding extended private key from binary data according to BIP 32 + fn decode(data: &[u8]) -> Result { + match data.len() { + 78 => Self::decode_32(data), + 107 => Self::decode_256(data), + _ => Err(Error::WrongExtendedKeyLength(data.len())), + } + } + /// Decoding extended private key from binary data according to BIP 32 - pub fn decode(data: &[u8]) -> Result { + fn decode_32(data: &[u8]) -> Result { if data.len() != 78 { return Err(Error::WrongExtendedKeyLength(data.len())); } @@ -634,7 +895,7 @@ impl ExtendedPrivKey { } /// Extended private key binary encoding according to BIP 32 - pub fn encode(&self) -> [u8; 78] { + fn encode_32(&self) -> [u8; 78] { let mut ret = [0; 78]; ret[0..4].copy_from_slice( &match self.network { @@ -651,6 +912,91 @@ impl ExtendedPrivKey { ret } + /// Decoding extended private key from binary data with 256-bit child numbers + fn decode_256(data: &[u8]) -> Result { + if data.len() != 107 { + return Err(Error::WrongExtendedKeyLength(data.len())); + } + + let version = &data[0..4]; + let network = match version { + [0x0Eu8, 0xEC, 0xF0, 0x2E] => Network::Dash, // Mainnet private + [0x0Eu8, 0xED, 0x27, 0x74] => Network::Testnet, // Testnet private + [b0, b1, b2, b3] => return Err(Error::UnknownVersion([*b0, *b1, *b2, *b3])), + _ => unreachable!("length checked above"), + }; + + let depth = data[4]; + let parent_fingerprint = data[5..9].try_into().expect("4 bytes for fingerprint"); + + let hardening_byte = data[9]; + let is_hardened = match hardening_byte { + 0x00 => false, + _ => true, + }; + + let child_number_bytes = data[10..42].try_into().expect("32 bytes for child number"); + let child_number = if is_hardened { + ChildNumber::Hardened256 { index: child_number_bytes } + } else { + ChildNumber::Normal256 { index: child_number_bytes } + }; + + let chain_code = data[42..74].try_into().expect("32 bytes for chain code"); + let private_key = secp256k1::SecretKey::from_slice(&data[75..107])?; + + Ok(ExtendedPrivKey { + network, + depth, + parent_fingerprint, + child_number, + private_key, + chain_code: ChainCode(chain_code), + }) + } + + /// Encoding extended private key to binary data with 256-bit child numbers + fn encode_256(&self) -> [u8; 107] { + let mut ret = [0u8; 107]; + + // Version bytes + let version: [u8; 4] = match self.network { + Network::Dash => [0x0E, 0xEC, 0xF0, 0x2E], + Network::Testnet | Network::Devnet | Network::Regtest => [0x0E, 0xED, 0x27, 0x74], + }; + ret[0..4].copy_from_slice(&version); + + // Depth + ret[4] = self.depth; + + // Parent fingerprint + ret[5..9].copy_from_slice(&self.parent_fingerprint[..]); + + // Hardening byte + let hardening_byte = match self.child_number { + ChildNumber::Normal256 { .. } => 0x00, + ChildNumber::Hardened256 { .. } => 0x01, + _ => panic!("Invalid child number for 256-bit format"), + }; + ret[9] = hardening_byte; + + // Child number (32 bytes) + let child_number_bytes = match self.child_number { + ChildNumber::Normal256 { index } | ChildNumber::Hardened256 { index } => index, + _ => panic!("Invalid child number for 256-bit format"), + }; + ret[10..42].copy_from_slice(&child_number_bytes); + + // Chain code (32 bytes) + ret[42..74].copy_from_slice(&self.chain_code[..]); + + // Key data (33 bytes) + ret[74] = 0x00; // Padding for private key + ret[75..107].copy_from_slice(&self.private_key[..]); + + ret + } + /// Returns the HASH160 of the public key belonging to the xpriv pub fn identifier(&self, secp: &Secp256k1) -> XpubIdentifier { ExtendedPubKey::from_priv(secp, self).identifier() @@ -716,7 +1062,8 @@ impl ExtendedPubKey { i: ChildNumber, ) -> Result<(secp256k1::SecretKey, ChainCode), Error> { match i { - ChildNumber::Hardened { .. } => Err(Error::CannotDeriveFromHardenedKey), + ChildNumber::Hardened { .. } | ChildNumber::Hardened256 { .. } => + Err(Error::CannotDeriveFromHardenedKey), ChildNumber::Normal { index: n } => { let mut hmac_engine: HmacEngine = HmacEngine::new(&self.chain_code[..]); @@ -729,6 +1076,23 @@ impl ExtendedPubKey { let chain_code = ChainCode::from_hmac(hmac_result); Ok((private_key, chain_code)) } + ChildNumber::Normal256 { index: idx } => { + // UInt256 mode (index >= 2^32) + let mut hmac_engine: HmacEngine = + HmacEngine::new(&self.chain_code[..]); + + // HMAC Input: serP(Kpar) || ser256(i) + hmac_engine.input(&self.public_key.serialize()[..]); + hmac_engine.input(&idx); + + let hmac_result: Hmac = Hmac::from_engine(hmac_engine); + + // IL must be less than n (order of the curve) + let private_key = secp256k1::SecretKey::from_slice(&hmac_result[..32])?; + let chain_code = ChainCode::from_hmac(hmac_result); + + Ok((private_key, chain_code)) + } } } @@ -751,8 +1115,26 @@ impl ExtendedPubKey { }) } - /// Decoding extended public key from binary data according to BIP 32 + /// Extended public key binary encoding according to BIP 32 and DIP-14 + pub fn encode(&self) -> Vec { + if self.child_number.is_256_bits() { + self.encode_256().to_vec() + } else { + self.encode_32().to_vec() + } + } + + /// Decoding extended public key from binary data according to BIP 32 and DIP-14 pub fn decode(data: &[u8]) -> Result { + match data.len() { + 78 => Self::decode_32(data), + 107 => Self::decode_256(data), + _ => Err(Error::WrongExtendedKeyLength(data.len())), + } + } + + /// Decoding extended public key from binary data according to BIP 32 + pub fn decode_32(data: &[u8]) -> Result { if data.len() != 78 { return Err(Error::WrongExtendedKeyLength(data.len())); } @@ -779,7 +1161,7 @@ impl ExtendedPubKey { } /// Extended public key binary encoding according to BIP 32 - pub fn encode(&self) -> [u8; 78] { + pub fn encode_32(&self) -> [u8; 78] { let mut ret = [0; 78]; ret[0..4].copy_from_slice( &match self.network { @@ -795,6 +1177,92 @@ impl ExtendedPubKey { ret } + /// Encoding extended public key to binary data with 256-bit child numbers + fn encode_256(&self) -> [u8; 107] { + let mut ret = [0u8; 107]; + + // Version bytes + let version: [u8; 4] = match self.network { + Network::Dash => [0x0E, 0xEC, 0xEF, 0xC5], + Network::Testnet | Network::Devnet | Network::Regtest => [0x0E, 0xED, 0x27, 0x0B], + }; + ret[0..4].copy_from_slice(&version); + + // Depth + ret[4] = self.depth; + + // Parent fingerprint + ret[5..9].copy_from_slice(&self.parent_fingerprint[..]); + + // Hardening byte + let hardening_byte = match self.child_number { + ChildNumber::Normal256 { .. } => 0x00, + ChildNumber::Hardened256 { .. } => 0x01, + _ => panic!("Invalid child number for 256-bit format"), + }; + ret[9] = hardening_byte; + + // Child number (32 bytes) + let child_number_bytes = match self.child_number { + ChildNumber::Normal256 { index } | ChildNumber::Hardened256 { index } => index, + _ => panic!("Invalid child number for 256-bit format"), + }; + ret[10..42].copy_from_slice(&child_number_bytes); + + // Chain code (32 bytes) + ret[42..74].copy_from_slice(&self.chain_code[..]); + + // Key data (33 bytes) + ret[74..107].copy_from_slice(&self.public_key.serialize()[..]); + + ret + } + + /// Decoding extended public key from binary data with 256-bit child numbers + fn decode_256(data: &[u8]) -> Result { + if data.len() != 107 { + return Err(Error::WrongExtendedKeyLength(data.len())); + } + + let version = &data[0..4]; + let network = match version { + [0x0Eu8, 0xEC, 0xEF, 0xC5] => Network::Dash, // Mainnet public + [0x0Eu8, 0xED, 0x27, 0x0B] => Network::Testnet, // Testnet public + [b0, b1, b2, b3] => return Err(Error::UnknownVersion([*b0, *b1, *b2, *b3])), + _ => unreachable!("length checked above"), + }; + + let depth = data[4]; + let parent_fingerprint = data[5..9].try_into().expect("4 bytes for fingerprint"); + + let hardening_byte = data[9]; + let is_hardened = match hardening_byte { + 0x00 => false, + _ => true, + }; + + let child_number_bytes = data[10..42].try_into().expect("32 bytes for child number"); + let child_number = if is_hardened { + ChildNumber::Hardened256 { index: child_number_bytes } + } else { + ChildNumber::Normal256 { index: child_number_bytes } + }; + + let chain_code = data[42..74].try_into().expect("32 bytes for chain code"); + + // Key data (33 bytes) + let public_key = secp256k1::PublicKey::from_slice(&data[74..107])?; + + Ok(ExtendedPubKey { + network, + depth, + parent_fingerprint, + child_number, + public_key, + chain_code: ChainCode(chain_code), + }) + } + /// Returns the HASH160 of the chaincode pub fn identifier(&self) -> XpubIdentifier { let mut engine = XpubIdentifier::engine(); @@ -819,11 +1287,6 @@ impl FromStr for ExtendedPrivKey { fn from_str(inp: &str) -> Result { let data = base58::decode_check(inp)?; - - if data.len() != 78 { - return Err(base58::Error::InvalidLength(data.len()).into()); - } - ExtendedPrivKey::decode(&data) } } @@ -839,11 +1302,6 @@ impl FromStr for ExtendedPubKey { fn from_str(inp: &str) -> Result { let data = base58::decode_check(inp)?; - - if data.len() != 78 { - return Err(base58::Error::InvalidLength(data.len()).into()); - } - ExtendedPubKey::decode(&data) } } @@ -962,12 +1420,12 @@ mod tests { for &num in path.0.iter() { sk = sk.ckd_priv(secp, num).unwrap(); match num { - Normal { .. } => { + Normal { .. } | ChildNumber::Normal256 { .. } => { let pk2 = pk.ckd_pub(secp, num).unwrap(); pk = ExtendedPubKey::from_priv(secp, &sk); assert_eq!(pk, pk2); } - Hardened { .. } => { + Hardened { .. } | ChildNumber::Hardened256 { .. } => { assert_eq!(pk.ckd_pub(secp, num), Err(Error::CannotDeriveFromHardenedKey)); pk = ExtendedPubKey::from_priv(secp, &sk); } @@ -1266,4 +1724,221 @@ mod tests { let xpriv_str = "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fENZ3QzxW"; ExtendedPrivKey::from_str(xpriv_str).unwrap(); } + + #[test] + fn test_dashpay_vector_1() { + let secp = Secp256k1::new(); + let seed = Vec::from_hex("b16d3782e714da7c55a397d5f19104cfed7ffa8036ac514509bbb50807f8ac598eeb26f0797bd8cc221a6cbff2168d90a5e9ee025a5bd977977b9eccd97894bb").unwrap(); + + // Test Vector 1: Non-hardened / Hardened path example + test_path( + &secp, + Network::Testnet, + &seed, + "m/0x775d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3b/\ + 0xf537439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89a6'/\ + 0x4c4592ca670c983fc43397dfd21a6f427fac9b4ac53cb4dcdc6522ec51e81e79/0" + .parse() + .unwrap(), + "tprv8iNr6Z8PgAHmYSgMKGbq42kMVAAQmwmzm5iTJdUXoxLf25zG3GeRCvnEdC6HKTHkU59nZkfjvcGk9VW2YHsFQMwsZrQLyNrGx9c37kgb368", + "tpubDF4tEyAdpXySRui9CvGRTSQU4BgLwGxuLPKEb9WqEE93raF2ffU1PRQ6oJHCgZ7dArzcMj9iKG8s8EFA1DdwgzWAXs61uFuRE1bQi8kAmLy", + ); + } + + #[test] + fn test_dashpay_vector_2() { + let secp = Secp256k1::new(); + let seed = Vec::from_hex("b16d3782e714da7c55a397d5f19104cfed7ffa8036ac514509bbb50807f8ac598eeb26f0797bd8cc221a6cbff2168d90a5e9ee025a5bd977977b9eccd97894bb").unwrap(); + + // Test Vector 2: Multiple hardened derivations with final non-hardened index + test_path( + &secp, + Network::Testnet, + &seed, + "m/9'/5'/15'/0'/\ + 0x555d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3a'/\ + 0xa137439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89b5'/0" + .parse() + .unwrap(), + "tprv8p9LqE2tA2b94gc3ciRNA525WVkFvzkcC9qjpKEcGaTqjb9u2pwTXj41KkZTj3c1a6fJUpyXRfcB4dimsYsLMjQjsTJwi5Ukx6tJ5BpmYpx", + "tpubDLqNye58JQGox9dqWN5xZUgC5XGC6KwWmTSX6qGugrGEa5QffDm3iDfsVtX7qyXuWoQsXA6YCSuckKshyjnwiGGoYWHonAv2X98HTU613UH", + ); + } + + #[test] + fn test_dashpay_vector_3() { + let secp = Secp256k1::new(); + let seed = Vec::from_hex("b16d3782e714da7c55a397d5f19104cfed7ffa8036ac514509bbb50807f8ac598eeb26f0797bd8cc221a6cbff2168d90a5e9ee025a5bd977977b9eccd97894bb").unwrap(); + + // Test Vector 3: Non-hardened derivation + test_path( + &secp, + Network::Testnet, + &seed, + "m/0x775d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3b".parse().unwrap(), + "dpts1vgMVEs9mmv1YLwURCeoTn9CFMZ8JMVhyZuxQSKttNSETR3zydMFHMKTTNDQPf6nnupCCtcNnSu3nKZXAJhaguyoJWD4Ju5PE6PSkBqAKWci7HLz37qmFmZZU6GMkLvNLtST2iV8NmqqbX37c45", + "dptp1C5gGd8NzvAke5WNKyRfpDRyvV2UZ3jjrZVZU77qk9yZemMGSdZpkWp7y6wt3FzvFxAHSW8VMCaC1p6Ny5EqWuRm2sjvZLUUFMMwXhmW6eS69qjX958RYBH5R8bUCGZkCfUyQ8UVWcx9katkrRr", + ); + } + + #[test] + fn test_dashpay_vector_4() { + let secp = Secp256k1::new(); + let seed = Vec::from_hex("b16d3782e714da7c55a397d5f19104cfed7ffa8036ac514509bbb50807f8ac598eeb26f0797bd8cc221a6cbff2168d90a5e9ee025a5bd977977b9eccd97894bb").unwrap(); + + // Test Vector 4: Hardened path with complex indices + test_path( + &secp, + Network::Testnet, + &seed, + "m/0x775d3854c910b7dee436869c4724bed2fe0784e198b8a39f02bbb49d8ebcfc3b/\ + 0xf537439f36d04a15474ff7423e4b904a14373fafb37a41db74c84f1dbb5c89a6'" + .parse() + .unwrap(), + "dpts1vwRsaPMQfqwp59ELpx5UeuYtdaMCJyGTwiGtr8zgf6qWPMWnhPpg8R73hwR1xLibbdKVdh17zfwMxFEMxZzBKUgPwvuosUGDKW4ayZjs3AQB9EGRcVpDoFT8V6nkcc6KzksmZxvmDcd3MqiPEu", + "dptp1CLkexeadp6guoi8Fbiwq6CLZm3hT1DJLwHsxWvwYSeAhjenFhcQ9HumZSftfZEr4dyQjFD7gkM5bSn6Aj7F1Jve8KTn4JsMEaj9dFyJkYs4Ga5HSUqeajxGVmzaY1pEioDmvUtZL3J1NCDCmzQ", + ); + } + + const HEX_SEED: &str = "368a0691faa33e646108368dc0d9a1f9c440e0c5393ffd2def5ed2200d6019d0f7094c24503d6d1209756ac5bfd87731b0e816736de8f5f44ea636d2b830b3bf"; + + #[test] + fn test_bip_44_account_path() { + let path = DerivationPath::bip_44_account(Network::Dash, 0); + assert_eq!(path.to_string(), "m/44'/5'/0'"); + } + + #[test] + fn test_bip_44_payment_path() { + let path = DerivationPath::bip_44_payment_path(Network::Dash, 0, true, 0); + assert_eq!(path.to_string(), "m/44'/5'/0'/1/0"); + + let path = DerivationPath::bip_44_payment_path(Network::Testnet, 1, false, 42); + assert_eq!(path.to_string(), "m/44'/1'/1'/0/42"); + } + + #[test] + fn test_identity_registration_path() { + let path = DerivationPath::identity_registration_path(Network::Dash, 10); + assert_eq!(path.to_string(), "m/9'/5'/5'/1'/10"); + } + + #[test] + fn test_identity_top_up_path() { + let path = DerivationPath::identity_top_up_path(Network::Testnet, 2); + assert_eq!(path.to_string(), "m/9'/1'/5'/2'/2"); + } + + #[test] + fn test_identity_invitation_path() { + let path = DerivationPath::identity_invitation_path(Network::Dash, 15); + assert_eq!(path.to_string(), "m/9'/5'/5'/3'/15"); + } + + #[test] + fn test_identity_authentication_path() { + let path = DerivationPath::identity_authentication_path( + Network::Dash, + KeyDerivationType::ECDSA, + 1, + 2, + ); + assert_eq!(path.to_string(), "m/9'/5'/5'/0'/0'/1'/2'"); + + let path = DerivationPath::identity_authentication_path( + Network::Testnet, + KeyDerivationType::BLS, + 2, + 3, + ); + assert_eq!(path.to_string(), "m/9'/1'/5'/0'/1'/2'/3'"); + } + + #[test] + fn test_derive_priv_ecdsa_for_master_seed() { + let path = DerivationPath::bip_44_account(Network::Dash, 0); + let sk = path + .derive_priv_ecdsa_for_master_seed( + hex::decode(HEX_SEED).unwrap().as_ref(), + Network::Dash, + ) + .unwrap(); + assert_eq!( + sk.to_string(), + "xprv9yiAr178GdLQhB7qVbi6YQ76jopjKcUB6gGFZzYjdCNSmq1fU1RG13K3f3UP1EPNPSerY4conJPozCYeKz9QGmmvZ3CFML3qet8YVCwiTrN" + ); + // Add correct expected value + } + + #[test] + fn test_derive_pub_ecdsa_for_master_seed() { + let path = DerivationPath::bip_44_account(Network::Dash, 0); + let pk = path + .derive_pub_ecdsa_for_master_seed( + hex::decode(HEX_SEED).unwrap().as_ref(), + Network::Dash, + ) + .unwrap(); + assert_eq!( + pk.to_string(), + "xpub6ChXFWe26zthufCJbdF6uY3qHqfDj5C2TuBrNNxMBXuRedLp1YjWYqdXWMnn9eLzbWWZCqbi4Cdnes1SNgK9GRaBUcZPLyLEpPRi3dU3syV" + ); + // Add correct expected value + } + + #[test] + fn test_derive_priv_ecdsa_payment_change_key() { + let path = DerivationPath::bip_44_payment_path(Network::Dash, 0, true, 3); + let sk = path + .derive_priv_ecdsa_for_master_seed( + hex::decode(HEX_SEED).unwrap().as_ref(), + Network::Dash, + ) + .unwrap(); + assert_eq!(sk.to_priv().to_wif(), "XGtY11vBj7wfeoHxJQjhBzpbZem2CpEwa62WCisXkwzCLmmD4jRD"); + // Add correct expected value + } + + #[test] + fn test_derive_priv_ecdsa_payment_main_key() { + let path = DerivationPath::bip_44_payment_path(Network::Dash, 0, false, 3); + let sk = path + .derive_priv_ecdsa_for_master_seed( + hex::decode(HEX_SEED).unwrap().as_ref(), + Network::Dash, + ) + .unwrap(); + assert_eq!(sk.to_priv().to_wif(), "XJavmPyJdYEpqZwzVAarQVRhpR7mVLiFHgHoZZTuZdzrpEKDhy6f"); + // Add correct expected value + } + + #[test] + fn test_derive_pub_ecdsa_payment_change_key() { + let path = DerivationPath::bip_44_payment_path(Network::Dash, 0, true, 3); + let sk = path + .derive_pub_ecdsa_for_master_seed( + hex::decode(HEX_SEED).unwrap().as_ref(), + Network::Dash, + ) + .unwrap(); + assert_eq!( + sk.public_key.to_string(), + "034c155580c961177c91eda529147d93ee5088b49a3d9462f8cd9943533ac2fbc8" + ); // Add correct expected value + } + + #[test] + fn test_derive_pub_ecdsa_payment_external_key() { + let path = DerivationPath::bip_44_payment_path(Network::Dash, 0, false, 3); + let sk = path + .derive_pub_ecdsa_for_master_seed( + hex::decode(HEX_SEED).unwrap().as_ref(), + Network::Dash, + ) + .unwrap(); + assert_eq!( + sk.public_key.to_string(), + "0251b09b90295c4c793e9452af0e14142c3406b67e864541149de708eb2d41d104" + ); // Add correct expected value + } } diff --git a/dash/src/blockdata/script/tests.rs b/dash/src/blockdata/script/tests.rs index 7afaead83..f42aa4f9a 100644 --- a/dash/src/blockdata/script/tests.rs +++ b/dash/src/blockdata/script/tests.rs @@ -640,12 +640,12 @@ fn test_script_serde_human_and_not() { // Serialize let json = serde_json::to_string(&script).unwrap(); assert_eq!(json, "\"000102\""); - let bincode = bincode::serialize(&script).unwrap(); + let bincode = bincode_test::serialize(&script).unwrap(); assert_eq!(bincode, [3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2]); // bincode adds u64 for length, serde_cbor use varint // Deserialize assert_eq!(script, serde_json::from_str::(&json).unwrap()); - assert_eq!(script, bincode::deserialize::(&bincode).unwrap()); + assert_eq!(script, bincode_test::deserialize::(&bincode).unwrap()); } #[test] diff --git a/dash/src/blockdata/witness.rs b/dash/src/blockdata/witness.rs index 60484312f..b83c7f158 100644 --- a/dash/src/blockdata/witness.rs +++ b/dash/src/blockdata/witness.rs @@ -648,17 +648,17 @@ mod test { #[cfg(feature = "serde")] #[test] fn test_serde_bincode() { - use bincode; + use bincode_test; let old_witness_format = vec![vec![0u8], vec![2]]; let new_witness_format = Witness::from_slice(&old_witness_format); - let old = bincode::serialize(&old_witness_format).unwrap(); - let new = bincode::serialize(&new_witness_format).unwrap(); + let old = bincode_test::serialize(&old_witness_format).unwrap(); + let new = bincode_test::serialize(&new_witness_format).unwrap(); assert_eq!(old, new); - let back: Witness = bincode::deserialize(&new).unwrap(); + let back: Witness = bincode_test::deserialize(&new).unwrap(); assert_eq!(new_witness_format, back); } diff --git a/dash/src/crypto/key.rs b/dash/src/crypto/key.rs index 342c0a843..3a2884b7f 100644 --- a/dash/src/crypto/key.rs +++ b/dash/src/crypto/key.rs @@ -43,17 +43,25 @@ pub enum Error { Base58(base58::Error), /// secp256k1-related error Secp256k1(secp256k1::Error), + /// bls signatures related error + #[cfg(feature = "bls-signatures")] + BLSError(String), + /// edwards 25519 related error + #[cfg(feature = "ed25519-dalek")] + Ed25519Dalek(String), /// Invalid key prefix error InvalidKeyPrefix(u8), /// Hex decoding error Hex(hex::Error), /// `PublicKey` hex should be 66 or 130 digits long. InvalidHexLength(usize), + /// Something is not supported based on active features + NotSupported(String), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { + match self { Error::Base58(ref e) => write_err!(f, "key base58 error"; e), Error::Secp256k1(ref e) => write_err!(f, "key secp256k1 error"; e), Error::InvalidKeyPrefix(ref b) => write!(f, "key prefix invalid: {}", b), @@ -61,6 +69,13 @@ impl fmt::Display for Error { Error::InvalidHexLength(got) => { write!(f, "PublicKey hex should be 66 or 130 digits long, got: {}", got) } + Error::NotSupported(ref string) => { + write!(f, "{}", string.as_str()) + } + #[cfg(feature = "bls-signatures")] + Error::BLSError(ref string) => write!(f, "{}", string.as_str()), + #[cfg(feature = "ed25519-dalek")] + Error::Ed25519Dalek(ref string) => write!(f, "{}", string.as_str()), } } } @@ -75,6 +90,11 @@ impl std::error::Error for Error { Secp256k1(e) => Some(e), Hex(e) => Some(e), InvalidKeyPrefix(_) | InvalidHexLength(_) => None, + NotSupported(_) => None, + #[cfg(feature = "bls-signatures")] + BLSError(_) => None, + #[cfg(feature = "ed25519-dalek")] + Ed25519Dalek(_) => None, } } } diff --git a/dash/src/crypto/sighash.rs b/dash/src/crypto/sighash.rs index 6bde3220a..7cbf37c06 100644 --- a/dash/src/crypto/sighash.rs +++ b/dash/src/crypto/sighash.rs @@ -1659,7 +1659,7 @@ mod tests { }; // tests - let keypair = secp256k1::KeyPair::from_secret_key(secp, &internal_priv_key); + let keypair = secp256k1::Keypair::from_secret_key(secp, &internal_priv_key); let (internal_key, _parity) = XOnlyPublicKey::from_keypair(&keypair); let tweak = TapTweakHash::from_key_and_tweak(internal_key, merkle_root); let tweaked_keypair = keypair.add_xonly_tweak(secp, &tweak.to_scalar()).unwrap(); @@ -1679,7 +1679,8 @@ mod tests { .unwrap(); let msg = secp256k1::Message::from(sighash); - let key_spend_sig = secp.sign_schnorr_with_aux_rand(&msg, &tweaked_keypair, &[0u8; 32]); + let key_spend_sig = + secp.sign_schnorr_with_aux_rand(msg.as_ref(), &tweaked_keypair, &[0u8; 32]); assert_eq!(expected.internal_pubkey, internal_key); assert_eq!(expected.tweak, tweak); diff --git a/dash/src/dip9.rs b/dash/src/dip9.rs new file mode 100644 index 000000000..8b07731ac --- /dev/null +++ b/dash/src/dip9.rs @@ -0,0 +1,230 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub enum DerivationPathReference { + Unknown = 0, + BIP32 = 1, + BIP44 = 2, + BlockchainIdentities = 3, + ProviderFunds = 4, + ProviderVotingKeys = 5, + ProviderOperatorKeys = 6, + ProviderOwnerKeys = 7, + ContactBasedFunds = 8, + ContactBasedFundsRoot = 9, + ContactBasedFundsExternal = 10, + BlockchainIdentityCreditRegistrationFunding = 11, + BlockchainIdentityCreditTopupFunding = 12, + BlockchainIdentityCreditInvitationFunding = 13, + ProviderPlatformNodeKeys = 14, + Root = 255, +} + +use bitflags::bitflags; +use secp256k1::Secp256k1; + +use crate::Network; +use crate::bip32::{ChildNumber, DerivationPath, Error, ExtendedPrivKey, ExtendedPubKey}; + +bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] + pub struct DerivationPathType: u32 { + const UNKNOWN = 0; + const CLEAR_FUNDS = 1; + const ANONYMOUS_FUNDS = 1 << 1; + const VIEW_ONLY_FUNDS = 1 << 2; + const SINGLE_USER_AUTHENTICATION = 1 << 3; + const MULTIPLE_USER_AUTHENTICATION = 1 << 4; + const PARTIAL_PATH = 1 << 5; + const PROTECTED_FUNDS = 1 << 6; + const CREDIT_FUNDING = 1 << 7; + + // Composite flags + const IS_FOR_AUTHENTICATION = Self::SINGLE_USER_AUTHENTICATION.bits() | Self::MULTIPLE_USER_AUTHENTICATION.bits(); + const IS_FOR_FUNDS = Self::CLEAR_FUNDS.bits() + | Self::ANONYMOUS_FUNDS.bits() + | Self::VIEW_ONLY_FUNDS.bits() + | Self::PROTECTED_FUNDS.bits(); + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct IndexConstPath { + pub indexes: [ChildNumber; N], + pub reference: DerivationPathReference, + pub path_type: DerivationPathType, +} + +impl AsRef<[ChildNumber]> for IndexConstPath { + fn as_ref(&self) -> &[ChildNumber] { self.indexes.as_ref() } +} + +impl From> for DerivationPath { + fn from(value: IndexConstPath) -> Self { DerivationPath::from(value.indexes.as_ref()) } +} + +impl IndexConstPath { + pub fn append_path(&self, derivation_path: DerivationPath) -> DerivationPath { + let mut root_derivation_path = DerivationPath::from(self.indexes.as_ref()); + root_derivation_path.extend(derivation_path); + root_derivation_path + } + + pub fn append(&self, child_number: ChildNumber) -> DerivationPath { + let mut root_derivation_path = DerivationPath::from(self.indexes.as_ref()); + root_derivation_path.extend(&[child_number]); + root_derivation_path + } + + pub fn derive_priv_ecdsa_for_master_seed( + &self, + seed: &[u8], + add_derivation_path: DerivationPath, + network: Network, + ) -> Result { + let secp = Secp256k1::new(); + let sk = ExtendedPrivKey::new_master(network, seed)?; + let path = self.append_path(add_derivation_path); + sk.derive_priv(&secp, &path) + } + + pub fn derive_pub_ecdsa_for_master_seed( + &self, + seed: &[u8], + add_derivation_path: DerivationPath, + network: Network, + ) -> Result { + let secp = Secp256k1::new(); + let sk = self.derive_priv_ecdsa_for_master_seed(seed, add_derivation_path, network)?; + Ok(ExtendedPubKey::from_priv(&secp, &sk)) + } + + pub fn derive_pub_for_master_extended_public_key( + &self, + master_extended_public_key: ExtendedPubKey, + add_derivation_path: DerivationPath, + ) -> Result { + let secp = Secp256k1::new(); + let path = self.append_path(add_derivation_path); + master_extended_public_key.derive_pub(&secp, &path) + } +} + +// Constants for feature purposes and sub-features +pub const BIP44_PURPOSE: u32 = 44; +// Constants for feature purposes and sub-features +pub const FEATURE_PURPOSE: u32 = 9; +pub const DASH_COIN_TYPE: u32 = 5; +pub const DASH_TESTNET_COIN_TYPE: u32 = 1; +pub const FEATURE_PURPOSE_IDENTITIES: u32 = 5; +pub const FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_AUTHENTICATION: u32 = 0; +pub const FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_REGISTRATION: u32 = 1; +pub const FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_TOPUP: u32 = 2; +pub const FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_INVITATIONS: u32 = 3; +pub const FEATURE_PURPOSE_DASHPAY: u32 = 15; +pub const DASH_BIP44_PATH_MAINNET: IndexConstPath<2> = IndexConstPath { + indexes: [ + ChildNumber::Hardened { index: BIP44_PURPOSE }, + ChildNumber::Hardened { index: DASH_COIN_TYPE }, + ], + reference: DerivationPathReference::BIP44, + path_type: DerivationPathType::CLEAR_FUNDS, +}; + +pub const DASH_BIP44_PATH_TESTNET: IndexConstPath<2> = IndexConstPath { + indexes: [ + ChildNumber::Hardened { index: BIP44_PURPOSE }, + ChildNumber::Hardened { index: DASH_TESTNET_COIN_TYPE }, + ], + reference: DerivationPathReference::BIP44, + path_type: DerivationPathType::CLEAR_FUNDS, +}; + +pub const IDENTITY_REGISTRATION_PATH_MAINNET: IndexConstPath<4> = IndexConstPath { + indexes: [ + ChildNumber::Hardened { index: FEATURE_PURPOSE }, + ChildNumber::Hardened { index: DASH_COIN_TYPE }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_REGISTRATION }, + ], + reference: DerivationPathReference::BlockchainIdentityCreditRegistrationFunding, + path_type: DerivationPathType::CREDIT_FUNDING, +}; + +pub const IDENTITY_REGISTRATION_PATH_TESTNET: IndexConstPath<4> = IndexConstPath { + indexes: [ + ChildNumber::Hardened { index: FEATURE_PURPOSE }, + ChildNumber::Hardened { index: DASH_TESTNET_COIN_TYPE }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_REGISTRATION }, + ], + reference: DerivationPathReference::BlockchainIdentityCreditRegistrationFunding, + path_type: DerivationPathType::CREDIT_FUNDING, +}; + +// Identity Top-Up Paths +pub const IDENTITY_TOPUP_PATH_MAINNET: IndexConstPath<4> = IndexConstPath { + indexes: [ + ChildNumber::Hardened { index: FEATURE_PURPOSE }, + ChildNumber::Hardened { index: DASH_COIN_TYPE }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_TOPUP }, + ], + reference: DerivationPathReference::BlockchainIdentityCreditTopupFunding, + path_type: DerivationPathType::CREDIT_FUNDING, +}; + +pub const IDENTITY_TOPUP_PATH_TESTNET: IndexConstPath<4> = IndexConstPath { + indexes: [ + ChildNumber::Hardened { index: FEATURE_PURPOSE }, + ChildNumber::Hardened { index: DASH_TESTNET_COIN_TYPE }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_TOPUP }, + ], + reference: DerivationPathReference::BlockchainIdentityCreditTopupFunding, + path_type: DerivationPathType::CREDIT_FUNDING, +}; + +// Identity Invitation Paths +pub const IDENTITY_INVITATION_PATH_MAINNET: IndexConstPath<4> = IndexConstPath { + indexes: [ + ChildNumber::Hardened { index: FEATURE_PURPOSE }, + ChildNumber::Hardened { index: DASH_COIN_TYPE }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_INVITATIONS }, + ], + reference: DerivationPathReference::BlockchainIdentityCreditInvitationFunding, + path_type: DerivationPathType::CREDIT_FUNDING, +}; + +pub const IDENTITY_INVITATION_PATH_TESTNET: IndexConstPath<4> = IndexConstPath { + indexes: [ + ChildNumber::Hardened { index: FEATURE_PURPOSE }, + ChildNumber::Hardened { index: DASH_TESTNET_COIN_TYPE }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_INVITATIONS }, + ], + reference: DerivationPathReference::BlockchainIdentityCreditInvitationFunding, + path_type: DerivationPathType::CREDIT_FUNDING, +}; + +// Authentication Keys Paths +pub const IDENTITY_AUTHENTICATION_PATH_MAINNET: IndexConstPath<4> = IndexConstPath { + indexes: [ + ChildNumber::Hardened { index: FEATURE_PURPOSE }, + ChildNumber::Hardened { index: DASH_COIN_TYPE }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_AUTHENTICATION }, + ], + reference: DerivationPathReference::BlockchainIdentities, + path_type: DerivationPathType::SINGLE_USER_AUTHENTICATION, +}; + +pub const IDENTITY_AUTHENTICATION_PATH_TESTNET: IndexConstPath<4> = IndexConstPath { + indexes: [ + ChildNumber::Hardened { index: FEATURE_PURPOSE }, + ChildNumber::Hardened { index: DASH_TESTNET_COIN_TYPE }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES }, + ChildNumber::Hardened { index: FEATURE_PURPOSE_IDENTITIES_SUBFEATURE_AUTHENTICATION }, + ], + reference: DerivationPathReference::BlockchainIdentities, + path_type: DerivationPathType::SINGLE_USER_AUTHENTICATION, +}; diff --git a/dash/src/lib.rs b/dash/src/lib.rs index cf86d5343..4223d5071 100644 --- a/dash/src/lib.rs +++ b/dash/src/lib.rs @@ -76,6 +76,11 @@ pub extern crate bitcoinconsensus; pub extern crate dashcore_hashes as hashes; pub extern crate secp256k1; +#[cfg(feature = "bls-signatures")] +pub use bls_signatures; +#[cfg(feature = "ed25519-dalek")] +pub use ed25519_dalek; + #[cfg(feature = "serde")] #[macro_use] extern crate actual_serde as serde; @@ -102,6 +107,7 @@ pub mod consensus; // Private until we either make this a crate or flatten it - still to be decided. pub mod bls_sig_utils; pub(crate) mod crypto; +mod dip9; pub mod ephemerealdata; pub mod error; pub mod hash_types; diff --git a/dash/src/pow.rs b/dash/src/pow.rs index 20d6c9612..4af770e93 100644 --- a/dash/src/pow.rs +++ b/dash/src/pow.rs @@ -1419,8 +1419,8 @@ mod tests { assert_eq!(::serde_json::to_string(&uint).unwrap(), json); assert_eq!(::serde_json::from_str::(&json).unwrap(), uint); - let bin_encoded = bincode::serialize(&uint).unwrap(); - let bin_decoded: U256 = bincode::deserialize(&bin_encoded).unwrap(); + let bin_encoded = bincode_test::serialize(&uint).unwrap(); + let bin_decoded: U256 = bincode_test::deserialize(&bin_encoded).unwrap(); assert_eq!(bin_decoded, uint); }; diff --git a/dash/tests/psbt.rs b/dash/tests/psbt.rs index b22063a40..0b045eb44 100644 --- a/dash/tests/psbt.rs +++ b/dash/tests/psbt.rs @@ -34,6 +34,7 @@ macro_rules! hex_psbt { }; } +#[ignore] #[test] #[ignore] fn bip174_psbt_workflow() { @@ -125,7 +126,7 @@ fn bip174_psbt_workflow() { /// Attempts to build an extended private key from seed and also directly from a string. fn build_extended_private_key() -> ExtendedPrivKey { // Strings from BIP 174 test vector. - let extended_private_key = "tprv8ZgxMBicQKsPeo2FFervjry6YxkJ2JdXKbwP3SFnApxEMXDM9Kt22SzQfWbCCWXDAUHttNjcqvttEvVk7Tausd5TrL45aiTi7aug6f2c537"; + let extended_private_key = "tprv8ZgxMBicQKsPd9TeAdPADNnSyH9SSUUbTVeFszDE23Ki6TBB5nCefAdHkK8Fm3qMQR6sHwA56zqRmKmxnHk37JkiFzvncDqoKmPWubu7hDF"; let seed = "cUkG8i1RFfWGWy5ziR11zJ5V4U4W3viSFCfyJmZnvQaUsd1xuF3T"; let xpriv = ExtendedPrivKey::from_str(extended_private_key).unwrap(); diff --git a/dash/tests/serde.rs b/dash/tests/serde.rs index 513b846c9..7130bfaa9 100644 --- a/dash/tests/serde.rs +++ b/dash/tests/serde.rs @@ -26,7 +26,7 @@ use std::collections::BTreeMap; use std::convert::TryFrom; use std::str::FromStr; -use bincode::serialize; +use bincode_test::serialize; use dashcore::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey, KeySource}; use dashcore::consensus::deserialize; use dashcore::psbt::raw::{Key, Pair, ProprietaryKey}; diff --git a/hashes/Cargo.toml b/hashes/Cargo.toml index 9876b4e2a..09f28a3fa 100644 --- a/hashes/Cargo.toml +++ b/hashes/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dashcore_hashes" -version = "0.12.0" +version = "0.14.0" authors = ["Samuel Westrich "] license = "CC0-1.0" repository = "https://github.com/rust-dashcore/dash_hashes/"