use crate::dig_l2_definition as definitions;
use crate::emission::Emission;
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct L2BlockBody {
#[serde(with = "crate::serde_hex::hex_vec")]
pub data: Vec<u8>,
pub emissions: Vec<Emission>,
}
impl L2BlockBody {
pub fn calculate_data_root(&self) -> definitions::Hash32 {
let mut leaves: Vec<definitions::Hash32> = self
.data
.iter()
.map(|b| definitions::COMPUTE_DATA_HASH(*b))
.collect();
leaves.sort_unstable();
definitions::MERKLE_ROOT(&leaves)
}
pub fn calculate_emissions_root(&self) -> definitions::Hash32 {
let mut leaves: Vec<definitions::Hash32> =
self.emissions.iter().map(|e| e.calculate_root()).collect();
leaves.sort_unstable();
definitions::MERKLE_ROOT(&leaves)
}
pub fn calculate_root(&self) -> definitions::Hash32 {
let d = self.calculate_data_root();
let e = self.calculate_emissions_root();
definitions::COMPUTE_BODY_ROOT(&d, &e)
}
}
#[derive(Debug, Error)]
pub enum BodyError {
#[error("body error: {0}")]
Generic(String),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::emission::Emission;
#[test]
fn data_root_does_not_depend_on_input_order() {
let b1 = L2BlockBody {
data: vec![3, 1, 2],
emissions: vec![],
};
let b2 = L2BlockBody {
data: vec![2, 3, 1],
emissions: vec![],
};
assert_eq!(b1.calculate_data_root(), b2.calculate_data_root());
}
#[test]
fn emissions_root_does_not_depend_on_input_order() {
let e1 = Emission {
pubkey: [1u8; 48],
weight: 5,
};
let e2 = Emission {
pubkey: [2u8; 48],
weight: 5,
};
let e3 = Emission {
pubkey: [3u8; 48],
weight: 6,
};
let b1 = L2BlockBody {
data: vec![],
emissions: vec![e1.clone(), e2.clone(), e3.clone()],
};
let b2 = L2BlockBody {
data: vec![],
emissions: vec![e3, e1, e2],
};
assert_eq!(b1.calculate_emissions_root(), b2.calculate_emissions_root());
}
#[test]
fn body_root_changes_when_subroots_change() {
let e = Emission {
pubkey: [9u8; 48],
weight: 1,
};
let b1 = L2BlockBody {
data: vec![1, 2],
emissions: vec![e.clone()],
};
let b2 = L2BlockBody {
data: vec![1],
emissions: vec![e],
};
assert_ne!(b1.calculate_root(), b2.calculate_root());
}
}