csv_adapter_core/
genesis.rs1use alloc::vec::Vec;
8use serde::{Deserialize, Serialize};
9use sha2::{Digest, Sha256};
10
11use crate::hash::Hash;
12use crate::state::{GlobalState, Metadata, OwnedState};
13
14#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
19pub struct Genesis {
20 pub contract_id: Hash,
22 pub schema_id: Hash,
24 pub global_state: Vec<GlobalState>,
26 pub owned_state: Vec<OwnedState>,
28 pub metadata: Vec<Metadata>,
30}
31
32impl Genesis {
33 pub fn new(
35 contract_id: Hash,
36 schema_id: Hash,
37 global_state: Vec<GlobalState>,
38 owned_state: Vec<OwnedState>,
39 metadata: Vec<Metadata>,
40 ) -> Self {
41 Self {
42 contract_id,
43 schema_id,
44 global_state,
45 owned_state,
46 metadata,
47 }
48 }
49
50 pub fn hash(&self) -> Hash {
54 let mut hasher = Sha256::new();
55
56 hasher.update(b"CSV-GENESIS-v1");
58
59 hasher.update(self.contract_id.as_bytes());
61 hasher.update(self.schema_id.as_bytes());
62
63 hasher.update((self.global_state.len() as u64).to_le_bytes());
65 for state in &self.global_state {
66 hasher.update(state.type_id.to_le_bytes());
67 hasher.update(&state.data);
68 }
69
70 hasher.update((self.owned_state.len() as u64).to_le_bytes());
72 for state in &self.owned_state {
73 hasher.update(state.type_id.to_le_bytes());
74 hasher.update(state.seal.to_vec());
75 hasher.update(&state.data);
76 }
77
78 hasher.update((self.metadata.len() as u64).to_le_bytes());
80 for meta in &self.metadata {
81 hasher.update(meta.key.as_bytes());
82 hasher.update(&meta.value);
83 }
84
85 let result = hasher.finalize();
86 let mut array = [0u8; 32];
87 array.copy_from_slice(&result);
88 Hash::new(array)
89 }
90
91 pub fn state_count(&self) -> usize {
93 self.global_state.len() + self.owned_state.len()
94 }
95
96 pub fn global_states_of(&self, type_id: u16) -> Vec<&GlobalState> {
98 self.global_state
99 .iter()
100 .filter(|s| s.type_id == type_id)
101 .collect()
102 }
103
104 pub fn owned_states_of(&self, type_id: u16) -> Vec<&OwnedState> {
106 self.owned_state
107 .iter()
108 .filter(|s| s.type_id == type_id)
109 .collect()
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use crate::seal::SealRef;
117
118 fn test_genesis() -> Genesis {
119 Genesis::new(
120 Hash::new([1u8; 32]),
121 Hash::new([2u8; 32]),
122 vec![
123 GlobalState::new(1, vec![100, 200]), GlobalState::from_hash(2, Hash::new([3u8; 32])), ],
126 vec![
127 OwnedState::new(
128 10,
129 SealRef::new(vec![0xAA; 16], Some(1)).unwrap(),
130 1000u64.to_le_bytes().to_vec(),
131 ), OwnedState::new(
133 10,
134 SealRef::new(vec![0xBB; 16], Some(2)).unwrap(),
135 500u64.to_le_bytes().to_vec(),
136 ), ],
138 vec![
139 Metadata::from_string("issuer", "test-issuer"),
140 Metadata::from_string("date", "2026-04-06"),
141 ],
142 )
143 }
144
145 #[test]
146 fn test_genesis_creation() {
147 let genesis = test_genesis();
148 assert_eq!(genesis.contract_id, Hash::new([1u8; 32]));
149 assert_eq!(genesis.schema_id, Hash::new([2u8; 32]));
150 assert_eq!(genesis.global_state.len(), 2);
151 assert_eq!(genesis.owned_state.len(), 2);
152 assert_eq!(genesis.metadata.len(), 2);
153 }
154
155 #[test]
156 fn test_genesis_hash() {
157 let genesis = test_genesis();
158 let hash = genesis.hash();
159 assert_eq!(hash.as_bytes().len(), 32);
160 }
161
162 #[test]
163 fn test_genesis_hash_deterministic() {
164 let g1 = test_genesis();
165 let g2 = test_genesis();
166 assert_eq!(g1.hash(), g2.hash());
167 }
168
169 #[test]
170 fn test_genesis_hash_differs_by_contract_id() {
171 let mut g = test_genesis();
172 let original_hash = g.hash();
173 g.contract_id = Hash::new([99u8; 32]);
174 assert_ne!(g.hash(), original_hash);
175 }
176
177 #[test]
178 fn test_genesis_hash_differs_by_global_state() {
179 let mut g = test_genesis();
180 let original_hash = g.hash();
181 g.global_state.push(GlobalState::new(99, vec![1, 2, 3]));
182 assert_ne!(g.hash(), original_hash);
183 }
184
185 #[test]
186 fn test_genesis_hash_differs_by_owned_state() {
187 let mut g = test_genesis();
188 let original_hash = g.hash();
189 g.owned_state.push(OwnedState::new(
190 99,
191 SealRef::new(vec![0xCC; 16], Some(3)).unwrap(),
192 vec![42],
193 ));
194 assert_ne!(g.hash(), original_hash);
195 }
196
197 #[test]
198 fn test_genesis_hash_differs_by_metadata() {
199 let mut g = test_genesis();
200 let original_hash = g.hash();
201 g.metadata.push(Metadata::from_string("extra", "data"));
202 assert_ne!(g.hash(), original_hash);
203 }
204
205 #[test]
206 fn test_genesis_state_count() {
207 let genesis = test_genesis();
208 assert_eq!(genesis.state_count(), 4); }
210
211 #[test]
212 fn test_genesis_global_states_of() {
213 let genesis = test_genesis();
214 let states = genesis.global_states_of(1);
215 assert_eq!(states.len(), 1);
216 assert_eq!(states[0].type_id, 1);
217
218 let none = genesis.global_states_of(99);
219 assert!(none.is_empty());
220 }
221
222 #[test]
223 fn test_genesis_owned_states_of() {
224 let genesis = test_genesis();
225 let states = genesis.owned_states_of(10);
226 assert_eq!(states.len(), 2);
227
228 let none = genesis.owned_states_of(99);
229 assert!(none.is_empty());
230 }
231
232 #[test]
233 fn test_genesis_empty() {
234 let genesis = Genesis::new(
235 Hash::new([1u8; 32]),
236 Hash::new([2u8; 32]),
237 vec![],
238 vec![],
239 vec![],
240 );
241 assert_eq!(genesis.state_count(), 0);
242 assert!(genesis.global_states_of(1).is_empty());
243 assert!(genesis.owned_states_of(1).is_empty());
244
245 assert_eq!(genesis.hash().as_bytes().len(), 32);
247 }
248
249 #[test]
250 fn test_genesis_serialization_roundtrip() {
251 let genesis = test_genesis();
252 let bytes = bincode::serialize(&genesis).unwrap();
253 let restored: Genesis = bincode::deserialize(&bytes).unwrap();
254 assert_eq!(genesis, restored);
255 assert_eq!(genesis.hash(), restored.hash());
256 }
257}