use serde::{Deserialize, Serialize};
use crate::hash::{sha256d, Hash32};
pub const MM_MAGIC: [u8; 4] = [0xfa, 0xbe, b'm', b'm'];
pub const PARENT_ALGOS: [&str; 3] = ["sha256d", "scrypt", "equihash"];
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct AuxPow {
pub parent_algo: String,
pub parent_header: Vec<u8>,
pub parent_coinbase: Vec<u8>,
pub coinbase_branch: Vec<Hash32>,
pub chain_branch: Vec<Hash32>,
pub chain_index: u32,
}
impl AuxPow {
pub fn serialized_size(&self) -> usize {
self.parent_header.len()
+ self.parent_coinbase.len()
+ 32 * (self.coinbase_branch.len() + self.chain_branch.len())
+ 64
}
}
pub fn aux_slot(chain_id: u32, tree_size: u32, nonce: u32) -> u32 {
let mut rand = nonce;
rand = rand.wrapping_mul(1103515245).wrapping_add(12345);
rand = rand.wrapping_add(chain_id);
rand = rand.wrapping_mul(1103515245).wrapping_add(12345);
rand % tree_size.max(1)
}
pub fn fold_branch(leaf: Hash32, branch: &[Hash32], mut index: u32) -> Hash32 {
let mut acc = leaf;
for node in branch {
let mut d = [0u8; 64];
if index & 1 == 0 {
d[..32].copy_from_slice(&acc);
d[32..].copy_from_slice(node);
} else {
d[..32].copy_from_slice(node);
d[32..].copy_from_slice(&acc);
}
acc = sha256d(&d);
index /= 2;
}
acc
}
pub fn fold_coinbase_branch(txid: Hash32, branch: &[Hash32]) -> Hash32 {
let mut acc = txid;
for node in branch {
let mut d = [0u8; 64];
d[..32].copy_from_slice(&acc);
d[32..].copy_from_slice(node);
acc = sha256d(&d);
}
acc
}
pub fn mm_commitment(aux_root: &Hash32, tree_size: u32, nonce: u32) -> Vec<u8> {
let mut out = Vec::with_capacity(44);
out.extend_from_slice(&MM_MAGIC);
out.extend_from_slice(aux_root);
out.extend_from_slice(&tree_size.to_le_bytes());
out.extend_from_slice(&nonce.to_le_bytes());
out
}
pub fn find_commitment(coinbase: &[u8]) -> Option<(Hash32, u32, u32)> {
let pos = coinbase.windows(4).position(|w| w == MM_MAGIC)?;
if coinbase.len() < pos + 44 {
return None;
}
let root: Hash32 = coinbase[pos + 4..pos + 36].try_into().ok()?;
let size = u32::from_le_bytes(coinbase[pos + 36..pos + 40].try_into().ok()?);
let nonce = u32::from_le_bytes(coinbase[pos + 40..pos + 44].try_into().ok()?);
Some((root, size, nonce))
}
pub fn parent_merkle_root(header: &[u8]) -> Option<Hash32> {
header.get(36..68)?.try_into().ok()
}