use crate::encode::decode_vector;
use crate::encode::encode_vector;
use crate::nexa::hash_types::BlockHash;
use crate::nexa::transaction::Transaction;
use crate::utilnumber::BitcoindVarInt;
use bitcoin_hashes::Hash;
use bitcoincash::consensus::Decodable;
use bitcoincash::consensus::Encodable;
use bitcoincash::hash_types::TxMerkleNode;
use bitcoincash::util::uint::Uint256;
use sha2::Digest;
use sha2::Sha256;
use crate::impl_nexa_consensus_encoding;
use std::mem::size_of_val;
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct BlockHeader {
pub prev_blockhash: BlockHash,
pub bits: u32,
pub ancestor: BlockHash,
pub merkle_root: TxMerkleNode,
pub tx_filter: Uint256,
pub time: u32,
pub height: BitcoindVarInt,
pub chain_work: Uint256,
pub size: u64,
pub tx_count: BitcoindVarInt,
pub fee_pool_amt: BitcoindVarInt,
pub utxo_commitment: Vec<u8>,
pub miner_data: Vec<u8>,
pub nonce: Vec<u8>,
}
impl_nexa_consensus_encoding!(
BlockHeader,
prev_blockhash,
bits,
ancestor,
merkle_root,
tx_filter,
time,
height,
chain_work,
size,
tx_count,
fee_pool_amt,
utxo_commitment,
miner_data,
nonce
);
fn sha256hash(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha256::new();
hasher.update(data);
let h = hasher.finalize();
h.to_vec()
}
impl BlockHeader {
pub fn block_hash_commitment(&self) -> Vec<u8> {
[self.mini_hash(), self.extended_hash()].concat()
}
pub fn block_hash(&self) -> BlockHash {
let hash = sha256hash(&self.block_hash_commitment());
BlockHash::from_slice(&hash).unwrap()
}
pub fn mini_header(&self) -> Vec<u8> {
let e = "in mem buffers don't error";
let mut buf = std::io::BufWriter::new(Vec::with_capacity(
size_of_val(&self.prev_blockhash) + size_of_val(&self.bits),
));
self.prev_blockhash.consensus_encode(&mut buf).expect(e);
self.bits.consensus_encode(&mut buf).expect(e);
buf.into_inner().expect(e)
}
pub fn mini_hash(&self) -> Vec<u8> {
sha256hash(&self.mini_header())
}
pub fn extended_header(&self) -> Vec<u8> {
let e = "in mem buffers don't error";
let mut buf = std::io::BufWriter::new(Vec::new());
self.ancestor.consensus_encode(&mut buf).expect(e);
self.tx_filter.consensus_encode(&mut buf).expect(e);
self.merkle_root.consensus_encode(&mut buf).expect(e);
self.time.consensus_encode(&mut buf).expect(e);
self.height.0.consensus_encode(&mut buf).expect(e);
self.chain_work.consensus_encode(&mut buf).expect(e);
self.size.consensus_encode(&mut buf).expect(e);
self.tx_count.0.consensus_encode(&mut buf).expect(e);
self.fee_pool_amt.0.consensus_encode(&mut buf).expect(e);
self.utxo_commitment.consensus_encode(&mut buf).expect(e);
self.miner_data.consensus_encode(&mut buf).expect(e);
self.nonce.consensus_encode(&mut buf).expect(e);
buf.into_inner().expect(e)
}
pub fn extended_hash(&self) -> Vec<u8> {
sha256hash(&self.extended_header())
}
pub fn height(&self) -> u64 {
self.height.0
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Block {
pub header: BlockHeader,
pub txdata: Vec<Transaction>,
}
impl Block {
pub fn block_hash(&self) -> BlockHash {
self.header.block_hash()
}
}
impl Encodable for Block {
fn consensus_encode<W: std::io::Write + ?Sized>(
&self,
w: &mut W,
) -> Result<usize, std::io::Error> {
let mut len = 0;
len += self.header.consensus_encode(w)?;
len += encode_vector(w, &self.txdata)?;
Ok(len)
}
}
impl Decodable for Block {
fn consensus_decode<R: std::io::Read + ?Sized>(
r: &mut R,
) -> Result<Self, bitcoincash::consensus::encode::Error> {
let header: BlockHeader = Decodable::consensus_decode(r)?;
let txdata: Vec<Transaction> = decode_vector(r)?;
Ok(Block { header, txdata })
}
}
#[cfg(test)]
mod test {
use super::*;
use bitcoincash::consensus::encode::deserialize;
use bitcoincash::consensus::encode::deserialize_partial;
use bitcoincash::consensus::serialize;
use bitcoincash::hashes::hex::ToHex;
#[test]
fn parse_nexa_header() {
let header_serialized = hex::decode("0000000000000000000000000000000000000000000000000000000000000000ffff7f20000000000000000000000000000000000000000000000000000000000000000049317c27d6893f423308ccd46e1f51ec0310892a49b8e6645e176e7235ba04c8000000000000000000000000000000000000000000000000000000000000000027ffee60000200000000000000000000000000000000000000000000000000000000000000e700000000000000010000000105").unwrap();
let header: BlockHeader = deserialize(&header_serialized).unwrap();
assert_eq!(header.prev_blockhash, BlockHash::all_zeros());
assert_eq!(header.bits, 545259519);
assert_eq!(header.ancestor, BlockHash::all_zeros());
assert_eq!(
header.merkle_root.to_hex(),
"c804ba35726e175e64e6b8492a891003ec511f6ed4cc0833423f89d6277c3149"
);
assert_eq!(header.tx_filter, Uint256::default());
assert_eq!(header.time, 1626275623);
assert_eq!(header.height.0, 0);
assert_eq!(header.chain_work, Uint256::from_u64(0x02).unwrap());
assert_eq!(header.size, 231);
assert_eq!(header.tx_count.0, 1);
assert_eq!(header.fee_pool_amt.0, 0);
let empty: Vec<u8> = vec![];
assert_eq!(header.utxo_commitment, empty);
assert_eq!(header.miner_data, empty);
assert_eq!(header.nonce, vec![5]);
}
#[test]
fn parse_nexa_header_2() {
let hex = "94bcb94f3f6dfbe905072eacc76763828e177e02f2bf00d11a50a8191ce40c11ffff7f2071b3d0c9122622e3045c15b60e2c65d5f171e0216b1af3fe2dd107e331e41ed726659cf11d2d4f2f8683038b5961020482733cddc0deacb9d36839f92252d02b0000000000000000000000000000000000000000000000000000000000000000c1d9906280000201000000000000000000000000000000000000000000000000000000000000f800000000000000010000000406000000";
let header_serialized = hex::decode(hex).unwrap();
let header: (BlockHeader, usize) = deserialize_partial(&header_serialized).unwrap();
assert_eq!(header.1, header_serialized.len());
assert_eq!(header.0.height.0, 128);
}
#[test]
fn parse_nexa_header_3() {
let hex = "0000000000000000000000000000000000000000000000000000000000000000ffff7f20000000000000000000000000000000000000000000000000000000000000000049317c27d6893f423308ccd46e1f51ec0310892a49b8e6645e176e7235ba04c8000000000000000000000000000000000000000000000000000000000000000027ffee60000200000000000000000000000000000000000000000000000000000000000000e700000000000000010000000105";
let header_serialized = hex::decode(hex).unwrap();
let header: (BlockHeader, usize) = deserialize_partial(&header_serialized).unwrap();
println!("{:?}", header.0);
assert_eq!(header.1, header_serialized.len());
assert_eq!(header.0.height.0, 0);
}
#[test]
fn parse_nexa_serialize() {
let header_hex = "94bcb94f3f6dfbe905072eacc76763828e177e02f2bf00d11a50a8191ce40c11ffff7f2071b3d0c9122622e3045c15b60e2c65d5f171e0216b1af3fe2dd107e331e41ed726659cf11d2d4f2f8683038b5961020482733cddc0deacb9d36839f92252d02b0000000000000000000000000000000000000000000000000000000000000000c1d9906280000201000000000000000000000000000000000000000000000000000000000000f800000000000000010000000406000000";
let serialized = hex::decode(&header_hex).unwrap();
let header: BlockHeader = deserialize(&serialized).unwrap();
let serialized_again = serialize(&header);
assert_eq!(header_hex, hex::encode(&serialized_again))
}
#[test]
fn block_hash() {
let header_hex = "0000000000000000000000000000000000000000000000000000000000000000ffff7f20000000000000000000000000000000000000000000000000000000000000000049317c27d6893f423308ccd46e1f51ec0310892a49b8e6645e176e7235ba04c8000000000000000000000000000000000000000000000000000000000000000027ffee60000200000000000000000000000000000000000000000000000000000000000000e700000000000000010000000105";
let serialized = hex::decode(&header_hex).unwrap();
let header: BlockHeader = deserialize(&serialized).unwrap();
assert_eq!(
"0000000000000000000000000000000000000000000000000000000000000000ffff7f20",
header.mini_header().to_hex()
);
assert_eq!(
"1d45cd14d4f3134b608f80a0ac1f5d011438b59639b2fdf78a8d3f257de7994a",
header.mini_hash().to_hex()
);
assert_eq!(
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049317c27d6893f423308ccd46e1f51ec0310892a49b8e6645e176e7235ba04c827ffee6000000000000000000200000000000000000000000000000000000000000000000000000000000000e7000000000000000100000000000000000000000000000000000105",
header.extended_header().to_hex(),
);
assert_eq!(
"eb88bb4791c173b0ba458404d05c34131364850aeae48cef6ea0e902dcade3f0",
header.extended_hash().to_hex()
);
assert_eq!(
"1d45cd14d4f3134b608f80a0ac1f5d011438b59639b2fdf78a8d3f257de7994aeb88bb4791c173b0ba458404d05c34131364850aeae48cef6ea0e902dcade3f0",
header.block_hash_commitment().to_hex()
);
assert_eq!(
"d71ee431e307d12dfef31a6b21e071f1d5652c0eb6155c04e3222612c9d0b371",
header.block_hash().to_hex()
)
}
#[test]
fn parse_block() {
let block_hex = "0000000000000000000000000000000000000000000000000000000000000000ffff7f20000000000000000000000000000000000000000000000000000000000000000049317c27d6893f423308ccd46e1f51ec0310892a49b8e6645e176e7235ba04c8000000000000000000000000000000000000000000000000000000000000000027ffee60000200000000000000000000000000000000000000000000000000000000000000e700000000000000010000000105010000020000000000000000000151000000000000000000156a00023b1c0f54686973206973207265677465737400000000";
let serialized = hex::decode(&block_hex).unwrap();
let block: Block = deserialize(&serialized).unwrap();
assert_eq!(
"d71ee431e307d12dfef31a6b21e071f1d5652c0eb6155c04e3222612c9d0b371",
block.block_hash().to_hex()
);
assert_eq!(block.txdata.len(), 1);
assert!(block.txdata[0].is_coin_base());
}
}