1use bytes::{Buf, BufMut};
7use commonware_codec::{varint::UInt, EncodeSize, Error as CodecError, Read, ReadExt, Write};
8use commonware_cryptography::{sha256::Digest, Committable, Digestible, Hasher, Sha256};
9
10#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct SimplexBlock {
16 pub parent: Digest,
18
19 pub height: u64,
21
22 pub timestamp: u64,
24
25 pub state_root: [u8; 32],
27
28 pub tx_count: u32,
30
31 pub tx_root: [u8; 32],
33
34 digest: Digest,
36}
37
38impl SimplexBlock {
39 fn compute_digest(
41 parent: &Digest,
42 height: u64,
43 timestamp: u64,
44 state_root: &[u8; 32],
45 tx_count: u32,
46 tx_root: &[u8; 32],
47 ) -> Digest {
48 let mut hasher = Sha256::new();
49 hasher.update(parent);
50 hasher.update(&height.to_be_bytes());
51 hasher.update(×tamp.to_be_bytes());
52 hasher.update(state_root);
53 hasher.update(&tx_count.to_be_bytes());
54 hasher.update(tx_root);
55 hasher.finalize()
56 }
57
58 pub fn new(
60 parent: Digest,
61 height: u64,
62 timestamp: u64,
63 state_root: [u8; 32],
64 tx_count: u32,
65 tx_root: [u8; 32],
66 ) -> Self {
67 let digest =
68 Self::compute_digest(&parent, height, timestamp, &state_root, tx_count, &tx_root);
69 Self {
70 parent,
71 height,
72 timestamp,
73 state_root,
74 tx_count,
75 tx_root,
76 digest,
77 }
78 }
79
80 pub fn genesis() -> Self {
82 let mut hasher = Sha256::new();
83 hasher.update(b"guts-genesis");
84 let genesis_parent = hasher.finalize();
85
86 Self::new(genesis_parent, 0, 0, [0u8; 32], 0, [0u8; 32])
87 }
88}
89
90impl Write for SimplexBlock {
91 fn write(&self, writer: &mut impl BufMut) {
92 self.parent.write(writer);
93 UInt(self.height).write(writer);
94 UInt(self.timestamp).write(writer);
95 writer.put_slice(&self.state_root);
96 UInt(self.tx_count as u64).write(writer);
97 writer.put_slice(&self.tx_root);
98 }
99}
100
101impl Read for SimplexBlock {
102 type Cfg = ();
103
104 fn read_cfg(reader: &mut impl Buf, _: &Self::Cfg) -> Result<Self, CodecError> {
105 let parent = Digest::read(reader)?;
106 let height = UInt::read(reader)?.into();
107 let timestamp = UInt::read(reader)?.into();
108
109 let mut state_root = [0u8; 32];
110 if reader.remaining() < 32 {
111 return Err(CodecError::EndOfBuffer);
112 }
113 reader.copy_to_slice(&mut state_root);
114
115 let tx_count: u64 = UInt::read(reader)?.into();
116
117 let mut tx_root = [0u8; 32];
118 if reader.remaining() < 32 {
119 return Err(CodecError::EndOfBuffer);
120 }
121 reader.copy_to_slice(&mut tx_root);
122
123 let digest = Self::compute_digest(
124 &parent,
125 height,
126 timestamp,
127 &state_root,
128 tx_count as u32,
129 &tx_root,
130 );
131 Ok(Self {
132 parent,
133 height,
134 timestamp,
135 state_root,
136 tx_count: tx_count as u32,
137 tx_root,
138 digest,
139 })
140 }
141}
142
143impl EncodeSize for SimplexBlock {
144 fn encode_size(&self) -> usize {
145 self.parent.encode_size()
146 + UInt(self.height).encode_size()
147 + UInt(self.timestamp).encode_size()
148 + 32 + UInt(self.tx_count as u64).encode_size()
150 + 32 }
152}
153
154impl Digestible for SimplexBlock {
155 type Digest = Digest;
156
157 fn digest(&self) -> Digest {
158 self.digest
159 }
160}
161
162impl Committable for SimplexBlock {
163 type Commitment = Digest;
164
165 fn commitment(&self) -> Digest {
166 self.digest
167 }
168}
169
170impl commonware_consensus::Block for SimplexBlock {
171 fn parent(&self) -> Digest {
172 self.parent
173 }
174
175 fn height(&self) -> u64 {
176 self.height
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183 use commonware_codec::Encode;
184
185 fn test_parent() -> Digest {
187 let genesis = SimplexBlock::genesis();
188 genesis.digest()
189 }
190
191 #[test]
192 fn test_genesis_block() {
193 let genesis = SimplexBlock::genesis();
194 assert_eq!(genesis.height, 0);
195 assert_eq!(genesis.timestamp, 0);
196 assert_eq!(genesis.tx_count, 0);
197 }
198
199 #[test]
200 fn test_block_serialization() {
201 let block = SimplexBlock::new(test_parent(), 1, 1234567890, [1u8; 32], 5, [2u8; 32]);
202
203 let encoded = block.encode();
205
206 let decoded = SimplexBlock::read(&mut encoded.as_ref()).unwrap();
208
209 assert_eq!(block.height, decoded.height);
210 assert_eq!(block.timestamp, decoded.timestamp);
211 assert_eq!(block.state_root, decoded.state_root);
212 assert_eq!(block.tx_count, decoded.tx_count);
213 assert_eq!(block.digest(), decoded.digest());
214 }
215
216 #[test]
217 fn test_block_digest_consistency() {
218 let parent = test_parent();
219 let block1 = SimplexBlock::new(parent, 1, 1234567890, [1u8; 32], 5, [2u8; 32]);
220
221 let block2 = SimplexBlock::new(parent, 1, 1234567890, [1u8; 32], 5, [2u8; 32]);
222
223 assert_eq!(block1.digest(), block2.digest());
224 }
225
226 #[test]
227 fn test_different_blocks_different_digests() {
228 let parent = test_parent();
229 let block1 = SimplexBlock::new(parent, 1, 1234567890, [1u8; 32], 5, [2u8; 32]);
230
231 let block2 = SimplexBlock::new(
232 parent, 2, 1234567890, [1u8; 32], 5, [2u8; 32],
234 );
235
236 assert_ne!(block1.digest(), block2.digest());
237 }
238}