diff --git a/Cargo.lock b/Cargo.lock index f01dd3b..aeb4cbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,7 +249,7 @@ dependencies = [ "dash-primitives", "dash-script", "dash-types", - "hex", + "hex-conservative", "hex-literal", "rstest", "serde", @@ -276,7 +276,7 @@ dependencies = [ "dash-num", "dash-types", "divan", - "hex", + "hex-conservative", "hex-literal", "k256", "rand_core", @@ -295,7 +295,7 @@ dependencies = [ "cfg-if", "dash-num", "divan", - "hex", + "hex-conservative", "hex-literal", "rayon", "rstest", @@ -316,7 +316,7 @@ dependencies = [ "dash-pow", "dash-script", "dash-types", - "hex", + "hex-conservative", "hex-literal", "json5", "rstest", @@ -555,12 +555,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "hex-conservative" version = "0.3.2" diff --git a/pkgs/p2p_core/Cargo.toml b/pkgs/p2p_core/Cargo.toml index b4ead15..443adfe 100644 --- a/pkgs/p2p_core/Cargo.toml +++ b/pkgs/p2p_core/Cargo.toml @@ -36,7 +36,7 @@ serde = { version = "1", default-features = false, features = [ ], optional = true } [dev-dependencies] -hex = "0.4" +hex-conservative = "0.3" hex-literal = "0.4" rstest = "0.25" diff --git a/pkgs/p2p_core/tests/addrv2.rs b/pkgs/p2p_core/tests/addrv2.rs index 704d2bb..5c191aa 100644 --- a/pkgs/p2p_core/tests/addrv2.rs +++ b/pkgs/p2p_core/tests/addrv2.rs @@ -14,6 +14,7 @@ use dash_p2p_core::primitives::service_flags::ServiceFlags; use bitcoin_consensus_encoding::{decode_from_slice, encode_to_vec}; use dash_primitives::NetworkType; +use hex_conservative::FromHex; use rstest::rstest; fn ipv4_entry(ip: [u8; 4], port: u16, time: u32) -> AddrV2Entry { @@ -95,7 +96,7 @@ fn addrv2_bip155_wire_vector() { "f1f2", // port 0xf1f2 ); - let bytes = hex::decode(hex).unwrap_or_else(|e| panic!("bad hex: {e}")); + let bytes = Vec::::from_hex(hex).unwrap_or_else(|e| panic!("bad hex: {e}")); let decoded: AddrV2Msg = decode_from_slice(&bytes).unwrap_or_else(|e| panic!("decode failed: {e}")); assert_eq!(decoded.addrs.len(), 3); @@ -145,7 +146,7 @@ fn addr_v1_wire_vector() { "f1f2", // port ); - let bytes = hex::decode(hex).unwrap_or_else(|e| panic!("bad hex: {e}")); + let bytes = Vec::::from_hex(hex).unwrap_or_else(|e| panic!("bad hex: {e}")); let decoded: Addr = decode_from_slice(&bytes).unwrap_or_else(|e| panic!("decode failed: {e}")); assert_eq!(decoded.addrs.len(), 3); @@ -169,10 +170,10 @@ fn addr_v1_wire_vector() { /// round-trip correctly. #[rstest] fn addrv2_all_bip155_network_types() { - let torv3: Vec = hex::decode("79bcc625184b05194975c28b66b66b0469f7f6556fb1ac3189a79b40dda32f1f") + let torv3: Vec = Vec::::from_hex("79bcc625184b05194975c28b66b66b0469f7f6556fb1ac3189a79b40dda32f1f") .unwrap_or_else(|e| panic!("bad hex: {e}")); - let i2p: Vec = hex::decode("a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87") + let i2p: Vec = Vec::::from_hex("a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87") .unwrap_or_else(|e| panic!("bad hex: {e}")); let original = AddrV2Msg { diff --git a/pkgs/pkc/Cargo.toml b/pkgs/pkc/Cargo.toml index 90b0af0..e85c897 100644 --- a/pkgs/pkc/Cargo.toml +++ b/pkgs/pkc/Cargo.toml @@ -30,7 +30,7 @@ zeroize = { version = "1", default-features = false, features = ["derive"] } [dev-dependencies] divan = "0.1" -hex = "0.4" +hex-conservative = "0.3" rand_core = { version = "0.6", features = ["getrandom"] } rstest = "0.25" serde = { version = "1", features = ["derive"] } diff --git a/pkgs/pkc/tests/bls_chia_aggregate.rs b/pkgs/pkc/tests/bls_chia_aggregate.rs index a4838ac..f26e5c9 100644 --- a/pkgs/pkc/tests/bls_chia_aggregate.rs +++ b/pkgs/pkc/tests/bls_chia_aggregate.rs @@ -96,6 +96,8 @@ fn secure_aggregate_order_independent() { mod kat { use super::common::{self, decode_hex, VectorFile}; + + use hex_conservative::DisplayHex; use serde::Deserialize; #[derive(Deserialize)] @@ -135,7 +137,7 @@ mod kat { .collect(); let pk_refs: Vec<_> = pks.iter().collect(); let agg = dash_pkc::bls_chia::aggregate_pk(&pk_refs).unwrap(); - assert_eq!(hex::encode(agg.to_bytes()), v.agg_pk); + assert_eq!(agg.to_bytes().to_lower_hex_string(), v.agg_pk); } } @@ -155,7 +157,7 @@ mod kat { .collect(); let sig_refs: Vec<_> = sigs.iter().collect(); let agg = dash_pkc::bls_chia::aggregate_sig(&sig_refs).unwrap(); - assert_eq!(hex::encode(agg.to_bytes()), v.agg_sig); + assert_eq!(agg.to_bytes().to_lower_hex_string(), v.agg_sig); } } diff --git a/pkgs/pkc/tests/bls_chia_dh.rs b/pkgs/pkc/tests/bls_chia_dh.rs index 1acd741..e8268f4 100644 --- a/pkgs/pkc/tests/bls_chia_dh.rs +++ b/pkgs/pkc/tests/bls_chia_dh.rs @@ -39,6 +39,8 @@ fn dh_exchange_roundtrip(sk_seed0: SecretKey, sk_seed1: SecretKey) { mod kat { use super::common::{self, decode_hex, VectorFile}; + + use hex_conservative::DisplayHex; use serde::Deserialize; #[derive(Deserialize)] @@ -59,7 +61,7 @@ mod kat { let sk = dash_pkc::bls_chia::SecretKey::from_bytes(&sk_bytes).unwrap(); let peer_pk = dash_pkc::bls_chia::PublicKey::from_bytes(&pk_bytes).unwrap(); let shared = dash_pkc::bls_chia::PublicKey::dh_exchange(&sk, &peer_pk).unwrap(); - assert_eq!(hex::encode(shared.to_bytes()), v.shared); + assert_eq!(shared.to_bytes().to_lower_hex_string(), v.shared); } } } diff --git a/pkgs/pkc/tests/bls_chia_keygen.rs b/pkgs/pkc/tests/bls_chia_keygen.rs index 948a097..fcbdb2a 100644 --- a/pkgs/pkc/tests/bls_chia_keygen.rs +++ b/pkgs/pkc/tests/bls_chia_keygen.rs @@ -67,6 +67,8 @@ fn cross_format_pk_differs(sk_seed0: SecretKey) { mod kat { use super::common::{self, decode_hex, VectorFile}; + + use hex_conservative::DisplayHex; use serde::Deserialize; #[derive(Deserialize)] @@ -84,7 +86,7 @@ mod kat { let sk_bytes: [u8; 32] = decode_hex(&v.sk).try_into().unwrap(); let sk = dash_pkc::bls_chia::SecretKey::from_bytes(&sk_bytes).unwrap(); assert_eq!( - hex::encode(sk.public_key().to_bytes()), + sk.public_key().to_bytes().to_lower_hex_string(), v.pk, "pk mismatch for sk {}", v.sk diff --git a/pkgs/pkc/tests/bls_chia_llmq.rs b/pkgs/pkc/tests/bls_chia_llmq.rs index 5552f63..8f7a030 100644 --- a/pkgs/pkc/tests/bls_chia_llmq.rs +++ b/pkgs/pkc/tests/bls_chia_llmq.rs @@ -14,6 +14,8 @@ mod common; use dash_pkc::bls_chia::{aggregate_pk, aggregate_sig, threshold, PublicKey, SecretKey, Signature}; use dash_pkc::Hash256; +use hex_conservative::DisplayHex; + #[test] fn llmq_contribute_vvec() { let f = common::load("bls_chia_llmq_100"); @@ -115,7 +117,7 @@ fn llmq_commit_quorum_key() { .collect(); let pk_refs: Vec<&PublicKey> = member_pks.iter().collect(); let agg_pk = aggregate_pk(&pk_refs).unwrap(); - assert_eq!(hex::encode(agg_pk.to_bytes()), expected_qpk); + assert_eq!(agg_pk.to_bytes().to_lower_hex_string(), expected_qpk); } #[test] @@ -133,7 +135,7 @@ fn llmq_commit_sk_share() { let refs: Vec<&SecretKey> = received.iter().collect(); let agg = dash_pkc::bls_chia::aggregate_sk(&refs).unwrap(); - assert_eq!(hex::encode(agg.to_bytes()), expected_share); + assert_eq!(agg.to_bytes().to_lower_hex_string(), expected_share); } } @@ -203,7 +205,12 @@ fn llmq_finalize_recover_quorum_sig() { .iter() .map(|sid| { let sid_bytes = common::hex_to_32(sid); - let sid_display = hex::encode(sid_bytes.iter().copied().rev().collect::>()); + let sid_display = sid_bytes + .iter() + .copied() + .rev() + .collect::>() + .to_lower_hex_string(); let idx = member_ids.iter().position(|m| *m == sid_display).unwrap(); let sk = SecretKey::from_bytes(&common::hex_to_32(commits[idx]["sk_share"].as_str().unwrap())).unwrap(); let member_id = common::hash_from_hex(&sid_display); diff --git a/pkgs/pkc/tests/bls_chia_ser.rs b/pkgs/pkc/tests/bls_chia_ser.rs index 0591cae..b518a68 100644 --- a/pkgs/pkc/tests/bls_chia_ser.rs +++ b/pkgs/pkc/tests/bls_chia_ser.rs @@ -13,6 +13,8 @@ mod common; mod kat { use super::common::{self, decode_hex, VectorFile}; + + use hex_conservative::DisplayHex; use serde::Deserialize; #[derive(Deserialize)] @@ -39,7 +41,11 @@ mod kat { // identically. let legacy_bytes: [u8; 48] = decode_hex(&v.pk_legacy).try_into().unwrap(); let pk = dash_pkc::bls_chia::PublicKey::from_bytes(&legacy_bytes).unwrap(); - assert_eq!(hex::encode(pk.to_bytes()), v.pk_legacy, "legacy pk roundtrip mismatch"); + assert_eq!( + pk.to_bytes().to_lower_hex_string(), + v.pk_legacy, + "legacy pk roundtrip mismatch" + ); // The two formats must differ for the same point. assert_ne!(v.pk_legacy, v.pk_ietf, "legacy and ietf should differ"); @@ -56,7 +62,7 @@ mod kat { let legacy_bytes: [u8; 96] = decode_hex(&v.sig_legacy).try_into().unwrap(); let sig = dash_pkc::bls_chia::Signature::from_bytes(&legacy_bytes).unwrap(); assert_eq!( - hex::encode(sig.to_bytes()), + sig.to_bytes().to_lower_hex_string(), v.sig_legacy, "legacy sig roundtrip mismatch" ); diff --git a/pkgs/pkc/tests/bls_chia_sign.rs b/pkgs/pkc/tests/bls_chia_sign.rs index 16321d9..02b6e18 100644 --- a/pkgs/pkc/tests/bls_chia_sign.rs +++ b/pkgs/pkc/tests/bls_chia_sign.rs @@ -120,6 +120,8 @@ fn legacy_pk_serialization_differs_from_ietf() { mod kat { use super::common::{self, decode_hex, VectorFile}; + + use hex_conservative::DisplayHex; use serde::Deserialize; use sha2::{Digest, Sha256}; @@ -156,7 +158,7 @@ mod kat { let sk = dash_pkc::bls_chia::SecretKey::from_bytes(&sk_bytes).unwrap(); let sig = sk.sign(&msg); assert_eq!( - hex::encode(sig.to_bytes()), + sig.to_bytes().to_lower_hex_string(), v.sig, "sig mismatch for sk={} msg={}", v.sk, @@ -193,7 +195,7 @@ mod kat { concat[..32].copy_from_slice(&h0); concat[32..].copy_from_slice(&h1); assert_eq!( - hex::encode(concat), + concat.to_lower_hex_string(), **exp, "SHA-256 mismatch for tag {:?}", std::str::from_utf8(*tag).unwrap() diff --git a/pkgs/pkc/tests/bls_ietf_aggregate.rs b/pkgs/pkc/tests/bls_ietf_aggregate.rs index 62be063..91c0057 100644 --- a/pkgs/pkc/tests/bls_ietf_aggregate.rs +++ b/pkgs/pkc/tests/bls_ietf_aggregate.rs @@ -118,6 +118,8 @@ fn secure_verify_rejects_naive_aggregate(sk_seed0: SecretKey, sk_seed1: SecretKe mod kat { use super::common::{self, decode_hex, VectorFile}; + + use hex_conservative::DisplayHex; use serde::Deserialize; #[derive(Deserialize)] @@ -163,7 +165,7 @@ mod kat { .collect(); let pk_refs: Vec<_> = pks.iter().collect(); let agg = dash_pkc::bls_ietf::aggregate_pk(&pk_refs).unwrap(); - assert_eq!(hex::encode(agg.to_bytes()), v.agg_pk); + assert_eq!(agg.to_bytes().to_lower_hex_string(), v.agg_pk); } } @@ -183,7 +185,7 @@ mod kat { .collect(); let sig_refs: Vec<_> = sigs.iter().collect(); let agg = dash_pkc::bls_ietf::aggregate_sig(&sig_refs).unwrap(); - assert_eq!(hex::encode(agg.to_bytes()), v.agg_sig); + assert_eq!(agg.to_bytes().to_lower_hex_string(), v.agg_sig); } } @@ -203,7 +205,7 @@ mod kat { .collect(); let sk_refs: Vec<_> = sks.iter().collect(); let agg = dash_pkc::bls_ietf::aggregate_sk(&sk_refs).unwrap(); - assert_eq!(hex::encode(agg.to_bytes()), v.agg_sk); + assert_eq!(agg.to_bytes().to_lower_hex_string(), v.agg_sk); } } diff --git a/pkgs/pkc/tests/bls_ietf_dh.rs b/pkgs/pkc/tests/bls_ietf_dh.rs index c42aa8e..968db88 100644 --- a/pkgs/pkc/tests/bls_ietf_dh.rs +++ b/pkgs/pkc/tests/bls_ietf_dh.rs @@ -39,6 +39,8 @@ fn dh_exchange_roundtrip(sk_seed0: SecretKey, sk_seed1: SecretKey) { mod kat { use super::common::{self, decode_hex, VectorFile}; + + use hex_conservative::DisplayHex; use serde::Deserialize; #[derive(Deserialize)] @@ -59,7 +61,7 @@ mod kat { let sk = dash_pkc::bls_ietf::SecretKey::from_bytes(&sk_bytes).unwrap(); let peer_pk = dash_pkc::bls_ietf::PublicKey::from_bytes(&pk_bytes).unwrap(); let shared = dash_pkc::bls_ietf::PublicKey::dh_exchange(&sk, &peer_pk).unwrap(); - assert_eq!(hex::encode(shared.to_bytes()), v.shared); + assert_eq!(shared.to_bytes().to_lower_hex_string(), v.shared); } } } diff --git a/pkgs/pkc/tests/bls_ietf_keygen.rs b/pkgs/pkc/tests/bls_ietf_keygen.rs index 3315c5f..0bb6e36 100644 --- a/pkgs/pkc/tests/bls_ietf_keygen.rs +++ b/pkgs/pkc/tests/bls_ietf_keygen.rs @@ -65,6 +65,8 @@ fn cross_format_pk_differs(sk_seed0: SecretKey) { mod kat { use super::common::{self, decode_hex, VectorFile}; + + use hex_conservative::DisplayHex; use serde::Deserialize; #[derive(Deserialize)] @@ -82,7 +84,7 @@ mod kat { let sk_bytes: [u8; 32] = decode_hex(&v.sk).try_into().unwrap(); let sk = dash_pkc::bls_ietf::SecretKey::from_bytes(&sk_bytes).unwrap(); assert_eq!( - hex::encode(sk.public_key().to_bytes()), + sk.public_key().to_bytes().to_lower_hex_string(), v.pk, "pk mismatch for sk {}", v.sk diff --git a/pkgs/pkc/tests/bls_ietf_llmq.rs b/pkgs/pkc/tests/bls_ietf_llmq.rs index e7fdb54..79993a5 100644 --- a/pkgs/pkc/tests/bls_ietf_llmq.rs +++ b/pkgs/pkc/tests/bls_ietf_llmq.rs @@ -18,6 +18,8 @@ mod common; use dash_pkc::bls_ietf::{aggregate_pk, aggregate_sig, threshold, PublicKey, SecretKey, Signature}; use dash_pkc::Hash256; +use hex_conservative::DisplayHex; + #[test] fn llmq_contribute_vvec() { let f = common::load("bls_ietf_llmq_100"); @@ -149,7 +151,7 @@ fn llmq_commit_quorum_key() { .collect(); let pk_refs: Vec<&PublicKey> = member_pks.iter().collect(); let agg_pk = aggregate_pk(&pk_refs).unwrap(); - assert_eq!(hex::encode(agg_pk.to_bytes()), expected_qpk,); + assert_eq!(agg_pk.to_bytes().to_lower_hex_string(), expected_qpk,); } #[test] @@ -172,7 +174,7 @@ fn llmq_commit_sk_share() { let refs: Vec<&SecretKey> = received.iter().collect(); let agg = dash_pkc::bls_ietf::aggregate_sk(&refs).unwrap(); assert_eq!( - hex::encode(agg.to_bytes()), + agg.to_bytes().to_lower_hex_string(), expected_share, "sk_share mismatch for member {}", member_idx, @@ -250,7 +252,12 @@ fn llmq_finalize_recover_quorum_sig() { // sid is internal byte order; byte-reverse to get // the display hex that matches member_ids. let sid_bytes = common::hex_to_32(sid); - let sid_display = hex::encode(sid_bytes.iter().copied().rev().collect::>()); + let sid_display = sid_bytes + .iter() + .copied() + .rev() + .collect::>() + .to_lower_hex_string(); let idx = member_ids.iter().position(|m| *m == sid_display).unwrap(); let sk = SecretKey::from_bytes(&common::hex_to_32(commits[idx]["sk_share"].as_str().unwrap())).unwrap(); let member_id = common::hash_from_hex(&sid_display); diff --git a/pkgs/pkc/tests/bls_ietf_sign.rs b/pkgs/pkc/tests/bls_ietf_sign.rs index 3206ced..8c8b656 100644 --- a/pkgs/pkc/tests/bls_ietf_sign.rs +++ b/pkgs/pkc/tests/bls_ietf_sign.rs @@ -129,6 +129,8 @@ fn cross_format_sig_differs(sk_seed0: SecretKey) { mod kat { use super::common::{self, decode_hex, VectorFile}; + + use hex_conservative::DisplayHex; use serde::Deserialize; #[derive(Deserialize)] @@ -149,7 +151,7 @@ mod kat { let sk = dash_pkc::bls_ietf::SecretKey::from_bytes(&sk_bytes).unwrap(); let sig = sk.sign(&msg); assert_eq!( - hex::encode(sig.to_bytes()), + sig.to_bytes().to_lower_hex_string(), v.sig, "sig mismatch for sk={} msg={}", v.sk, diff --git a/pkgs/pkc/tests/k256_keygen.rs b/pkgs/pkc/tests/k256_keygen.rs index b29f268..85c155d 100644 --- a/pkgs/pkc/tests/k256_keygen.rs +++ b/pkgs/pkc/tests/k256_keygen.rs @@ -77,6 +77,8 @@ fn serde_pk_roundtrip(alice: SecretKey) { mod kat { use super::common::{self, decode_hex, VectorFile}; + + use hex_conservative::DisplayHex; use serde::Deserialize; #[derive(Deserialize)] @@ -95,7 +97,7 @@ mod kat { let sk = dash_pkc::k256::SecretKey::from_bytes(&sk_bytes).unwrap(); let pk = sk.public_key(); assert_eq!( - hex::encode(pk.to_bytes()), + pk.to_bytes().to_lower_hex_string(), v.pk_compressed, "pk mismatch for sk {}", v.sk diff --git a/pkgs/pkc/tests/k256_sign.rs b/pkgs/pkc/tests/k256_sign.rs index 0bcc115..004d91f 100644 --- a/pkgs/pkc/tests/k256_sign.rs +++ b/pkgs/pkc/tests/k256_sign.rs @@ -126,6 +126,8 @@ fn serde_recovery_id_roundtrip() { mod kat { use super::common::{self, decode_hex, VectorFile}; + + use hex_conservative::DisplayHex; use serde::Deserialize; #[derive(Deserialize)] @@ -155,7 +157,7 @@ mod kat { let sk = dash_pkc::k256::SecretKey::from_bytes(&sk_bytes).unwrap(); let (sig, rid) = sk.sign_recoverable(&msg).unwrap(); assert_eq!( - hex::encode(sig.to_compact()), + sig.to_compact().to_lower_hex_string(), v.sig, "sig mismatch for sk={} msg={}", v.sk, @@ -176,7 +178,7 @@ mod kat { let sig = dash_pkc::k256::Signature::from_compact(&sig_bytes).unwrap(); let rid = dash_pkc::k256::RecoveryId::new(v.recovery_id).unwrap(); let pk = dash_pkc::k256::PublicKey::recover(&msg, &sig, rid).unwrap(); - assert_eq!(hex::encode(pk.to_bytes()), v.pk, "recovered pk mismatch"); + assert_eq!(pk.to_bytes().to_lower_hex_string(), v.pk, "recovered pk mismatch"); } } } diff --git a/pkgs/pow/Cargo.toml b/pkgs/pow/Cargo.toml index f8404af..30fc1fd 100644 --- a/pkgs/pow/Cargo.toml +++ b/pkgs/pow/Cargo.toml @@ -19,7 +19,7 @@ rayon = { version = "1", optional = true } [dev-dependencies] divan = "0.1" -hex = "0.4" +hex-conservative = "0.3" hex-literal = "0.4" rstest = "0.25" serde = { version = "1", features = ["derive"] } diff --git a/pkgs/pow/tests/common/mod.rs b/pkgs/pow/tests/common/mod.rs index fa197af..27e6d38 100644 --- a/pkgs/pow/tests/common/mod.rs +++ b/pkgs/pow/tests/common/mod.rs @@ -11,6 +11,8 @@ use std::collections::HashMap; +use hex_conservative::FromHex; + const NIST_MSG_BLOB: &[u8] = include_bytes!("../../corpus/nist_msg.bin"); /// Returns the NIST test message of `byte_len` bytes. @@ -36,7 +38,7 @@ pub fn load(name: &str) -> NistVectors { .map(|(k, v)| { let bit_len: usize = k.parse().expect("key must be numeric"); assert_eq!(bit_len % 8, 0, "only byte-aligned vectors supported"); - let bytes = hex::decode(&v).expect("invalid hex in test vector"); + let bytes = Vec::::from_hex(&v).expect("invalid hex in test vector"); assert_eq!(bytes.len(), 64, "digest must be 64 bytes"); let mut arr = [0u8; 64]; arr.copy_from_slice(&bytes); diff --git a/pkgs/primitives/Cargo.toml b/pkgs/primitives/Cargo.toml index f2cac44..1b56903 100644 --- a/pkgs/primitives/Cargo.toml +++ b/pkgs/primitives/Cargo.toml @@ -14,6 +14,7 @@ std = [ "dash-num/std", "dash-script/std", "dash-types/std", + "hex-conservative/std", ] serde = ["dep:serde", "dash-num/serde", "dash-script/serde", "dash-types/serde"] full = ["std", "serde"] @@ -33,7 +34,7 @@ bitcoin-units = { version = "0.3", default-features = false, features = [ dash-num = { version = "0.0.0", path = "../num" } dash-script = { version = "0.0.0", path = "../script" } dash-types = { version = "0.0.0", path = "../types", default-features = false } -hex = { version = "0.4", default-features = false, features = ["alloc"] } +hex-conservative = { version = "0.3", default-features = false, features = ["alloc"] } serde = { version = "1", default-features = false, features = [ "alloc", "derive", diff --git a/pkgs/primitives/src/gov.rs b/pkgs/primitives/src/gov.rs index 5d9a13e..2869f6b 100644 --- a/pkgs/primitives/src/gov.rs +++ b/pkgs/primitives/src/gov.rs @@ -16,6 +16,7 @@ use crate::TxHash; use core::fmt; use bitcoin_hashes::sha256d; +use hex_conservative::DisplayHex; /// Governance object type codes. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -189,7 +190,7 @@ impl GovObject { /// `object_type` are excluded, and `data` is hex-encoded as ASCII bytes /// before hashing. pub fn hash(&self) -> TxHash { - let data_hex = hex::encode(&self.data); + let data_hex = self.data.to_lower_hex_string(); let mut buf = Vec::new(); buf.extend_from_slice(self.hash_parent.as_bytes()); @@ -217,7 +218,7 @@ impl GovObject { /// Returns the data as a hex string. pub fn data_as_hex(&self) -> alloc::string::String { - hex::encode(&self.data) + self.data.to_lower_hex_string() } } diff --git a/pkgs/primitives/tests/assetlock.rs b/pkgs/primitives/tests/assetlock.rs index 3132b7c..afd03e6 100644 --- a/pkgs/primitives/tests/assetlock.rs +++ b/pkgs/primitives/tests/assetlock.rs @@ -11,6 +11,7 @@ mod util; use dash_primitives::payload::AssetLock; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] @@ -40,7 +41,7 @@ fn decode_fields() { "{txid} output {i}", ); - let expected_script = hex::decode(util::json_str(&expected["scriptPubKey"])).unwrap(); + let expected_script = Vec::::from_hex(util::json_str(&expected["scriptPubKey"])).unwrap(); assert_eq!( output.script_pubkey.as_bytes(), &expected_script[..], diff --git a/pkgs/primitives/tests/assetunlock.rs b/pkgs/primitives/tests/assetunlock.rs index 74c7954..e432b09 100644 --- a/pkgs/primitives/tests/assetunlock.rs +++ b/pkgs/primitives/tests/assetunlock.rs @@ -13,6 +13,7 @@ mod util; use dash_primitives::payload::AssetUnlock; use dash_primitives::QuorumHash; use dash_types::BlsSignatureBytes; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] @@ -42,7 +43,7 @@ fn decode_fields() { "{txid}", ); - let expected_sig: [u8; 96] = hex::decode(util::json_str(&d["quorumSig"])) + let expected_sig: [u8; 96] = Vec::::from_hex(util::json_str(&d["quorumSig"])) .unwrap() .try_into() .unwrap(); diff --git a/pkgs/primitives/tests/blocks.rs b/pkgs/primitives/tests/blocks.rs index 1ee3a9f..ea4c76c 100644 --- a/pkgs/primitives/tests/blocks.rs +++ b/pkgs/primitives/tests/blocks.rs @@ -12,13 +12,14 @@ mod util; use bitcoin_consensus_encoding::{decode_from_slice, encode_to_vec}; use dash_primitives::{Block, BlockHash, DeploymentContext, MerkleRoot}; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] fn decode_fields() { let corpus = util::load_blocks(); for (block_hash_hex, entry) in &corpus { - let raw = hex::decode(&entry.raw).unwrap(); + let raw = Vec::::from_hex(&entry.raw).unwrap(); let block: Block = decode_from_slice(&raw).unwrap(); assert!(block.validate(&DeploymentContext::default()).is_ok()); @@ -76,7 +77,7 @@ fn decode_fields() { fn round_trip() { let corpus = util::load_blocks(); for (block_hash_hex, entry) in &corpus { - let raw = hex::decode(&entry.raw).unwrap(); + let raw = Vec::::from_hex(&entry.raw).unwrap(); let block: Block = decode_from_slice(&raw).unwrap(); let encoded = encode_to_vec(&block); assert_eq!(encoded, raw, "{block_hash_hex} round-trip"); @@ -87,7 +88,7 @@ fn round_trip() { fn block_hash() { let corpus = util::load_blocks(); for (block_hash_hex, entry) in &corpus { - let raw = hex::decode(&entry.raw).unwrap(); + let raw = Vec::::from_hex(&entry.raw).unwrap(); let pow_hash = BlockHash::from(dash_pow::hash(&raw[..80])); let expected = BlockHash::from_hex(block_hash_hex).unwrap(); assert_eq!(pow_hash, expected, "{block_hash_hex} hash"); diff --git a/pkgs/primitives/tests/cbtx.rs b/pkgs/primitives/tests/cbtx.rs index c681b35..a7ee17f 100644 --- a/pkgs/primitives/tests/cbtx.rs +++ b/pkgs/primitives/tests/cbtx.rs @@ -13,6 +13,7 @@ mod util; use dash_primitives::payload::CoinbaseCommitment; use dash_primitives::MerkleRoot; use dash_types::BlsSignatureBytes; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] @@ -53,7 +54,7 @@ fn decode_fields() { } if let Some(sig) = d.get("bestCLSignature") { - let expected: [u8; 96] = hex::decode(util::json_str(sig)).unwrap().try_into().unwrap(); + let expected: [u8; 96] = Vec::::from_hex(util::json_str(sig)).unwrap().try_into().unwrap(); assert_eq!(cbtx.best_cl_signature.unwrap(), BlsSignatureBytes(expected), "{txid}",); } diff --git a/pkgs/primitives/tests/coinbase.rs b/pkgs/primitives/tests/coinbase.rs index f0b0a2e..58723ab 100644 --- a/pkgs/primitives/tests/coinbase.rs +++ b/pkgs/primitives/tests/coinbase.rs @@ -11,6 +11,7 @@ mod util; use dash_primitives::TxType; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] @@ -36,7 +37,7 @@ fn decode_fields() { // Coinbase inputs have a "coinbase" field (the script_sig hex) for (i, ev) in expected_vin.iter().enumerate() { if let Some(cb) = ev.get("coinbase") { - let expected_script = hex::decode(util::json_str(cb)).unwrap(); + let expected_script = Vec::::from_hex(util::json_str(cb)).unwrap(); assert_eq!( tx.inputs[i].script_sig.as_bytes(), &expected_script[..], @@ -59,7 +60,7 @@ fn decode_fields() { bitcoin_units::Amount::from_sat(util::json_u64(&ev["valueSat"])).unwrap(), "{txid} vout[{i}] value", ); - let expected_script = hex::decode(util::json_str(&ev["scriptPubKey"])).unwrap(); + let expected_script = Vec::::from_hex(util::json_str(&ev["scriptPubKey"])).unwrap(); assert_eq!( tx.outputs[i].script_pubkey.as_bytes(), &expected_script[..], diff --git a/pkgs/primitives/tests/data.rs b/pkgs/primitives/tests/data.rs index 76a71f4..bc1b947 100644 --- a/pkgs/primitives/tests/data.rs +++ b/pkgs/primitives/tests/data.rs @@ -11,6 +11,7 @@ mod util; use dash_primitives::{TxHash, TxType}; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] @@ -36,7 +37,7 @@ fn decode_fields() { for (i, ev) in expected_vin.iter().enumerate() { if let Some(cb) = ev.get("coinbase") { // Coinbase input - let expected_script = hex::decode(util::json_str(cb)).unwrap(); + let expected_script = Vec::::from_hex(util::json_str(cb)).unwrap(); assert_eq!( tx.inputs[i].script_sig.as_bytes(), &expected_script[..], @@ -71,7 +72,7 @@ fn decode_fields() { bitcoin_units::Amount::from_sat(util::json_u64(&ev["valueSat"])).unwrap(), "{txid} vout[{i}] value", ); - let expected_script = hex::decode(util::json_str(&ev["scriptPubKey"])).unwrap(); + let expected_script = Vec::::from_hex(util::json_str(&ev["scriptPubKey"])).unwrap(); assert_eq!( tx.outputs[i].script_pubkey.as_bytes(), &expected_script[..], diff --git a/pkgs/primitives/tests/mnhftx.rs b/pkgs/primitives/tests/mnhftx.rs index 1d01531..5df76d6 100644 --- a/pkgs/primitives/tests/mnhftx.rs +++ b/pkgs/primitives/tests/mnhftx.rs @@ -13,6 +13,7 @@ mod util; use dash_primitives::payload::MnHardFork; use dash_primitives::QuorumHash; use dash_types::BlsSignatureBytes; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] @@ -41,7 +42,10 @@ fn decode_fields() { "{txid}", ); - let expected_sig: [u8; 96] = hex::decode(util::json_str(&signal["sig"])).unwrap().try_into().unwrap(); + let expected_sig: [u8; 96] = Vec::::from_hex(util::json_str(&signal["sig"])) + .unwrap() + .try_into() + .unwrap(); assert_eq!(payload.sig, BlsSignatureBytes(expected_sig), "{txid}",); } } diff --git a/pkgs/primitives/tests/proposals.rs b/pkgs/primitives/tests/proposals.rs index 15f04c9..0440e9e 100644 --- a/pkgs/primitives/tests/proposals.rs +++ b/pkgs/primitives/tests/proposals.rs @@ -12,13 +12,14 @@ mod util; use dash_primitives::gov::{GovObject, GovObjectType, Proposal}; use dash_primitives::TxHash; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] fn decode_and_hash() { let corpus = util::load_proposals(); for (obj_hash_hex, entry) in &corpus { - let raw = hex::decode(&entry.raw).unwrap(); + let raw = Vec::::from_hex(&entry.raw).unwrap(); let obj = GovObject::decode(&raw).unwrap(); let d = &entry.details; let payload = &d["payload"]; diff --git a/pkgs/primitives/tests/proregtx.rs b/pkgs/primitives/tests/proregtx.rs index 679f442..7e665d7 100644 --- a/pkgs/primitives/tests/proregtx.rs +++ b/pkgs/primitives/tests/proregtx.rs @@ -14,6 +14,7 @@ use dash_primitives::payload::ProRegTx; use dash_primitives::{InputsHash, TxHash}; use dash_script::KeyId; use dash_types::{BlsPublicKeyBytes, PlatformNodeId}; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] @@ -54,7 +55,7 @@ fn decode_fields() { assert_eq!(payload.key_id_voting, KeyId(key_id), "{txid} votingAddress",); } - let expected_pubkey: [u8; 48] = hex::decode(util::json_str(&d["pubKeyOperator"])) + let expected_pubkey: [u8; 48] = Vec::::from_hex(util::json_str(&d["pubKeyOperator"])) .unwrap() .try_into() .unwrap(); diff --git a/pkgs/primitives/tests/proupregtx.rs b/pkgs/primitives/tests/proupregtx.rs index 941be3c..877bb53 100644 --- a/pkgs/primitives/tests/proupregtx.rs +++ b/pkgs/primitives/tests/proupregtx.rs @@ -14,6 +14,7 @@ use dash_primitives::payload::ProUpRegTx; use dash_primitives::{InputsHash, TxHash}; use dash_script::KeyId; use dash_types::BlsPublicKeyBytes; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] @@ -41,7 +42,7 @@ fn decode_fields() { assert_eq!(payload.key_id_voting, KeyId(key_id), "{txid} votingAddress",); } - let expected_pubkey: [u8; 48] = hex::decode(util::json_str(&d["pubKeyOperator"])) + let expected_pubkey: [u8; 48] = Vec::::from_hex(util::json_str(&d["pubKeyOperator"])) .unwrap() .try_into() .unwrap(); diff --git a/pkgs/primitives/tests/qctx.rs b/pkgs/primitives/tests/qctx.rs index 68dd672..ef7c376 100644 --- a/pkgs/primitives/tests/qctx.rs +++ b/pkgs/primitives/tests/qctx.rs @@ -13,6 +13,7 @@ mod util; use dash_primitives::payload::FinalCommitment; use dash_primitives::{LlmqType, QuorumHash, QuorumVvecHash}; use dash_types::{BlsPublicKeyBytes, BlsSignatureBytes}; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] @@ -73,15 +74,15 @@ fn decode_fields() { ); // Bitset raw data - let expected_signers = hex::decode(util::json_str(&c["signers"])).unwrap(); + let expected_signers = Vec::::from_hex(util::json_str(&c["signers"])).unwrap(); assert_eq!(commitment.signers.data, expected_signers, "{txid} commitment.signers",); - let expected_valid = hex::decode(util::json_str(&c["validMembers"])).unwrap(); + let expected_valid = Vec::::from_hex(util::json_str(&c["validMembers"])).unwrap(); assert_eq!( commitment.valid_members.data, expected_valid, "{txid} commitment.validMembers", ); - let expected_pk: [u8; 48] = hex::decode(util::json_str(&c["quorumPublicKey"])) + let expected_pk: [u8; 48] = Vec::::from_hex(util::json_str(&c["quorumPublicKey"])) .unwrap() .try_into() .unwrap(); @@ -97,7 +98,7 @@ fn decode_fields() { "{txid} commitment.quorumVvecHash", ); - let expected_qsig: [u8; 96] = hex::decode(util::json_str(&c["quorumSig"])) + let expected_qsig: [u8; 96] = Vec::::from_hex(util::json_str(&c["quorumSig"])) .unwrap() .try_into() .unwrap(); @@ -107,7 +108,7 @@ fn decode_fields() { "{txid} commitment.quorumSig", ); - let expected_msig: [u8; 96] = hex::decode(util::json_str(&c["membersSig"])) + let expected_msig: [u8; 96] = Vec::::from_hex(util::json_str(&c["membersSig"])) .unwrap() .try_into() .unwrap(); diff --git a/pkgs/primitives/tests/spend.rs b/pkgs/primitives/tests/spend.rs index fe6e2b5..3e32a09 100644 --- a/pkgs/primitives/tests/spend.rs +++ b/pkgs/primitives/tests/spend.rs @@ -11,6 +11,7 @@ mod util; use dash_primitives::{TxHash, TxType}; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] @@ -44,7 +45,7 @@ fn decode_fields() { util::json_u64(&ev["vout"]) as u32, "{txid} vin[{i}] vout", ); - let expected_script = hex::decode(util::json_str(&ev["scriptSig"])).unwrap(); + let expected_script = Vec::::from_hex(util::json_str(&ev["scriptSig"])).unwrap(); assert_eq!( tx.inputs[i].script_sig.as_bytes(), &expected_script[..], @@ -66,7 +67,7 @@ fn decode_fields() { bitcoin_units::Amount::from_sat(util::json_u64(&ev["valueSat"])).unwrap(), "{txid} vout[{i}] value", ); - let expected_script = hex::decode(util::json_str(&ev["scriptPubKey"])).unwrap(); + let expected_script = Vec::::from_hex(util::json_str(&ev["scriptPubKey"])).unwrap(); assert_eq!( tx.outputs[i].script_pubkey.as_bytes(), &expected_script[..], diff --git a/pkgs/primitives/tests/triggers.rs b/pkgs/primitives/tests/triggers.rs index 238aa91..92f8af1 100644 --- a/pkgs/primitives/tests/triggers.rs +++ b/pkgs/primitives/tests/triggers.rs @@ -12,13 +12,14 @@ mod util; use dash_primitives::gov::{GovObject, GovObjectType}; use dash_primitives::TxHash; +use hex_conservative::FromHex; use rstest::rstest; #[rstest] fn decode_and_hash() { let corpus = util::load_triggers(); for (obj_hash_hex, entry) in &corpus { - let raw = hex::decode(&entry.raw).unwrap(); + let raw = Vec::::from_hex(&entry.raw).unwrap(); let obj = GovObject::decode(&raw).unwrap(); let d = &entry.details; let payload = &d["payload"]; diff --git a/pkgs/primitives/tests/util/mod.rs b/pkgs/primitives/tests/util/mod.rs index 8cab5f6..d442da8 100644 --- a/pkgs/primitives/tests/util/mod.rs +++ b/pkgs/primitives/tests/util/mod.rs @@ -12,6 +12,7 @@ use std::collections::BTreeMap; use bitcoin_consensus_encoding::{decode_from_slice, encode_to_vec}; use dash_primitives::{Transaction, TxHash}; +use hex_conservative::FromHex; use serde::Deserialize; /// A single entry from a transaction corpus JSON5 file. @@ -79,20 +80,20 @@ pub fn load_triggers() -> BTreeMap { /// Decodes a raw transaction hex string into a `Transaction`. pub fn decode_tx(raw_hex: &str) -> Transaction { - let bytes = hex::decode(raw_hex).unwrap(); + let bytes = Vec::::from_hex(raw_hex).unwrap(); decode_from_slice::(&bytes).unwrap() } /// Asserts that re-encoding a transaction produces the same bytes. pub fn assert_round_trip(raw_hex: &str, tx: &Transaction, label: &str) { - let expected = hex::decode(raw_hex).unwrap(); + let expected = Vec::::from_hex(raw_hex).unwrap(); let encoded = encode_to_vec(tx); assert_eq!(encoded, expected, "round-trip mismatch for {label}"); } /// Asserts that `double_sha256(raw_hex)` matches the corpus txid key. pub fn assert_txid(raw_hex: &str, expected_txid: &str) { - let raw = hex::decode(raw_hex).unwrap(); + let raw = Vec::::from_hex(raw_hex).unwrap(); let computed = dash_primitives::hash::tx_hash(&raw); let expected = TxHash::from_hex(expected_txid).unwrap(); assert_eq!(computed, expected, "txid mismatch for {expected_txid}"); @@ -104,7 +105,7 @@ pub fn assert_txid(raw_hex: &str, expected_txid: &str) { /// in big-endian display order; this reverses them to wire order. /// For 32-byte hashes, use `Hash256::from_hex()` instead. pub fn parse_reversed_hex(hex_str: &str) -> [u8; N] { - let decoded = hex::decode(hex_str).unwrap(); + let decoded = Vec::::from_hex(hex_str).unwrap(); assert_eq!(decoded.len(), N, "expected {N}-byte value, got {}", decoded.len()); let mut bytes = [0u8; N]; bytes.copy_from_slice(&decoded);