website_next: move hash logic to clients

This commit is contained in:
nym21
2026-07-01 17:04:29 +02:00
parent 5d83ee4d70
commit 153fcdf4e0
33 changed files with 1206 additions and 189 deletions
@@ -76,6 +76,36 @@ impl BrkClient {{
)
.unwrap();
output.push_str(r#" /// Decode a mainnet Bitcoin address into the BRK address type and raw payload bytes.
pub fn decode_address_payload(address: &str) -> Result<AddressPayload> {
decode_address_payload(address)
}
/// Compute the RapidHash v3 hash-prefix for raw address payload bytes.
pub fn address_payload_hash_prefix(payload: &[u8], nibbles: usize) -> Result<String> {
address_payload_hash_prefix(payload, nibbles)
}
/// Decode a mainnet Bitcoin address and compute its hash prefix.
pub fn address_hash_prefix(address: &str, nibbles: usize) -> Result<AddressHashPrefix> {
address_hash_prefix(address, nibbles)
}
/// Fetch address hash-prefix matches from raw payload bytes matching `addr_type` length.
pub fn get_address_payload_hash_prefix_matches(&self, addr_type: OutputType, payload: &[u8], nibbles: usize) -> Result<AddrHashPrefixMatches> {
validate_address_payload_for_type(addr_type, payload)?;
let prefix = address_payload_hash_prefix(payload, nibbles)?;
self.get_address_hash_prefix_matches(addr_type, &prefix)
}
/// Fetch address hash-prefix matches for a mainnet Bitcoin address.
pub fn get_address_hash_prefix_matches_for_address(&self, address: &str, nibbles: usize) -> Result<AddrHashPrefixMatches> {
let hashed = address_hash_prefix(address, nibbles)?;
self.get_address_hash_prefix_matches(hashed.addr_type, &hashed.prefix)
}
"#);
generate_api_methods(output, endpoints);
writeln!(output, "}}").unwrap();
@@ -118,6 +148,23 @@ fn generate_get_method(output: &mut String, endpoint: &Endpoint) {
"get_text"
};
if endpoint.path == "/api/address/hash-prefix/{addr_type}/{prefix}" {
writeln!(
output,
" let addr_type = address_payload_type_path(addr_type)?;"
)
.unwrap();
writeln!(
output,
" self.base.{}(&format!(\"{}\"{}))",
fetch_method, path, index_arg
)
.unwrap();
writeln!(output, " }}").unwrap();
writeln!(output).unwrap();
return;
}
if endpoint.query_params.is_empty() {
writeln!(
output,
@@ -11,7 +11,8 @@ use crate::{
pub fn generate_imports(output: &mut String) {
writeln!(
output,
r#"use std::sync::Arc;
r#"use std::str::FromStr;
use std::sync::Arc;
use std::ops::{{Bound, RangeBounds}};
use serde::de::DeserializeOwned;
pub use brk_cohort::*;
@@ -43,6 +44,97 @@ impl std::error::Error for BrkError {{}}
/// Result type for BRK client operations.
pub type Result<T> = std::result::Result<T, BrkError>;
/// BRK address type and raw payload bytes used by the hash-prefix index.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddressPayload {{
pub addr_type: OutputType,
pub payload: Vec<u8>,
}}
/// BRK address type and leading hex nibbles of the address-payload hash.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddressHashPrefix {{
pub addr_type: OutputType,
pub prefix: String,
}}
/// Compute the RapidHash v3 hash-prefix used by `/api/address/hash-prefix/{{addr_type}}/{{prefix}}`.
pub fn address_payload_hash_prefix(payload: &[u8], nibbles: usize) -> Result<String> {{
if payload.is_empty() {{
return Err(BrkError {{ message: "Expected a non-empty address payload".to_string() }});
}}
if payload.len() > 65 {{
return Err(BrkError {{ message: "Expected at most 65 address payload bytes".to_string() }});
}}
if !(1..=16).contains(&nibbles) {{
return Err(BrkError {{ message: "Expected hash-prefix length from 1 to 16 hex nibbles".to_string() }});
}}
Ok(format!("{{:016x}}", rapidhash::v3::rapidhash_v3(payload))[..nibbles].to_string())
}}
fn validate_address_payload_for_type(addr_type: OutputType, payload: &[u8]) -> Result<()> {{
let expected: &[usize] = match addr_type {{
OutputType::P2A => &[2],
OutputType::P2PK33 => &[33],
OutputType::P2PK65 => &[65],
OutputType::P2PKH | OutputType::P2SH | OutputType::P2WPKH => &[20],
OutputType::P2WSH | OutputType::P2TR => &[32],
OutputType::P2MS | OutputType::OpReturn | OutputType::Empty | OutputType::Unknown => {{
return Err(BrkError {{ message: format!("Unsupported address type for address payload hash-prefix: {{addr_type:?}}") }});
}},
}};
let addr_type = address_payload_type_path(addr_type)?;
if !expected.contains(&payload.len()) {{
let joined = expected
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(" or ");
return Err(BrkError {{ message: format!("Expected {{addr_type}} address payload length {{joined}} bytes") }});
}}
Ok(())
}}
fn address_payload_type_path(addr_type: OutputType) -> Result<&'static str> {{
match addr_type {{
OutputType::P2A => Ok("p2a"),
OutputType::P2PK33 | OutputType::P2PK65 => Ok("p2pk"),
OutputType::P2PKH => Ok("p2pkh"),
OutputType::P2SH => Ok("p2sh"),
OutputType::P2WPKH => Ok("v0_p2wpkh"),
OutputType::P2WSH => Ok("v0_p2wsh"),
OutputType::P2TR => Ok("v1_p2tr"),
OutputType::P2MS | OutputType::OpReturn | OutputType::Empty | OutputType::Unknown => {{
Err(BrkError {{ message: format!("Unsupported address type for address payload hash-prefix: {{addr_type:?}}") }})
}},
}}
}}
/// Decode a mainnet Bitcoin address into the BRK address type and raw payload bytes.
pub fn decode_address_payload(address: &str) -> Result<AddressPayload> {{
if address.is_empty() {{
return Err(BrkError {{ message: "Expected an address string".to_string() }});
}}
let addr_bytes = AddrBytes::from_str(address).map_err(|e| BrkError {{ message: e.to_string() }})?;
let addr_type = OutputType::from(&addr_bytes);
Ok(AddressPayload {{
addr_type,
payload: addr_bytes.as_slice().to_vec(),
}})
}}
/// Decode a mainnet Bitcoin address and compute its hash prefix.
pub fn address_hash_prefix(address: &str, nibbles: usize) -> Result<AddressHashPrefix> {{
let decoded = decode_address_payload(address)?;
Ok(AddressHashPrefix {{
addr_type: decoded.addr_type,
prefix: address_payload_hash_prefix(&decoded.payload, nibbles)?,
}})
}}
/// Options for configuring the BRK client.
#[derive(Debug, Clone)]
pub struct BrkClientOptions {{