use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::crypto::{hash, hash_pair, Hash, PublicKey, SecretKey, Sig};
use crate::error::{Error, Result};
use crate::event::{AuditEvent, EventId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct BlockHash(pub Hash);
impl BlockHash {
pub const ZERO: Self = Self(Hash::ZERO);
pub fn as_hash(&self) -> &Hash {
&self.0
}
pub fn from_header(header: &BlockHeader) -> Self {
header.hash()
}
}
impl std::fmt::Display for BlockHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SealerId {
pub key: PublicKey,
pub name: Option<String>,
}
impl SealerId {
pub fn new(key: PublicKey) -> Self {
Self { key, name: None }
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn id(&self) -> Hash {
self.key.id()
}
pub fn as_pubkey(&self) -> &PublicKey {
&self.key
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BlockHeader {
pub height: u64,
pub parent: BlockHash,
pub events_root: Hash,
pub events_count: u32,
pub mmr_root: Hash,
#[serde(with = "chrono::serde::ts_milliseconds")]
pub timestamp: DateTime<Utc>,
pub sealer: SealerId,
pub seal: Sig,
}
impl BlockHeader {
pub fn signing_bytes(&self) -> Vec<u8> {
let signable = SignableHeader {
height: self.height,
parent: &self.parent,
events_root: &self.events_root,
events_count: self.events_count,
mmr_root: &self.mmr_root,
timestamp: self.timestamp,
sealer: &self.sealer,
};
bincode::serialize(&signable).expect("serialization should not fail")
}
pub fn hash(&self) -> BlockHash {
BlockHash(hash(&self.signing_bytes()))
}
pub fn with_seal(mut self, seal: Sig) -> Self {
self.seal = seal;
self
}
pub fn verify_seal(&self) -> Result<()> {
self.sealer
.key
.verify(&self.signing_bytes(), &self.seal)
.map_err(|_| Error::invalid_block("invalid seal signature"))
}
pub fn validate(&self, parent: Option<&BlockHeader>) -> Result<()> {
self.verify_seal()?;
match parent {
Some(p) => {
if self.height != p.height + 1 {
return Err(Error::invalid_block(format!(
"height {} should be {}",
self.height,
p.height + 1
)));
}
if self.parent != p.hash() {
return Err(Error::invalid_block("parent hash mismatch"));
}
if self.timestamp < p.timestamp {
return Err(Error::invalid_block("timestamp before parent"));
}
}
None => {
if self.height != 0 {
return Err(Error::invalid_block("genesis must have height 0"));
}
if self.parent != BlockHash::ZERO {
return Err(Error::invalid_block("genesis must have zero parent"));
}
}
}
Ok(())
}
}
#[derive(Serialize)]
struct SignableHeader<'a> {
height: u64,
parent: &'a BlockHash,
events_root: &'a Hash,
events_count: u32,
mmr_root: &'a Hash,
#[serde(with = "chrono::serde::ts_milliseconds")]
timestamp: DateTime<Utc>,
sealer: &'a SealerId,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Block {
pub header: BlockHeader,
pub events: Vec<AuditEvent>,
}
impl Block {
pub fn hash(&self) -> BlockHash {
self.header.hash()
}
pub fn validate(&self, parent: Option<&BlockHeader>) -> Result<()> {
self.header.validate(parent)?;
if self.events.len() as u32 != self.header.events_count {
return Err(Error::invalid_block(format!(
"events count mismatch: {} vs {}",
self.events.len(),
self.header.events_count
)));
}
let computed_root = compute_events_root(&self.events);
if computed_root != self.header.events_root {
return Err(Error::invalid_block("events root mismatch"));
}
for event in &self.events {
event.validate()?;
}
Ok(())
}
pub fn validate_batch(&self, parent: Option<&BlockHeader>) -> Result<()> {
self.header.validate(parent)?;
if self.events.len() as u32 != self.header.events_count {
return Err(Error::invalid_block(format!(
"events count mismatch: {} vs {}",
self.events.len(),
self.header.events_count
)));
}
let computed_root = compute_events_root(&self.events);
if computed_root != self.header.events_root {
return Err(Error::invalid_block("events root mismatch"));
}
crate::batch_verify_events(&self.events)?;
let now = chrono::Utc::now();
for event in &self.events {
if event.event_time > now + chrono::Duration::minutes(5) {
return Err(Error::invalid_event("event_time is in the future"));
}
}
Ok(())
}
pub fn validate_parallel(&self, parent: Option<&BlockHeader>) -> Result<()> {
self.header.validate(parent)?;
if self.events.len() as u32 != self.header.events_count {
return Err(Error::invalid_block(format!(
"events count mismatch: {} vs {}",
self.events.len(),
self.header.events_count
)));
}
let computed_root = compute_events_root(&self.events);
if computed_root != self.header.events_root {
return Err(Error::invalid_block("events root mismatch"));
}
if self.events.len() >= 1000 {
crate::batch_verify_events_parallel(&self.events)?;
} else {
crate::batch_verify_events(&self.events)?;
}
let now = chrono::Utc::now();
for event in &self.events {
if event.event_time > now + chrono::Duration::minutes(5) {
return Err(Error::invalid_event("event_time is in the future"));
}
}
Ok(())
}
pub fn event_ids(&self) -> Vec<EventId> {
self.events.iter().map(|e| e.id()).collect()
}
}
pub fn compute_events_root(events: &[AuditEvent]) -> Hash {
if events.is_empty() {
return Hash::ZERO;
}
let mut hashes: Vec<Hash> = events.iter().map(|e| e.id().0).collect();
while hashes.len() & (hashes.len() - 1) != 0 {
hashes.push(*hashes.last().unwrap());
}
while hashes.len() > 1 {
let mut next_level = Vec::with_capacity(hashes.len() / 2);
for pair in hashes.chunks(2) {
next_level.push(hash_pair(pair[0], pair[1]));
}
hashes = next_level;
}
hashes[0]
}
pub fn compute_events_root_parallel(events: &[AuditEvent]) -> Hash {
use rayon::prelude::*;
if events.is_empty() {
return Hash::ZERO;
}
let mut hashes: Vec<Hash> = events.par_iter().map(|e| e.id().0).collect();
while hashes.len() & (hashes.len() - 1) != 0 {
hashes.push(*hashes.last().unwrap());
}
while hashes.len() > 1 {
if hashes.len() >= 64 {
let pairs: Vec<_> = hashes.chunks(2).collect();
hashes = pairs
.par_iter()
.map(|pair| hash_pair(pair[0], pair[1]))
.collect();
} else {
let mut next_level = Vec::with_capacity(hashes.len() / 2);
for pair in hashes.chunks(2) {
next_level.push(hash_pair(pair[0], pair[1]));
}
hashes = next_level;
}
}
hashes[0]
}
pub struct BlockBuilder {
parent: Option<BlockHeader>,
events: Vec<AuditEvent>,
sealer: SealerId,
mmr_root: Hash,
}
impl BlockBuilder {
pub fn new(sealer: SealerId) -> Self {
Self {
parent: None,
events: Vec::new(),
sealer,
mmr_root: Hash::ZERO,
}
}
pub fn parent(mut self, parent: BlockHeader) -> Self {
self.parent = Some(parent);
self
}
pub fn events(mut self, events: Vec<AuditEvent>) -> Self {
self.events = events;
self
}
pub fn mmr_root(mut self, root: Hash) -> Self {
self.mmr_root = root;
self
}
pub fn seal(self, key: &SecretKey) -> Block {
let (height, parent_hash) = match &self.parent {
Some(p) => (p.height + 1, p.hash()),
None => (0, BlockHash::ZERO),
};
let events_root = compute_events_root(&self.events);
let events_count = self.events.len() as u32;
let header = BlockHeader {
height,
parent: parent_hash,
events_root,
events_count,
mmr_root: self.mmr_root,
timestamp: Utc::now(),
sealer: self.sealer,
seal: Sig::empty(),
};
let seal = key.sign(&header.signing_bytes());
let header = header.with_seal(seal);
Block {
header,
events: self.events,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::crypto::SecretKey;
use crate::event::{ActorId, ActorKind, EventType, ResourceId, ResourceKind};
fn test_sealer() -> (SecretKey, SealerId) {
let key = SecretKey::generate();
let sealer = SealerId::new(key.public_key()).with_name("test-sealer");
(key, sealer)
}
fn test_event(key: &SecretKey) -> AuditEvent {
let actor = ActorId::new(key.public_key(), ActorKind::User);
let resource = ResourceId::new(ResourceKind::Repository, "test");
AuditEvent::builder()
.now()
.event_type(EventType::Push {
force: false,
commits: 1,
})
.actor(actor)
.resource(resource)
.sign(key)
.unwrap()
}
#[test]
fn test_genesis_block() {
let (key, sealer) = test_sealer();
let event = test_event(&key);
let block = BlockBuilder::new(sealer).events(vec![event]).seal(&key);
assert_eq!(block.header.height, 0);
assert_eq!(block.header.parent, BlockHash::ZERO);
assert!(block.validate(None).is_ok());
}
#[test]
fn test_block_chain() {
let (key, sealer) = test_sealer();
let genesis = BlockBuilder::new(sealer.clone())
.events(vec![test_event(&key)])
.seal(&key);
assert!(genesis.validate(None).is_ok());
let block1 = BlockBuilder::new(sealer.clone())
.parent(genesis.header.clone())
.events(vec![test_event(&key)])
.seal(&key);
assert!(block1.validate(Some(&genesis.header)).is_ok());
assert_eq!(block1.header.height, 1);
assert_eq!(block1.header.parent, genesis.hash());
let block2 = BlockBuilder::new(sealer)
.parent(block1.header.clone())
.events(vec![test_event(&key), test_event(&key)])
.seal(&key);
assert!(block2.validate(Some(&block1.header)).is_ok());
assert_eq!(block2.header.height, 2);
}
#[test]
fn test_empty_block() {
let (key, sealer) = test_sealer();
let block = BlockBuilder::new(sealer).events(vec![]).seal(&key);
assert!(block.validate(None).is_ok());
assert_eq!(block.header.events_count, 0);
assert_eq!(block.header.events_root, Hash::ZERO);
}
#[test]
fn test_events_root_deterministic() {
let key = SecretKey::generate();
let events = vec![test_event(&key), test_event(&key)];
let root1 = compute_events_root(&events);
let root2 = compute_events_root(&events);
assert_eq!(root1, root2);
}
#[test]
fn test_tampered_block_fails() {
let (key, sealer) = test_sealer();
let mut block = BlockBuilder::new(sealer)
.events(vec![test_event(&key)])
.seal(&key);
block.header.height = 999;
assert!(block.validate(None).is_err());
}
}