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<Digest> for Block {
82    fn digest(&self) -> Digest {
83        self.digest
84    }
85}
86
87impl Committable<Digest> for Block {
88    fn commitment(&self) -> Digest {
89        self.digest
90    }
91}
92
93#[derive(Clone, Debug, PartialEq, Eq)]
94pub struct Notarized {
95    pub proof: Notarization<MinSig, Digest>,
96    pub block: Block,
97}
98
99impl Notarized {
100    pub fn new(proof: Notarization<MinSig, Digest>, block: Block) -> Self {
101        Self { proof, block }
102    }
103
104    pub fn verify(&self, namespace: &[u8], identity: &<MinSig as Variant>::Public) -> bool {
105        self.proof.verify(namespace, identity)
106    }
107}
108
109impl Write for Notarized {
110    fn write(&self, buf: &mut impl BufMut) {
111        self.proof.write(buf);
112        self.block.write(buf);
113    }
114}
115
116impl Read for Notarized {
117    type Cfg = ();
118
119    fn read_cfg(buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, Error> {
120        let proof = Notarization::<MinSig, Digest>::read(buf)?;
121        let block = Block::read(buf)?;
122
123        // Ensure the proof is for the block
124        if proof.proposal.payload != block.digest() {
125            return Err(Error::Invalid(
126                "types::Notarized",
127                "Proof payload does not match block digest",
128            ));
129        }
130        Ok(Self { proof, block })
131    }
132}
133
134impl EncodeSize for Notarized {
135    fn encode_size(&self) -> usize {
136        self.proof.encode_size() + self.block.encode_size()
137    }
138}
139
140#[derive(Clone, Debug, PartialEq, Eq)]
141pub struct Finalized {
142    pub proof: Finalization<MinSig, Digest>,
143    pub block: Block,
144}
145
146impl Finalized {
147    pub fn new(proof: Finalization<MinSig, Digest>, block: Block) -> Self {
148        Self { proof, block }
149    }
150
151    pub fn verify(&self, namespace: &[u8], identity: &<MinSig as Variant>::Public) -> bool {
152        self.proof.verify(namespace, identity)
153    }
154}
155
156impl Write for Finalized {
157    fn write(&self, buf: &mut impl BufMut) {
158        self.proof.write(buf);
159        self.block.write(buf);
160    }
161}
162
163impl Read for Finalized {
164    type Cfg = ();
165
166    fn read_cfg(buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, Error> {
167        let proof = Finalization::<MinSig, Digest>::read(buf)?;
168        let block = Block::read(buf)?;
169
170        // Ensure the proof is for the block
171        if proof.proposal.payload != block.digest() {
172            return Err(Error::Invalid(
173                "types::Finalized",
174                "Proof payload does not match block digest",
175            ));
176        }
177        Ok(Self { proof, block })
178    }
179}
180
181impl EncodeSize for Finalized {
182    fn encode_size(&self) -> usize {
183        self.proof.encode_size() + self.block.encode_size()
184    }
185}