heddle_object_model/object/
thread_genesis_admission.rs1use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6use super::{
7 ContentHash,
8 thread_replication::{GenesisOwner, ThreadGenesis, integration::TrustedHostedExecutor},
9};
10use crate::error::{HeddleError, Result};
11pub const FORMAT: &str = "heddle-thread-genesis-admission-v2";
12pub const ENVELOPE_FORMAT: &str = "heddle-thread-genesis-authority-v1";
13pub const MAX_BYTES: usize = 2048;
14#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
15#[serde(deny_unknown_fields)]
16pub struct ThreadGenesisAdmission {
17 pub version: u16,
18 pub basis: super::original_boundary_acceptance::AdmissionBasis,
19 pub spool: Uuid,
20 pub spool_genesis: ContentHash,
21 pub thread: ContentHash,
22 pub owner: Uuid,
23 pub creator: [u8; 32],
24 pub authority_digest: ContentHash,
25 pub executor: [u8; 32],
26 pub admitted_at_ms: i64,
27}
28impl ThreadGenesisAdmission {
29 pub fn encode(&self) -> Result<Vec<u8>> {
30 if self.version != 2
31 || self.spool.is_nil()
32 || self.owner.is_nil()
33 || self.creator == [0; 32]
34 || self.executor == [0; 32]
35 || self.admitted_at_ms < 0
36 {
37 return Err(invalid("invalid original genesis admission"));
38 }
39 let bytes = rmp_serde::to_vec_named(self)?;
40 if bytes.len() > MAX_BYTES {
41 return Err(invalid("genesis admission exceeds bound"));
42 }
43 Ok(bytes)
44 }
45 pub fn decode(bytes: &[u8]) -> Result<Self> {
46 if bytes.is_empty() || bytes.len() > MAX_BYTES {
47 return Err(invalid("genesis admission exceeds bound"));
48 }
49 let value: Self = rmp_serde::from_slice(bytes)?;
50 if value.encode()? != bytes {
51 return Err(invalid("noncanonical genesis admission"));
52 }
53 Ok(value)
54 }
55 pub fn authorize(
56 &self,
57 genesis: &ThreadGenesis,
58 envelope: &[u8],
59 trust: &TrustedHostedExecutor,
60 ) -> Result<()> {
61 self.authorize_with_acceptance(genesis, envelope, trust, None)
62 }
63 pub fn authorize_with_acceptance(
64 &self,
65 genesis: &ThreadGenesis,
66 envelope: &[u8],
67 trust: &TrustedHostedExecutor,
68 evidence: Option<&super::original_boundary_acceptance::OriginalBoundaryAcceptance>,
69 ) -> Result<()> {
70 self.basis.authorize_evidence(
71 evidence,
72 self.spool,
73 self.owner,
74 Some(super::original_boundary_acceptance::BoundaryOriginalKind::AccountGenesis),
75 )?;
76 self.encode()?;
77 if self.spool != trust.spool
78 || self.spool_genesis != trust.spool_genesis
79 || self.executor != trust.executor
80 {
81 return Err(invalid(
82 "genesis admission differs from independently pinned executor",
83 ));
84 }
85 if envelope.is_empty()
86 || envelope.len() > 64 * 1024
87 || self.thread != genesis.id()?
88 || self.spool.to_string() != genesis.spool
89 || genesis.owner != GenesisOwner::Account(self.owner)
90 || self.creator != genesis.creator
91 || self.authority_digest != ContentHash::compute_typed(ENVELOPE_FORMAT, envelope)
92 {
93 return Err(invalid(
94 "genesis admission differs from original creator proof",
95 ));
96 }
97 Ok(())
98 }
99}
100fn invalid(message: &str) -> HeddleError {
101 HeddleError::InvalidObject(message.into())
102}