use secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, XOnlyPublicKey};
use sha2::{Digest, Sha256};
use crate::{Error, Result};
#[derive(Clone)]
pub struct ScanKeys {
b_scan: SecretKey,
b_spend: PublicKey,
secp: Secp256k1<secp256k1::All>,
}
impl std::fmt::Debug for ScanKeys {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScanKeys")
.field("b_spend", &self.b_spend)
.finish_non_exhaustive()
}
}
impl ScanKeys {
pub fn from_secret_bytes(b_scan_bytes: &[u8], b_spend_bytes: &[u8]) -> Result<Self> {
let secp = Secp256k1::new();
let b_scan = SecretKey::from_slice(b_scan_bytes)
.map_err(|e| Error::Crypto(format!("invalid b_scan: {e}")))?;
let b_spend_sk = SecretKey::from_slice(b_spend_bytes)
.map_err(|e| Error::Crypto(format!("invalid b_spend: {e}")))?;
let b_spend = PublicKey::from_secret_key(&secp, &b_spend_sk);
Ok(Self {
b_scan,
b_spend,
secp,
})
}
#[must_use]
pub fn spend_pubkey(&self) -> &PublicKey {
&self.b_spend
}
pub fn derive_output(&self, tweak_bytes: &[u8; 32]) -> Result<SilentPaymentOutput> {
let tweak_scalar = Scalar::from_be_bytes(*tweak_bytes)
.map_err(|_| Error::Crypto("tweak scalar overflow".into()))?;
let b_scan_point = PublicKey::from_secret_key(&self.secp, &self.b_scan);
let shared_point = b_scan_point
.mul_tweak(&self.secp, &tweak_scalar)
.map_err(|e| Error::Crypto(format!("b_scan * tweak: {e}")))?;
let k = hash_shared_secret(&shared_point.serialize(), 0);
let k_sk = SecretKey::from_slice(&k)
.map_err(|e| Error::Crypto(format!("hash scalar invalid: {e}")))?;
let k_scalar = Scalar::from(k_sk);
let output_key = self
.b_spend
.add_exp_tweak(&self.secp, &k_scalar)
.map_err(|e| Error::Crypto(format!("B_spend + k·G: {e}")))?;
let (xonly, _parity) = output_key.x_only_public_key();
Ok(SilentPaymentOutput {
pubkey: xonly,
tweak: *tweak_bytes,
})
}
#[must_use]
#[allow(dead_code)]
pub(crate) fn b_scan_secret_bytes(&self) -> [u8; 32] {
self.b_scan.secret_bytes()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SilentPaymentOutput {
pub pubkey: XOnlyPublicKey,
pub tweak: [u8; 32],
}
impl SilentPaymentOutput {
#[must_use]
pub fn pubkey_hex(&self) -> String {
hex::encode(self.pubkey.serialize())
}
}
#[must_use]
pub fn tagged_hash(tag: &[u8], data: &[u8]) -> [u8; 32] {
let tag_hash: [u8; 32] = Sha256::digest(tag).into();
let mut h = Sha256::new();
h.update(tag_hash);
h.update(tag_hash);
h.update(data);
h.finalize().into()
}
#[must_use]
pub fn hash_shared_secret(serialised_point: &[u8], counter: u32) -> [u8; 32] {
let mut data = serialised_point.to_vec();
data.extend_from_slice(&counter.to_be_bytes());
tagged_hash(b"BIP0352/SharedSecret", &data)
}
#[must_use]
pub fn hash_inputs(outpoints_hash: &[u8; 32], a_sum_33: &[u8; 33]) -> [u8; 32] {
let mut data = Vec::with_capacity(65);
data.extend_from_slice(outpoints_hash.as_ref());
data.extend_from_slice(a_sum_33.as_ref());
tagged_hash(b"BIP0352/Inputs", &data)
}
#[must_use]
pub fn verify_merkle_proof(
leaf_data: &[u8],
leaf_index: u64,
siblings: &[[u8; 32]],
expected_root: &[u8; 32],
) -> bool {
let mut current = double_sha256(leaf_data);
let mut index = leaf_index;
for sibling in siblings {
let (left, right) = if index % 2 == 0 {
(¤t, sibling)
} else {
(sibling, ¤t)
};
let mut combined = [0u8; 64];
combined[..32].copy_from_slice(left);
combined[32..].copy_from_slice(right);
current = double_sha256(combined.as_ref());
index /= 2;
}
¤t == expected_root
}
#[must_use]
pub fn double_sha256(data: &[u8]) -> [u8; 32] {
let first: [u8; 32] = Sha256::digest(data).into();
Sha256::digest(first).into()
}
#[must_use]
pub fn build_merkle_tree(leaves: &[Vec<u8>]) -> (Vec<u8>, Vec<Vec<[u8; 32]>>) {
if leaves.is_empty() {
return (vec![0u8; 32], vec![]);
}
let n = leaves.len();
let leaf_hashes: Vec<[u8; 32]> = leaves.iter().map(|l| double_sha256(l)).collect();
let mut proofs: Vec<Vec<[u8; 32]>> = vec![vec![]; n];
let mut current_layer = leaf_hashes;
loop {
if current_layer.len() == 1 {
break;
}
let mut next_layer = Vec::with_capacity(current_layer.len().div_ceil(2));
let mut i = 0;
while i < current_layer.len() {
let left = current_layer[i];
let right = if i + 1 < current_layer.len() {
current_layer[i + 1]
} else {
current_layer[i] };
let mut combined = [0u8; 64];
combined[..32].copy_from_slice(&left);
combined[32..].copy_from_slice(&right);
next_layer.push(double_sha256(combined.as_ref()));
i += 2;
}
for (leaf_i, proof) in proofs.iter_mut().enumerate() {
let level_depth = proof.len();
let idx = leaf_i >> level_depth;
let sibling_idx = if idx % 2 == 0 { idx + 1 } else { idx - 1 };
let sibling = if sibling_idx < current_layer.len() {
current_layer[sibling_idx]
} else {
current_layer[idx]
};
proof.push(sibling);
}
current_layer = next_layer;
}
let root = current_layer[0].to_vec();
(root, proofs)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tagged_hash_known_vector() {
let point = [0x02u8; 33];
let h = hash_shared_secret(point.as_ref(), 0);
assert_ne!(h, [0u8; 32]);
assert_eq!(h, hash_shared_secret(point.as_ref(), 0));
}
#[test]
fn merkle_single_leaf() {
let leaves = vec![b"leaf0".to_vec()];
let (root, proofs) = build_merkle_tree(&leaves);
assert_eq!(root, double_sha256(b"leaf0" as &[u8]).to_vec());
let root_arr: &[u8; 32] = root.as_slice().try_into().unwrap();
assert!(verify_merkle_proof(
b"leaf0" as &[u8],
0,
&proofs[0],
root_arr
));
}
#[test]
fn merkle_two_leaves() {
let leaves = vec![b"leaf0".to_vec(), b"leaf1".to_vec()];
let (root, proofs) = build_merkle_tree(&leaves);
let root_arr: &[u8; 32] = root.as_slice().try_into().unwrap();
assert!(verify_merkle_proof(
b"leaf0" as &[u8],
0,
&proofs[0],
root_arr
));
assert!(verify_merkle_proof(
b"leaf1" as &[u8],
1,
&proofs[1],
root_arr
));
}
#[test]
fn derive_output_deterministic() {
let b_scan = [1u8; 32];
let b_spend = [2u8; 32];
let keys = ScanKeys::from_secret_bytes(b_scan.as_ref(), b_spend.as_ref()).unwrap();
let tweak = [3u8; 32];
let out1 = keys.derive_output(&tweak).unwrap();
let out2 = keys.derive_output(&tweak).unwrap();
assert_eq!(out1.pubkey, out2.pubkey);
}
#[test]
fn double_sha256_known() {
let empty: &[u8] = &[];
let h = double_sha256(empty);
assert_eq!(
hex::encode(h),
"5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456"
);
}
}