dig_network_block/
emission.rs

1//! Emission type and its root calculation.
2//!
3//! This file defines the standardized `Emission` record used in L2 block bodies
4//! and provides a `calculate_root()` method that returns the per-emission hash
5//! (leaf) using the CAPITALIZED spec functions from `dig_l2_definition`.
6
7use crate::dig_l2_definition as definitions;
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11/// Standardized reward distribution record used in every L2 block.
12///
13/// The `pubkey` is a BLS public key (48 bytes), and `weight` is the relative
14/// share in the reward pool. JSON encodes `pubkey` as a `0x`-prefixed hex
15/// string, and `weight` as a number.
16#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
17pub struct Emission {
18    /// BLS public key (48 bytes), serialized as `0x` hex in JSON.
19    #[serde(with = "crate::serde_hex::hex48")]
20    pub pubkey: [u8; 48],
21    /// Relative share of reward pool.
22    pub weight: u64,
23}
24
25impl Emission {
26    /// Computes the per-emission hash as defined by the spec using
27    /// `COMPUTE_EMISSION_HASH`. This value can serve directly as a leaf for
28    /// inclusion in the emissions Merkle tree.
29    pub fn calculate_root(&self) -> definitions::Hash32 {
30        definitions::COMPUTE_EMISSION_HASH(&self.pubkey, self.weight)
31    }
32}
33
34/// Errors originating from `Emission`-level operations.
35#[derive(Debug, Error)]
36pub enum EmissionError {
37    /// Placeholder for future validation errors (kept to satisfy file-level error requirement).
38    #[error("emission error: {0}")]
39    Generic(String),
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn emission_hash_matches_definition() {
48        let e = Emission {
49            pubkey: [7u8; 48],
50            weight: 42,
51        };
52        let h1 = e.calculate_root();
53        let h2 = definitions::COMPUTE_EMISSION_HASH(&e.pubkey, e.weight);
54        assert_eq!(h1, h2);
55    }
56
57    #[test]
58    fn emission_json_round_trip() {
59        let e = Emission {
60            pubkey: [0x11u8; 48],
61            weight: 9,
62        };
63        let s = serde_json::to_string(&e).unwrap();
64        // Ensure pubkey serialized to string with 0x
65        let v: serde_json::Value = serde_json::from_str(&s).unwrap();
66        let pk_str = v.get("pubkey").and_then(|x| x.as_str()).unwrap();
67        assert!(pk_str.starts_with("0x"));
68        assert_eq!(pk_str.len(), 2 + 48 * 2);
69        let back: Emission = serde_json::from_str(&s).unwrap();
70        assert_eq!(back, e);
71    }
72}