mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-11 11:08:13 -07:00
website_next: move hash logic to clients
This commit is contained in:
@@ -688,6 +688,160 @@ const _p = (prefix, acc) => acc ? `${{prefix}}_${{acc}}` : prefix;
|
||||
"#
|
||||
)
|
||||
.unwrap();
|
||||
output.push_str(r##"
|
||||
const _MASK_64 = 0xffffffffffffffffn;
|
||||
const _RAPIDHASH_SECRETS = /** @type {const} */ ([
|
||||
0x2d358dccaa6c78a5n,
|
||||
0x8bb84b93962eacc9n,
|
||||
0x4b33a62ed433d4a3n,
|
||||
0x4d5a2da51de1aa47n,
|
||||
0xa0761d6478bd642fn,
|
||||
0xe7037ed1a0b428dbn,
|
||||
0x90ed1765281c388cn,
|
||||
]);
|
||||
const _RAPIDHASH_SEED = _rapidHashSeed(0n);
|
||||
|
||||
/** @param {bigint} value */
|
||||
function _u64(value) {
|
||||
return value & _MASK_64;
|
||||
}
|
||||
|
||||
/** @param {bigint} left @param {bigint} right */
|
||||
function _rapidMix(left, right) {
|
||||
const result = _u64(left) * _u64(right);
|
||||
return _u64(result) ^ _u64(result >> 64n);
|
||||
}
|
||||
|
||||
/** @param {bigint} left @param {bigint} right @returns {[bigint, bigint]} */
|
||||
function _rapidMum(left, right) {
|
||||
const result = _u64(left) * _u64(right);
|
||||
return [_u64(result), _u64(result >> 64n)];
|
||||
}
|
||||
|
||||
/** @param {bigint} seed */
|
||||
function _rapidHashSeed(seed) {
|
||||
return _u64(seed ^ _rapidMix(seed ^ _RAPIDHASH_SECRETS[2], _RAPIDHASH_SECRETS[1]));
|
||||
}
|
||||
|
||||
/** @param {Uint8Array} bytes @param {number} offset */
|
||||
function _readU32(bytes, offset) {
|
||||
return (
|
||||
BigInt(bytes[offset]) |
|
||||
(BigInt(bytes[offset + 1]) << 8n) |
|
||||
(BigInt(bytes[offset + 2]) << 16n) |
|
||||
(BigInt(bytes[offset + 3]) << 24n)
|
||||
);
|
||||
}
|
||||
|
||||
/** @param {Uint8Array} bytes @param {number} offset */
|
||||
function _readU64(bytes, offset) {
|
||||
return _readU32(bytes, offset) | (_readU32(bytes, offset + 4) << 32n);
|
||||
}
|
||||
|
||||
/** @param {Uint8Array | ArrayBuffer | ArrayBufferView | number[]} payload */
|
||||
function _asUint8Array(payload) {
|
||||
if (payload instanceof Uint8Array) return payload;
|
||||
if (payload instanceof ArrayBuffer) return new Uint8Array(payload);
|
||||
if (ArrayBuffer.isView(payload)) return new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength);
|
||||
if (Array.isArray(payload)) return new Uint8Array(payload);
|
||||
throw new Error("Expected address payload bytes");
|
||||
}
|
||||
|
||||
/** @param {Uint8Array | ArrayBuffer | ArrayBufferView | number[]} payload */
|
||||
function _rapidHashV3(payload) {
|
||||
const bytes = _asUint8Array(payload);
|
||||
const length = bytes.length;
|
||||
if (length === 0) throw new Error("Expected a non-empty address payload");
|
||||
if (length > 65) throw new Error("Expected at most 65 address payload bytes");
|
||||
|
||||
let seed = _RAPIDHASH_SEED;
|
||||
let a = 0n;
|
||||
let b = 0n;
|
||||
let remainder;
|
||||
|
||||
if (length <= 16) {
|
||||
if (length >= 4) {
|
||||
seed ^= BigInt(length);
|
||||
if (length >= 8) {
|
||||
a ^= _readU64(bytes, 0);
|
||||
b ^= _readU64(bytes, length - 8);
|
||||
} else {
|
||||
a ^= _readU32(bytes, 0);
|
||||
b ^= _readU32(bytes, length - 4);
|
||||
}
|
||||
} else if (length > 0) {
|
||||
a ^= (BigInt(bytes[0]) << 45n) | BigInt(bytes[length - 1]);
|
||||
b ^= BigInt(bytes[length >> 1]);
|
||||
}
|
||||
remainder = BigInt(length);
|
||||
} else {
|
||||
seed = _rapidMix(_readU64(bytes, 0) ^ _RAPIDHASH_SECRETS[2], _readU64(bytes, 8) ^ seed);
|
||||
if (length > 32) {
|
||||
seed = _rapidMix(_readU64(bytes, 16) ^ _RAPIDHASH_SECRETS[2], _readU64(bytes, 24) ^ seed);
|
||||
if (length > 48) {
|
||||
seed = _rapidMix(_readU64(bytes, 32) ^ _RAPIDHASH_SECRETS[1], _readU64(bytes, 40) ^ seed);
|
||||
if (length > 64) {
|
||||
seed = _rapidMix(_readU64(bytes, 48) ^ _RAPIDHASH_SECRETS[1], _readU64(bytes, 56) ^ seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
remainder = BigInt(length);
|
||||
a ^= _readU64(bytes, length - 16) ^ remainder;
|
||||
b ^= _readU64(bytes, length - 8);
|
||||
}
|
||||
|
||||
a ^= _RAPIDHASH_SECRETS[1];
|
||||
b ^= seed;
|
||||
[a, b] = _rapidMum(a, b);
|
||||
return _rapidMix(a ^ 0xaaaaaaaaaaaaaaaan, b ^ _RAPIDHASH_SECRETS[1] ^ remainder);
|
||||
}
|
||||
|
||||
/** @param {number} nibbles */
|
||||
function _validateHashPrefixNibbles(nibbles) {
|
||||
if (!Number.isInteger(nibbles) || nibbles < 1 || nibbles > 16) {
|
||||
throw new Error("Expected hash-prefix length from 1 to 16 hex nibbles");
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {OutputType} addrType @returns {number[]} */
|
||||
function _addressPayloadLengths(addrType) {
|
||||
switch (addrType) {
|
||||
case "p2a": return [2];
|
||||
case "p2pk": return [33, 65];
|
||||
case "p2pkh":
|
||||
case "p2sh":
|
||||
case "v0_p2wpkh": return [20];
|
||||
case "v0_p2wsh":
|
||||
case "v1_p2tr": return [32];
|
||||
default:
|
||||
throw new Error(`Unsupported address type for address payload hash-prefix: ${addrType}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {OutputType} addrType
|
||||
* @param {Uint8Array | ArrayBuffer | ArrayBufferView | number[]} payload
|
||||
*/
|
||||
function _validateAddressPayloadForType(addrType, payload) {
|
||||
const length = _asUint8Array(payload).length;
|
||||
const expected = _addressPayloadLengths(addrType);
|
||||
if (!expected.includes(length)) {
|
||||
throw new Error(`Expected ${addrType} address payload length ${expected.join(" or ")} bytes`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the RapidHash v3 hash-prefix used by `/api/address/hash-prefix/{addr_type}/{prefix}`.
|
||||
* @param {Uint8Array | ArrayBuffer | ArrayBufferView | number[]} payload - Raw address payload bytes
|
||||
* @param {number} nibbles - Prefix length from 1 to 16 hex nibbles
|
||||
* @returns {string}
|
||||
*/
|
||||
function addressPayloadHashPrefix(payload, nibbles) {
|
||||
_validateHashPrefixNibbles(nibbles);
|
||||
return _rapidHashV3(payload).toString(16).padStart(16, "0").slice(0, nibbles);
|
||||
}
|
||||
|
||||
"##);
|
||||
}
|
||||
|
||||
/// Generate static constants for the BrkClient class.
|
||||
|
||||
@@ -111,6 +111,32 @@ pub fn generate_main_client(
|
||||
writeln!(output, " this.series = this._buildTree();").unwrap();
|
||||
writeln!(output, " }}\n").unwrap();
|
||||
|
||||
output.push_str(r##" /**
|
||||
* Compute the RapidHash v3 hash-prefix for raw address payload bytes.
|
||||
* @param {Uint8Array | ArrayBuffer | ArrayBufferView | number[]} payload
|
||||
* @param {number} nibbles
|
||||
* @returns {string}
|
||||
*/
|
||||
static addressPayloadHashPrefix(payload, nibbles) {
|
||||
return addressPayloadHashPrefix(payload, nibbles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch address hash-prefix matches from raw address payload bytes.
|
||||
* @param {OutputType} addrType
|
||||
* @param {Uint8Array | ArrayBuffer | ArrayBufferView | number[]} payload - Raw payload bytes matching addrType length
|
||||
* @param {number} nibbles
|
||||
* @param {{ signal?: AbortSignal, onValue?: (value: AddrHashPrefixMatches) => void, cache?: boolean }} [options]
|
||||
* @returns {Promise<AddrHashPrefixMatches>}
|
||||
*/
|
||||
getAddressPayloadHashPrefixMatches(addrType, payload, nibbles, options = {}) {
|
||||
_validateAddressPayloadForType(addrType, payload);
|
||||
const prefix = addressPayloadHashPrefix(payload, nibbles);
|
||||
return this.getAddressHashPrefixMatches(addrType, prefix, options);
|
||||
}
|
||||
|
||||
"##);
|
||||
|
||||
writeln!(output, " /**").unwrap();
|
||||
writeln!(output, " * @private").unwrap();
|
||||
writeln!(output, " * @returns {{SeriesTree}}").unwrap();
|
||||
@@ -161,7 +187,11 @@ pub fn generate_main_client(
|
||||
|
||||
writeln!(output, "}}\n").unwrap();
|
||||
|
||||
writeln!(output, "export {{ BrkClient, BrkError }};").unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
"export {{ BrkClient, BrkError, addressPayloadHashPrefix }};"
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
||||
@@ -84,6 +84,23 @@ pub fn generate_main_client(output: &mut String, endpoints: &[Endpoint]) {
|
||||
.unwrap();
|
||||
writeln!(output, " return _date_to_index(index, d)").unwrap();
|
||||
writeln!(output).unwrap();
|
||||
|
||||
output.push_str(r#" @staticmethod
|
||||
def address_payload_hash_prefix(payload: Union[bytes, bytearray, memoryview], nibbles: int) -> str:
|
||||
"""Compute the RapidHash v3 hash-prefix for raw address payload bytes."""
|
||||
return address_payload_hash_prefix(payload, nibbles)
|
||||
|
||||
def get_address_payload_hash_prefix_matches(
|
||||
self,
|
||||
addr_type: OutputType,
|
||||
payload: Union[bytes, bytearray, memoryview],
|
||||
nibbles: int,
|
||||
) -> AddrHashPrefixMatches:
|
||||
"""Fetch address hash-prefix matches from raw payload bytes matching addr_type length."""
|
||||
_validate_address_payload_for_type(addr_type, payload)
|
||||
return self.get_address_hash_prefix_matches(addr_type, address_payload_hash_prefix(payload, nibbles))
|
||||
|
||||
"#);
|
||||
// Generate API methods
|
||||
generate_api_methods(output, endpoints);
|
||||
}
|
||||
|
||||
@@ -147,6 +147,125 @@ def _p(prefix: str, acc: str) -> str:
|
||||
"#
|
||||
)
|
||||
.unwrap();
|
||||
output.push_str(r#"
|
||||
_MASK_64 = (1 << 64) - 1
|
||||
_RAPIDHASH_SECRETS = (
|
||||
0x2d358dccaa6c78a5,
|
||||
0x8bb84b93962eacc9,
|
||||
0x4b33a62ed433d4a3,
|
||||
0x4d5a2da51de1aa47,
|
||||
0xa0761d6478bd642f,
|
||||
0xe7037ed1a0b428db,
|
||||
0x90ed1765281c388c,
|
||||
)
|
||||
_RAPIDHASH_SEED = 0
|
||||
|
||||
|
||||
def _u64(value: int) -> int:
|
||||
return value & _MASK_64
|
||||
|
||||
|
||||
def _rapid_mix(left: int, right: int) -> int:
|
||||
result = _u64(left) * _u64(right)
|
||||
return _u64(result) ^ _u64(result >> 64)
|
||||
|
||||
|
||||
def _rapid_mum(left: int, right: int) -> Tuple[int, int]:
|
||||
result = _u64(left) * _u64(right)
|
||||
return _u64(result), _u64(result >> 64)
|
||||
|
||||
|
||||
def _rapid_hash_seed(seed: int) -> int:
|
||||
return _u64(seed ^ _rapid_mix(seed ^ _RAPIDHASH_SECRETS[2], _RAPIDHASH_SECRETS[1]))
|
||||
|
||||
|
||||
_RAPIDHASH_SEED = _rapid_hash_seed(0)
|
||||
|
||||
|
||||
def _read_u32(data: bytes, offset: int) -> int:
|
||||
return int.from_bytes(data[offset:offset + 4], "little")
|
||||
|
||||
|
||||
def _read_u64(data: bytes, offset: int) -> int:
|
||||
return int.from_bytes(data[offset:offset + 8], "little")
|
||||
|
||||
|
||||
def _rapid_hash_v3(payload: Union[bytes, bytearray, memoryview]) -> int:
|
||||
data = bytes(payload)
|
||||
length = len(data)
|
||||
if length == 0:
|
||||
raise ValueError("Expected a non-empty address payload")
|
||||
if length > 65:
|
||||
raise ValueError("Expected at most 65 address payload bytes")
|
||||
|
||||
seed = _RAPIDHASH_SEED
|
||||
a = 0
|
||||
b = 0
|
||||
|
||||
if length <= 16:
|
||||
if length >= 4:
|
||||
seed ^= length
|
||||
if length >= 8:
|
||||
a ^= _read_u64(data, 0)
|
||||
b ^= _read_u64(data, length - 8)
|
||||
else:
|
||||
a ^= _read_u32(data, 0)
|
||||
b ^= _read_u32(data, length - 4)
|
||||
elif length > 0:
|
||||
a ^= (data[0] << 45) | data[length - 1]
|
||||
b ^= data[length >> 1]
|
||||
remainder = length
|
||||
else:
|
||||
if length > 16:
|
||||
seed = _rapid_mix(_read_u64(data, 0) ^ _RAPIDHASH_SECRETS[2], _read_u64(data, 8) ^ seed)
|
||||
if length > 32:
|
||||
seed = _rapid_mix(_read_u64(data, 16) ^ _RAPIDHASH_SECRETS[2], _read_u64(data, 24) ^ seed)
|
||||
if length > 48:
|
||||
seed = _rapid_mix(_read_u64(data, 32) ^ _RAPIDHASH_SECRETS[1], _read_u64(data, 40) ^ seed)
|
||||
if length > 64:
|
||||
seed = _rapid_mix(_read_u64(data, 48) ^ _RAPIDHASH_SECRETS[1], _read_u64(data, 56) ^ seed)
|
||||
remainder = length
|
||||
a ^= _read_u64(data, length - 16) ^ remainder
|
||||
b ^= _read_u64(data, length - 8)
|
||||
|
||||
a ^= _RAPIDHASH_SECRETS[1]
|
||||
b ^= seed
|
||||
a, b = _rapid_mum(a, b)
|
||||
return _rapid_mix(a ^ 0xaaaaaaaaaaaaaaaa, b ^ _RAPIDHASH_SECRETS[1] ^ remainder)
|
||||
|
||||
|
||||
def _validate_hash_prefix_nibbles(nibbles: int) -> None:
|
||||
if isinstance(nibbles, bool) or not isinstance(nibbles, int) or nibbles < 1 or nibbles > 16:
|
||||
raise ValueError("Expected hash-prefix length from 1 to 16 hex nibbles")
|
||||
|
||||
|
||||
def _address_payload_lengths(addr_type: OutputType) -> Tuple[int, ...]:
|
||||
if addr_type == "p2a":
|
||||
return (2,)
|
||||
if addr_type == "p2pk":
|
||||
return (33, 65)
|
||||
if addr_type in ("p2pkh", "p2sh", "v0_p2wpkh"):
|
||||
return (20,)
|
||||
if addr_type in ("v0_p2wsh", "v1_p2tr"):
|
||||
return (32,)
|
||||
raise ValueError(f"Unsupported address type for address payload hash-prefix: {addr_type}")
|
||||
|
||||
|
||||
def _validate_address_payload_for_type(addr_type: OutputType, payload: Union[bytes, bytearray, memoryview]) -> None:
|
||||
length = len(bytes(payload))
|
||||
expected = _address_payload_lengths(addr_type)
|
||||
if length not in expected:
|
||||
joined = " or ".join(str(value) for value in expected)
|
||||
raise ValueError(f"Expected {addr_type} address payload length {joined} bytes")
|
||||
|
||||
|
||||
def address_payload_hash_prefix(payload: Union[bytes, bytearray, memoryview], nibbles: int) -> str:
|
||||
"""Compute the RapidHash v3 hash-prefix used by `/api/address/hash-prefix/{addr_type}/{prefix}`."""
|
||||
_validate_hash_prefix_nibbles(nibbles)
|
||||
return f"{_rapid_hash_v3(payload):016x}"[:nibbles]
|
||||
|
||||
|
||||
"#);
|
||||
}
|
||||
|
||||
/// Generate the SeriesData and SeriesEndpoint classes
|
||||
|
||||
@@ -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 {{
|
||||
|
||||
Reference in New Issue
Block a user