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