use crate::hash::{blake2b_256, tagged_hash, Digest32};
use crate::signet::{self, BLOCK_DATA_LEN};
use crate::stock::{arr16, arr32, u16_at, u32_at};
use crate::{BlockHash, Error, HeaderFamily, Target, VERSION_HEADER_V2_FLAG};
pub const FLAG_USE_TIME_OFFSET: u8 = 4;
pub const FLAG_ASIC_PROFILE_MASK: u8 = 0x03;
pub const FLAG_RESERVED_MASK: u8 = 0xc0;
pub const POW_HASH_NAME: &str = "knots:blake2b-v2";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Blake2bV2Header {
pub version: u32,
pub prev_block_hash: BlockHash,
pub merkle_root: [u8; 32],
pub time_on_wire: u32,
pub bits: u32,
pub nonce: u32,
pub nonce2: u32,
pub nonce3: u32,
pub extranonce: [u8; 16],
pub time_offset: u32,
pub tx_count: u16,
pub flags: u8,
pub xor_key_mask_clear_bits: u8,
pub xor_key: [u8; 16],
pub height: u32,
pub mm_rhs: [u8; 32],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct V2HashStages {
pub xor_key_hash: Digest32,
pub h1: Digest32,
pub h2: Digest32,
pub blake2b_1: Digest32,
pub blake2b_2: Digest32,
pub mask: Digest32,
pub asic_profile: u8,
pub hash: BlockHash,
}
impl Blake2bV2Header {
pub const WIRE_SIZE: usize = 164;
pub fn decode(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() != Self::WIRE_SIZE {
return Err(Error::WrongLength {
family: HeaderFamily::Blake2bV2,
expected: Self::WIRE_SIZE,
actual: bytes.len(),
});
}
let version = u32_at(bytes, 0);
if version & VERSION_HEADER_V2_FLAG == 0 {
return Err(Error::VersionBit31Clear);
}
Ok(Blake2bV2Header {
version,
prev_block_hash: BlockHash::from_wire(arr32(bytes, 4)),
merkle_root: arr32(bytes, 36),
time_on_wire: u32_at(bytes, 68),
bits: u32_at(bytes, 72),
nonce: u32_at(bytes, 76),
nonce2: u32_at(bytes, 80),
nonce3: u32_at(bytes, 84),
extranonce: arr16(bytes, 88),
time_offset: u32_at(bytes, 104),
tx_count: u16_at(bytes, 108),
flags: bytes[110],
xor_key_mask_clear_bits: bytes[111],
xor_key: arr16(bytes, 112),
height: u32_at(bytes, 128),
mm_rhs: arr32(bytes, 132),
})
}
pub fn encode(&self) -> [u8; 164] {
let mut out = [0u8; 164];
out[..72].copy_from_slice(&self.signet_preimage(self.merkle_root));
out[72..76].copy_from_slice(&self.bits.to_le_bytes());
out[76..80].copy_from_slice(&self.nonce.to_le_bytes());
out[80..84].copy_from_slice(&self.nonce2.to_le_bytes());
out[84..88].copy_from_slice(&self.nonce3.to_le_bytes());
out[88..104].copy_from_slice(&self.extranonce);
out[104..108].copy_from_slice(&self.time_offset.to_le_bytes());
out[108..110].copy_from_slice(&self.tx_count.to_le_bytes());
out[110] = self.flags;
out[111] = self.xor_key_mask_clear_bits;
out[112..128].copy_from_slice(&self.xor_key);
out[128..132].copy_from_slice(&self.height.to_le_bytes());
out[132..].copy_from_slice(&self.mm_rhs);
out
}
pub fn time(&self) -> u32 {
if self.uses_time_offset() {
self.time_on_wire.wrapping_add(self.time_offset)
} else {
self.time_on_wire
}
}
pub fn uses_time_offset(&self) -> bool {
self.flags & FLAG_USE_TIME_OFFSET != 0
}
pub fn asic_profile(&self) -> u8 {
self.flags & FLAG_ASIC_PROFILE_MASK
}
pub fn check_flags(&self) -> Result<(), Error> {
if self.flags & FLAG_RESERVED_MASK != 0 {
Err(Error::ReservedFlags(self.flags))
} else {
Ok(())
}
}
pub fn hash(&self) -> BlockHash {
self.hash_stages().hash
}
pub fn hash_stages(&self) -> V2HashStages {
let prev_display = self.prev_block_hash.0;
let xor_key_hash = tagged_hash(b"Bitcoin block hash PoW XOR key", &[&self.xor_key]);
let mut mask = [0u8; 32];
if self.xor_key.iter().any(|&b| b != 0) {
mask = tagged_hash(b"Bitcoin block hash PoW XOR mask", &[&self.xor_key]);
let clear_bytes = usize::from(self.xor_key_mask_clear_bits >> 3);
let n = clear_bytes.min(32);
mask[..n].fill(0);
if clear_bytes < 32 {
mask[clear_bytes] &= 0xff >> (self.xor_key_mask_clear_bits & 7);
}
}
let prev_hidden = tagged_hash(b"Bitcoin prevblock header, hashed", &[&prev_display]);
let h1 = tagged_hash(
b"Bitcoin block header 1",
&[
&self.version.to_le_bytes(),
&prev_display,
&self.height.to_le_bytes(),
&self.merkle_root,
&self.time_on_wire.to_le_bytes(),
&[0u8],
&self.bits.to_le_bytes(),
&u32::from(self.tx_count).to_le_bytes(),
&[self.flags, self.xor_key_mask_clear_bits],
&xor_key_hash,
],
);
let zeros16 = [0u8; 16];
let h2 = tagged_hash(
b"Merge-mining hook",
&[&h1, &zeros16, &zeros16, &self.mm_rhs],
);
let b1 = blake2b_256(&[&0u32.to_le_bytes(), &h2, &self.extranonce]);
let nonce = self.nonce.to_le_bytes();
let nonce2 = self.nonce2.to_le_bytes();
let nonce3 = self.nonce3.to_le_bytes();
let offset = self.time_offset.to_le_bytes();
let asic_profile = self.asic_profile();
let b2 = match asic_profile {
0 => {
let mut p = prev_hidden;
p[..6].fill(0);
blake2b_256(&[&p, &nonce, &nonce2, &offset, &nonce3, &b1])
}
1 => blake2b_256(&[&nonce, &nonce2, &nonce3, &offset, &b1, &h2]),
2 => blake2b_256(&[&[0u8; 48], &h2, &nonce, &nonce2, &offset, &nonce3, &b1]),
_ => blake2b_256(&[&[0u8; 80], &h2, &nonce, &nonce2, &offset, &nonce3, &b1]),
};
let mut hash = [0u8; 32];
for i in 0..32 {
hash[i] = b2[i] ^ mask[i];
}
V2HashStages {
xor_key_hash,
h1,
h2,
blake2b_1: b1,
blake2b_2: b2,
mask,
asic_profile,
hash: BlockHash(hash),
}
}
pub fn target(&self) -> Result<Target, Error> {
Target::from_compact(self.bits)
}
pub fn meets_target(&self) -> bool {
self.target().is_ok_and(|t| self.hash().meets(&t))
}
pub fn check_pow(&self, pow_limit: &Target) -> Result<(), Error> {
crate::check_pow(self.bits, self.hash(), pow_limit)
}
pub fn signet_preimage(&self, stripped_merkle_root: [u8; 32]) -> [u8; BLOCK_DATA_LEN] {
let mut out = [0u8; BLOCK_DATA_LEN];
out[..4].copy_from_slice(&self.version.to_le_bytes());
out[4..36].copy_from_slice(&self.prev_block_hash.to_wire());
out[36..68].copy_from_slice(&stripped_merkle_root);
out[68..].copy_from_slice(&self.time_on_wire.to_le_bytes());
out
}
pub fn block_data(&self, stripped_merkle_root: [u8; 32]) -> [u8; 32] {
signet::block_data(&self.signet_preimage(stripped_merkle_root))
}
}