mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-11 13:39:25 +00:00
add block hash calculations
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use crate::{
|
||||
hash,
|
||||
serialize::*,
|
||||
transaction::{Input, Transaction},
|
||||
};
|
||||
|
||||
mod merkle_root;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct BlockHeader {
|
||||
pub major_version: u64,
|
||||
@@ -65,6 +68,29 @@ impl Block {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn tx_merkle_root(&self) -> [u8; 32] {
|
||||
merkle_root::tree_hash(self.miner_tx.hash(), &self.txs)
|
||||
}
|
||||
|
||||
pub fn serialize_hashable(&self) -> Vec<u8> {
|
||||
let mut blob = self.header.serialize();
|
||||
|
||||
blob.extend_from_slice(&self.tx_merkle_root());
|
||||
|
||||
write_varint(&(1 + self.txs.len() as u64), &mut blob).unwrap();
|
||||
|
||||
let mut out = vec![];
|
||||
write_varint(&(blob.len() as u64), &mut out).unwrap();
|
||||
out.append(&mut blob);
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
pub fn id(&self) -> [u8; 32] {
|
||||
// TODO: block 202612?
|
||||
hash(&self.serialize_hashable())
|
||||
}
|
||||
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut serialized = vec![];
|
||||
self.write(&mut serialized).unwrap();
|
||||
|
||||
68
coins/monero/src/block/merkle_root.rs
Normal file
68
coins/monero/src/block/merkle_root.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use crate::hash;
|
||||
|
||||
/// Round to power of two, for count>=3 and for count being not too large (<= 2^28)
|
||||
/// (as reasonable for tree hash calculations)
|
||||
///
|
||||
fn tree_hash_cnt(count: usize) -> usize {
|
||||
// This algo has some bad history but all we are doing is 1 << floor(log2(count))
|
||||
// There are _many_ ways to do log2, for some reason the one selected was the most obscure one,
|
||||
// and fixing it made it even more obscure.
|
||||
//
|
||||
// Iterative method implemented below aims for clarity over speed, if performance is needed
|
||||
// then my advice is to use the BSR instruction on x86
|
||||
//
|
||||
// All the paranoid asserts have been removed since it is trivial to mathematically prove that
|
||||
// the return will always be a power of 2.
|
||||
// Problem space has been defined as 3 <= count <= 2^28. Of course quarter of a billion
|
||||
// transactions is not a sane upper limit for a block, so there will be tighter limits
|
||||
// in other parts of the code
|
||||
|
||||
assert!(count >= 3); // cases for 0,1,2 are handled elsewhere
|
||||
assert!(count <= 0x10000000); // sanity limit to 2^28, MSB=1 will cause an inf loop
|
||||
|
||||
let mut pow = 2_usize;
|
||||
while pow < count {
|
||||
pow <<= 1
|
||||
}
|
||||
|
||||
pow >> 1
|
||||
}
|
||||
|
||||
fn hash_concat(a: [u8; 32], b: [u8; 32]) -> [u8; 32] {
|
||||
let mut v = [a, b].concat();
|
||||
hash(&v)
|
||||
}
|
||||
|
||||
/// Compute tree hash as defined by Cryptonote
|
||||
pub fn tree_hash(root_hash: [u8; 32], extra_hashes: &[[u8; 32]]) -> [u8; 32] {
|
||||
match extra_hashes.len() {
|
||||
0 => root_hash,
|
||||
1 => hash_concat(root_hash, extra_hashes[0]),
|
||||
other => {
|
||||
let count = other + 1;
|
||||
|
||||
let mut cnt = tree_hash_cnt(count);
|
||||
|
||||
let mut hashes =
|
||||
std::iter::once(root_hash).chain(extra_hashes.iter().copied()).collect::<Vec<_>>();
|
||||
|
||||
let mut i = 2 * cnt - count;
|
||||
let mut j = 2 * cnt - count;
|
||||
while j < cnt {
|
||||
hashes[j] = hash_concat(hashes[i], hashes[i + 1]);
|
||||
i += 2;
|
||||
j += 1;
|
||||
}
|
||||
assert_eq!(i, count);
|
||||
|
||||
while cnt > 2 {
|
||||
cnt >>= 1;
|
||||
for i in 0 .. cnt {
|
||||
hashes[i] = hash_concat(hashes[2 * i], hashes[2 * i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
hash_concat(hashes[0], hashes[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user