diff --git a/crates/rbitcoin-bench/src/targets.rs b/crates/rbitcoin-bench/src/targets.rs index 162dd394c2..f3b54525bf 100644 --- a/crates/rbitcoin-bench/src/targets.rs +++ b/crates/rbitcoin-bench/src/targets.rs @@ -1,17 +1,11 @@ //! One scripthash or Bitcoin address per line. Embedded corpora in `corpora/`. use bitcoin::address::NetworkUnchecked; -use bitcoin::hashes::{sha256, Hash}; use bitcoin::Address; use std::path::Path; use std::str::FromStr; -/// Electrum / Esplora display scripthash: SHA256(spk) bytes reversed, hex. -pub fn electrum_scripthash_hex(spk: &[u8]) -> String { - rbitcoin_primitives::display_hash_hex(sha256::Hash::hash(spk).to_byte_array()) -} - pub const CORPUS_HOT: &str = include_str!("../corpora/hot.txt"); pub const CORPUS_CASA: &str = include_str!("../corpora/casa.txt"); pub const CORPUS_SPARROW: &str = include_str!("../corpora/sparrow.txt"); @@ -39,7 +33,7 @@ pub fn parse_target_line(line: &str) -> Result, String> { } let addr: Address = Address::from_str(line).map_err(|e| e.to_string())?; let addr = addr.assume_checked(); - Ok(Some(electrum_scripthash_hex( + Ok(Some(rbitcoin_primitives::scripthash_display_hex( addr.script_pubkey().as_bytes(), ))) } diff --git a/crates/rbitcoin-electrum/src/server.rs b/crates/rbitcoin-electrum/src/server.rs index dab8b3f6ee..b4ab586e1f 100644 --- a/crates/rbitcoin-electrum/src/server.rs +++ b/crates/rbitcoin-electrum/src/server.rs @@ -11,7 +11,7 @@ use rbitcoin_net::{BlockingRegion, MempoolHub}; use rbitcoin_primitives::{Fk, Height}; use rbitcoin_query::{ChainView, ChainViewKind, HistoryFilter, Query, ShJoinSlot}; -use rbitcoin_store::{script_hash, StoreError}; +use rbitcoin_store::StoreError; use serde_json::{json, Value}; use std::collections::{HashMap, HashSet}; use std::net::SocketAddr; @@ -2617,8 +2617,7 @@ fn scripthash_status_full_slot( /// Helper to compute electrum scripthash hex (reversed) from script bytes. pub fn electrum_scripthash_hex(script: &[u8]) -> String { - let h = script_hash(script); - hash_hex_rev(&h) + rbitcoin_primitives::scripthash_display_hex(script) } #[cfg(test)] diff --git a/crates/rbitcoin-primitives/src/hex.rs b/crates/rbitcoin-primitives/src/hex.rs index 1e94d1d6e7..79d3bc70a1 100644 --- a/crates/rbitcoin-primitives/src/hex.rs +++ b/crates/rbitcoin-primitives/src/hex.rs @@ -8,6 +8,7 @@ //! //! Errors are hex-conservative's own. +use bitcoin_hashes::{sha256, Hash}; use hex_conservative::{DisplayHex, FromHex}; pub use hex_conservative::{HexToArrayError, HexToBytesError}; @@ -40,6 +41,15 @@ pub fn display_hash_hex(mut h: [u8; 32]) -> String { encode(h) } +/// Electrum / Esplora scripthash: SHA256(scriptPubKey) in display order. +/// +/// `sha256::Hash`'s `Display` prints internal byte order. `Txid` display +/// does not. This is the spelling a client sends. +#[inline] +pub fn scripthash_display_hex(script: &[u8]) -> String { + display_hash_hex(sha256::Hash::hash(script).to_byte_array()) +} + /// Parse display-order 32-byte hex → internal byte order. /// /// `0x` / `0X` is stripped here. Any other failure is [`HexToArrayError`]: @@ -55,6 +65,7 @@ pub fn parse_display_hash32(hex: &str) -> Result<[u8; 32], HexToArrayError> { #[cfg(test)] mod tests { use super::*; + use bitcoin_hashes::{sha256, Hash}; #[test] fn roundtrip() { @@ -81,6 +92,20 @@ mod tests { ); } + #[test] + fn p2a_scripthash_display_is_reversed_sha256_not_hash_display() { + // bc1pfeessrawgf. sha256::Hash Display is internal order. + let spk = [0x51, 0x02, 0x4e, 0x73]; + let got = scripthash_display_hex(&spk); + assert_eq!( + got, + "c5d0fb3863474a90cfe5b26801e66cb45fdc046b40e6829788eaf1b8770ffea6" + ); + let digest = sha256::Hash::hash(&spk); + assert_ne!(got, digest.to_string()); + assert_eq!(got, display_hash_hex(digest.to_byte_array())); + } + #[test] fn display_hash_roundtrips_known_internal_bytes() { let mut internal = [0u8; 32]; diff --git a/crates/rbitcoin-primitives/src/lib.rs b/crates/rbitcoin-primitives/src/lib.rs index 92b923a88b..b906fc7425 100644 --- a/crates/rbitcoin-primitives/src/lib.rs +++ b/crates/rbitcoin-primitives/src/lib.rs @@ -17,7 +17,7 @@ pub use compact::{ }; pub use hex::{ decode as hex_decode, display_hash_hex, encode as hex_encode, parse_display_hash32, - HexToArrayError, HexToBytesError, + scripthash_display_hex, HexToArrayError, HexToBytesError, }; pub use loc_simd::{deinterleave_pairs_u8x8, inclusive_u8x8_times_8}; pub use median_time::median_time_past_times;