alto_types/
block.rs

1use bytes::{Buf, BufMut};
2use commonware_codec::{varint::UInt, EncodeSize, Error, Read, ReadExt, Write};
3use commonware_consensus::threshold_simplex::types::{Finalization, Notarization};
4use commonware_cryptography::{
5    bls12381::primitives::variant::{MinSig, Variant},
6    sha256::Digest,
7    Committable, Digestible, Hasher, Sha256,
8};
9
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct Block {
12    /// The parent block's digest.
13    pub parent: Digest,
14
15    /// The height of the block in the blockchain.
16    pub height: u64,
17
18    /// The timestamp of the block (in milliseconds since the Unix epoch).
19    pub timestamp: u64,
20
21    /// Pre-computed digest of the block.
22    digest: Digest,
23}
24
25impl Block {
26    fn compute_digest(parent: &Digest, height: u64, timestamp: u64) -> Digest {
27        let mut hasher = Sha256::new();
28        hasher.update(parent);
29        hasher.update(&height.to_be_bytes());
30        hasher.update(&timestamp.to_be_bytes());
31        hasher.finalize()
32    }
33
34    pub fn new(parent: Digest, height: u64, timestamp: u64) -> Self {
35        let digest = Self::compute_digest(&parent, height, timestamp);
36        Self {
37            parent,
38            height,
39            timestamp,
40            digest,
41        }
42    }
43}
44
45impl Write for Block {
46    fn write(&self, writer: &mut impl BufMut) {
47        self.parent.write(writer);
48        UInt(self.height).write(writer);
49        UInt(self.timestamp).write(writer);
50    }
51}
52
53impl Read for Block {
54    type Cfg = ();
55
56    fn read_cfg(reader: &mut impl Buf, _: &Self::Cfg) -> Result<Self, Error> {
57        let parent = Digest::read(reader)?;
58        let height = UInt::read(reader)?.into();
59        let timestamp = UInt::read(reader)?.into();
60
61        // Pre-compute the digest
62        let digest = Self::compute_digest(&parent, height, timestamp);
63        Ok(Self {
64            parent,
65            height,
66            timestamp,
67
68            digest,
69        })
70    }
71}
72
73impl EncodeSize for Block {
74    fn encode_size(&self) -> usize {
75        self.parent.encode_size()
76            + UInt(self.height).encode_size()
77            + UInt(self.timestamp).encode_size()
78    }
79}
80
81impl Digestible for Block {
82    type Digest = Digest;
83
84    fn digest(&self) -> Digest {
85        self.digest
86    }
87}
88
89impl Committable for Block {
90    type Commitment = Digest;
91
92    fn commitment(&self) -> Digest {
93        self.digest
94    }
95}
96
97#[derive(Clone, Debug, PartialEq, Eq)]
98pub struct Notarized {
99    pub proof: Notarization<MinSig, Digest>,
100    pub block: Block,
101}
102
103impl Notarized {
104    pub fn new(proof: Notarization<MinSig, Digest>, block: Block) -> Self {
105        Self { proof, block }
106    }
107
108    pub fn verify(&self, namespace: &[u8], identity: &<MinSig as Variant>::Public) -> bool {
109        self.proof.verify(namespace, identity)
110    }
111}
112
113impl Write for Notarized {
114    fn write(&self, buf: &mut impl BufMut) {
115        self.proof.write(buf);
116        self.block.write(buf);
117    }
118}
119
120impl Read for Notarized {
121    type Cfg = ();
122
123    fn read_cfg(buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, Error> {
124        let proof = Notarization::<MinSig, Digest>::read(buf)?;
125        let block = Block::read(buf)?;
126
127        // Ensure the proof is for the block
128        if proof.proposal.payload != block.digest() {
129            return Err(Error::Invalid(
130                "types::Notarized",
131                "Proof payload does not match block digest",
132            ));
133        }
134        Ok(Self { proof, block })
135    }
136}
137
138impl EncodeSize for Notarized {
139    fn encode_size(&self) -> usize {
140        self.proof.encode_size() + self.block.encode_size()
141    }
142}
143
144#[derive(Clone, Debug, PartialEq, Eq)]
145pub struct Finalized {
146    pub proof: Finalization<MinSig, Digest>,
147    pub block: Block,
148}
149
150impl Finalized {
151    pub fn new(proof: Finalization<MinSig, Digest>, block: Block) -> Self {
152        Self { proof, block }
153    }
154
155    pub fn verify(&self, namespace: &[u8], identity: &<MinSig as Variant>::Public) -> bool {
156        self.proof.verify(namespace, identity)
157    }
158}
159
160impl Write for Finalized {
161    fn write(&self, buf: &mut impl BufMut) {
162        self.proof.write(buf);
163        self.block.write(buf);
164    }
165}
166
167impl Read for Finalized {
168    type Cfg = ();
169
170    fn read_cfg(buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, Error> {
171        let proof = Finalization::<MinSig, Digest>::read(buf)?;
172        let block = Block::read(buf)?;
173
174        // Ensure the proof is for the block
175        if proof.proposal.payload != block.digest() {
176            return Err(Error::Invalid(
177                "types::Finalized",
178                "Proof payload does not match block digest",
179            ));
180        }
181        Ok(Self { proof, block })
182    }
183}
184
185impl EncodeSize for Finalized {
186    fn encode_size(&self) -> usize {
187        self.proof.encode_size() + self.block.encode_size()
188    }
189}
190
191impl commonware_consensus::Block for Block {
192    fn parent(&self) -> Digest {
193        self.parent
194    }
195
196    fn height(&self) -> u64 {
197        self.height
198    }
199}