use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use crate::hash::Hash;
use crate::state::{GlobalState, Metadata, OwnedState};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Genesis {
pub contract_id: Hash,
pub schema_id: Hash,
pub global_state: Vec<GlobalState>,
pub owned_state: Vec<OwnedState>,
pub metadata: Vec<Metadata>,
}
impl Genesis {
pub fn new(
contract_id: Hash,
schema_id: Hash,
global_state: Vec<GlobalState>,
owned_state: Vec<OwnedState>,
metadata: Vec<Metadata>,
) -> Self {
Self {
contract_id,
schema_id,
global_state,
owned_state,
metadata,
}
}
pub fn hash(&self) -> Hash {
let mut hasher = Sha256::new();
hasher.update(b"CSV-GENESIS-v1");
hasher.update(self.contract_id.as_bytes());
hasher.update(self.schema_id.as_bytes());
hasher.update(&(self.global_state.len() as u64).to_le_bytes());
for state in &self.global_state {
hasher.update(&state.type_id.to_le_bytes());
hasher.update(&state.data);
}
hasher.update(&(self.owned_state.len() as u64).to_le_bytes());
for state in &self.owned_state {
hasher.update(&state.type_id.to_le_bytes());
hasher.update(&state.seal.to_vec());
hasher.update(&state.data);
}
hasher.update(&(self.metadata.len() as u64).to_le_bytes());
for meta in &self.metadata {
hasher.update(meta.key.as_bytes());
hasher.update(&meta.value);
}
let result = hasher.finalize();
let mut array = [0u8; 32];
array.copy_from_slice(&result);
Hash::new(array)
}
pub fn state_count(&self) -> usize {
self.global_state.len() + self.owned_state.len()
}
pub fn global_states_of(&self, type_id: u16) -> Vec<&GlobalState> {
self.global_state
.iter()
.filter(|s| s.type_id == type_id)
.collect()
}
pub fn owned_states_of(&self, type_id: u16) -> Vec<&OwnedState> {
self.owned_state
.iter()
.filter(|s| s.type_id == type_id)
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::seal::SealRef;
fn test_genesis() -> Genesis {
Genesis::new(
Hash::new([1u8; 32]),
Hash::new([2u8; 32]),
vec![
GlobalState::new(1, vec![100, 200]), GlobalState::from_hash(2, Hash::new([3u8; 32])), ],
vec![
OwnedState::new(
10,
SealRef::new(vec![0xAA; 16], Some(1)).unwrap(),
1000u64.to_le_bytes().to_vec(),
), OwnedState::new(
10,
SealRef::new(vec![0xBB; 16], Some(2)).unwrap(),
500u64.to_le_bytes().to_vec(),
), ],
vec![
Metadata::from_string("issuer", "test-issuer"),
Metadata::from_string("date", "2026-04-06"),
],
)
}
#[test]
fn test_genesis_creation() {
let genesis = test_genesis();
assert_eq!(genesis.contract_id, Hash::new([1u8; 32]));
assert_eq!(genesis.schema_id, Hash::new([2u8; 32]));
assert_eq!(genesis.global_state.len(), 2);
assert_eq!(genesis.owned_state.len(), 2);
assert_eq!(genesis.metadata.len(), 2);
}
#[test]
fn test_genesis_hash() {
let genesis = test_genesis();
let hash = genesis.hash();
assert_eq!(hash.as_bytes().len(), 32);
}
#[test]
fn test_genesis_hash_deterministic() {
let g1 = test_genesis();
let g2 = test_genesis();
assert_eq!(g1.hash(), g2.hash());
}
#[test]
fn test_genesis_hash_differs_by_contract_id() {
let mut g = test_genesis();
let original_hash = g.hash();
g.contract_id = Hash::new([99u8; 32]);
assert_ne!(g.hash(), original_hash);
}
#[test]
fn test_genesis_hash_differs_by_global_state() {
let mut g = test_genesis();
let original_hash = g.hash();
g.global_state.push(GlobalState::new(99, vec![1, 2, 3]));
assert_ne!(g.hash(), original_hash);
}
#[test]
fn test_genesis_hash_differs_by_owned_state() {
let mut g = test_genesis();
let original_hash = g.hash();
g.owned_state.push(OwnedState::new(
99,
SealRef::new(vec![0xCC; 16], Some(3)).unwrap(),
vec![42],
));
assert_ne!(g.hash(), original_hash);
}
#[test]
fn test_genesis_hash_differs_by_metadata() {
let mut g = test_genesis();
let original_hash = g.hash();
g.metadata.push(Metadata::from_string("extra", "data"));
assert_ne!(g.hash(), original_hash);
}
#[test]
fn test_genesis_state_count() {
let genesis = test_genesis();
assert_eq!(genesis.state_count(), 4); }
#[test]
fn test_genesis_global_states_of() {
let genesis = test_genesis();
let states = genesis.global_states_of(1);
assert_eq!(states.len(), 1);
assert_eq!(states[0].type_id, 1);
let none = genesis.global_states_of(99);
assert!(none.is_empty());
}
#[test]
fn test_genesis_owned_states_of() {
let genesis = test_genesis();
let states = genesis.owned_states_of(10);
assert_eq!(states.len(), 2);
let none = genesis.owned_states_of(99);
assert!(none.is_empty());
}
#[test]
fn test_genesis_empty() {
let genesis = Genesis::new(
Hash::new([1u8; 32]),
Hash::new([2u8; 32]),
vec![],
vec![],
vec![],
);
assert_eq!(genesis.state_count(), 0);
assert!(genesis.global_states_of(1).is_empty());
assert!(genesis.owned_states_of(1).is_empty());
assert_eq!(genesis.hash().as_bytes().len(), 32);
}
#[test]
fn test_genesis_serialization_roundtrip() {
let genesis = test_genesis();
let bytes = bincode::serialize(&genesis).unwrap();
let restored: Genesis = bincode::deserialize(&bytes).unwrap();
assert_eq!(genesis, restored);
assert_eq!(genesis.hash(), restored.hash());
}
}