1use std::collections::BTreeMap;
4
5use api::{
6 heddle::api::{common as host, v1alpha2 as wire},
7 hybrid_codec::Reject,
8};
9use heddleco_capability_verifier::{
10 self as permission, VerificationLimits, VerifiedCloneKeyring, VerifiedOwnerState,
11};
12
13pub struct NativeAuthority {
15 envelope: wire::ThreadControlAuthority,
16 publishers: Vec<Vec<u8>>,
17 credentials: Vec<String>,
18}
19
20fn native_authorities(
21 statement: &host::HostedWitnessStatementV1,
22 original_envelope: &[u8],
23 original_publishers: Vec<Vec<u8>>,
24 boundaries: &[wire::ImportBoundaryAcceptanceV1],
25) -> Option<Vec<NativeAuthority>> {
26 let decode = |bytes: &[u8]| {
27 api::mint_root_association::decode_thread_control_authority_for_verification(bytes).ok()
28 };
29 let mut authorities = match statement.basis {
30 1 => vec![NativeAuthority {
31 envelope: decode(original_envelope)?,
32 publishers: original_publishers,
33 credentials: vec![],
34 }],
35 2 if boundaries
36 .iter()
37 .any(|e| e.binding.as_ref() == statement.boundary_acceptance.as_ref())
38 && statement.boundary_acceptance.is_some() =>
39 {
40 Vec::new()
41 }
42 _ => return None,
43 };
44 for evidence in boundaries {
48 let signed = evidence.signed_acceptance.as_ref()?;
49 if signed.format != objects::object::original_boundary_acceptance::FORMAT
50 || signed.signatures.len() != 1
51 {
52 return None;
53 }
54 let acceptance = crypto::original_boundary_acceptance::SignedBoundaryAcceptance {
55 canonical: signed.canonical_record.clone(),
56 signature: signed.signatures[0].signature.clone(),
57 }
58 .verify_signature()
59 .ok()?;
60 if signed.signatures[0].public_key != acceptance.accepting_publisher {
61 return None;
62 }
63 let objects::object::thread_replication::SourceAuthor::Account { authority, .. } =
64 acceptance.accepting_author
65 else {
66 return None;
67 };
68 authorities.push(NativeAuthority {
69 envelope: decode(&authority)?,
70 publishers: vec![acceptance.accepting_publisher.to_vec()],
71 credentials: vec![],
72 });
73 }
74 for authority in &mut authorities {
75 let keys = heddle_biscuit_verifier::parse_ed25519_public_keys_hex(
76 &hex::encode(&authority.envelope.mint_root_public_key),
77 1,
78 )
79 .ok()?;
80 let key = *keys.first()?;
81 let token =
82 heddle_biscuit_verifier::signature_v1::verify(&authority.envelope.sealed_biscuit, key)
83 .ok()?;
84 let facts = heddle_biscuit_verifier::inspect_verified_credential(&token, &key).ok()?;
85 authority.credentials = facts.revocation_identities().map(str::to_owned).collect();
86 }
87 Some(authorities)
88}
89
90pub trait PublicEvidence {
93 fn validate(&self) -> Result<(), Reject>;
94 fn public_history(&self) -> crate::thread_replication::delegated_import::PublicHistory<'_>;
95 fn policies(&self) -> &[wire::SignedSpoolPolicyRecord];
96 fn statements(&self) -> &[host::SignedHostedWitnessStatementV1];
97 fn binding_selectors(&self) -> Vec<(Vec<u8>, u64)> {
98 Vec::new()
99 }
100 fn imported(&self) -> Option<&wire::ImportPublicProofBundleV1> {
101 None
102 }
103 fn native(&self) -> Option<&wire::NativePublicProofBundleV1> {
104 None
105 }
106 fn delegations(&self) -> &[wire::SignedImportJobDelegationV1] {
107 &[]
108 }
109 fn member_permission(&self) -> Option<&wire::SignedImportMemberPermissionV1> {
110 None
111 }
112 fn native_authorities(
113 &self,
114 statement: &host::HostedWitnessStatementV1,
115 ) -> Option<Vec<NativeAuthority>>;
116}
117fn authority_envelopes(
118 statement: &host::HostedWitnessStatementV1,
119 authorities: &[wire::ImportAuthorityWitnessV1],
120 landings: &[wire::HostedLandingWitnessV1],
121) -> Option<Vec<NativeAuthority>> {
122 if statement.purpose == 2 {
123 let p = authorities.iter().find(|p| {
124 api::hybrid_codec::canonical(*p).is_ok_and(|b| b == statement.canonical_payload)
125 })?;
126 native_authorities(
127 statement,
128 &p.authority_envelope,
129 p.original
130 .as_ref()?
131 .signatures
132 .iter()
133 .map(|s| s.public_key.clone())
134 .collect(),
135 &p.boundary_acceptances,
136 )
137 } else if statement.purpose == 4 {
138 let p = landings.iter().find(|p| {
139 api::hybrid_codec::canonical(*p).is_ok_and(|b| b == statement.canonical_payload)
140 })?;
141 native_authorities(
142 statement,
143 &p.authority_envelope,
144 vec![p.request.as_ref()?.signature.as_ref()?.public_key.clone()],
145 &[],
146 )
147 } else {
148 None
149 }
150}
151impl PublicEvidence for wire::ImportPublicProofBundleV1 {
152 fn validate(&self) -> Result<(), Reject> {
153 api::import_authority::validate_public_bundle(self)
154 }
155 fn public_history(&self) -> crate::thread_replication::delegated_import::PublicHistory<'_> {
156 self.into()
157 }
158 fn policies(&self) -> &[wire::SignedSpoolPolicyRecord] {
159 &self.policies
160 }
161 fn statements(&self) -> &[host::SignedHostedWitnessStatementV1] {
162 &self.statements
163 }
164 fn imported(&self) -> Option<&wire::ImportPublicProofBundleV1> {
165 Some(self)
166 }
167 fn delegations(&self) -> &[wire::SignedImportJobDelegationV1] {
168 &self.delegations
169 }
170 fn member_permission(&self) -> Option<&wire::SignedImportMemberPermissionV1> {
171 self.member_permission.as_ref()
172 }
173 fn native_authorities(
174 &self,
175 statement: &host::HostedWitnessStatementV1,
176 ) -> Option<Vec<NativeAuthority>> {
177 if statement.purpose != 1 {
178 return authority_envelopes(
179 statement,
180 &self.authority_witnesses,
181 &self.landing_witnesses,
182 );
183 }
184 let p = self.genesis_witnesses.iter().find(|p| {
185 api::hybrid_codec::canonical(*p).is_ok_and(|b| b == statement.canonical_payload)
186 })?;
187 native_authorities(
188 statement,
189 &p.creator_authority_envelope,
190 p.original_genesis
191 .as_ref()?
192 .signatures
193 .iter()
194 .map(|s| s.public_key.clone())
195 .collect(),
196 p.boundary_acceptance.as_slice(),
197 )
198 }
199}
200impl PublicEvidence for wire::NativePublicProofBundleV1 {
201 fn validate(&self) -> Result<(), Reject> {
202 api::native_witness::validate_public_bundle(self)
203 }
204 fn public_history(&self) -> crate::thread_replication::delegated_import::PublicHistory<'_> {
205 self.into()
206 }
207 fn policies(&self) -> &[wire::SignedSpoolPolicyRecord] {
208 &self.policies
209 }
210 fn statements(&self) -> &[host::SignedHostedWitnessStatementV1] {
211 &self.statements
212 }
213 fn native(&self) -> Option<&wire::NativePublicProofBundleV1> {
214 Some(self)
215 }
216 fn binding_selectors(&self) -> Vec<(Vec<u8>, u64)> {
217 self.genesis_witnesses
218 .iter()
219 .filter_map(|p| p.binding.as_ref()?.body.as_ref()?.identity.as_ref())
220 .map(|id| (id.owner_state_hash.clone(), id.ownership_transfer_sequence))
221 .collect()
222 }
223 fn native_authorities(
224 &self,
225 statement: &host::HostedWitnessStatementV1,
226 ) -> Option<Vec<NativeAuthority>> {
227 if statement.purpose != 1 {
228 return authority_envelopes(
229 statement,
230 &self.authority_witnesses,
231 &self.landing_witnesses,
232 );
233 }
234 let p = self.genesis_witnesses.iter().find(|p| {
235 api::hybrid_codec::canonical(*p).is_ok_and(|b| b == statement.canonical_payload)
236 })?;
237 native_authorities(
238 statement,
239 &p.creator_authority_envelope,
240 p.original_genesis
241 .as_ref()?
242 .signatures
243 .iter()
244 .map(|s| s.public_key.clone())
245 .collect(),
246 p.boundary_acceptance.as_slice(),
247 )
248 }
249}
250
251#[derive(Clone, Debug, PartialEq)]
253pub enum PublicProof {
254 Import(Box<wire::ImportPublicProofBundleV1>),
255 Native(Box<wire::NativePublicProofBundleV1>),
256}
257impl From<wire::ImportPublicProofBundleV1> for PublicProof {
258 fn from(b: wire::ImportPublicProofBundleV1) -> Self {
259 Self::Import(Box::new(b))
260 }
261}
262impl From<wire::NativePublicProofBundleV1> for PublicProof {
263 fn from(b: wire::NativePublicProofBundleV1) -> Self {
264 Self::Native(Box::new(b))
265 }
266}
267impl PublicProof {
268 pub fn witness_set(&self) -> Option<&host::SignedHostedWitnessSetV1> {
269 match self {
270 Self::Import(b) => b.witness_set.as_ref(),
271 Self::Native(b) => b.witness_set.as_ref(),
272 }
273 }
274 pub fn replace_receiver_metadata(&mut self, refreshed: Self) -> Result<(), Reject> {
275 match (self, refreshed) {
276 (Self::Import(b), Self::Import(r)) => replace_import_metadata(b, *r),
277 (Self::Native(b), Self::Native(r)) => replace_native_metadata(b, *r),
278 _ => Err(Reject::Protocol),
279 }
280 }
281 pub fn encode_to_vec(&self) -> Vec<u8> {
282 use prost::Message;
283 match self {
284 Self::Import(b) => b.encode_to_vec(),
285 Self::Native(b) => b.encode_to_vec(),
286 }
287 }
288}
289impl PublicEvidence for PublicProof {
290 fn validate(&self) -> Result<(), Reject> {
291 match self {
292 Self::Import(b) => b.validate(),
293 Self::Native(b) => b.validate(),
294 }
295 }
296 fn public_history(&self) -> crate::thread_replication::delegated_import::PublicHistory<'_> {
297 match self {
298 Self::Import(b) => b.as_ref().into(),
299 Self::Native(b) => b.as_ref().into(),
300 }
301 }
302 fn policies(&self) -> &[wire::SignedSpoolPolicyRecord] {
303 match self {
304 Self::Import(b) => &b.policies,
305 Self::Native(b) => &b.policies,
306 }
307 }
308 fn statements(&self) -> &[host::SignedHostedWitnessStatementV1] {
309 match self {
310 Self::Import(b) => &b.statements,
311 Self::Native(b) => &b.statements,
312 }
313 }
314 fn binding_selectors(&self) -> Vec<(Vec<u8>, u64)> {
315 match self {
316 Self::Import(b) => b.binding_selectors(),
317 Self::Native(b) => b.binding_selectors(),
318 }
319 }
320 fn imported(&self) -> Option<&wire::ImportPublicProofBundleV1> {
321 match self {
322 Self::Import(b) => Some(b),
323 _ => None,
324 }
325 }
326 fn native(&self) -> Option<&wire::NativePublicProofBundleV1> {
327 match self {
328 Self::Native(b) => Some(b),
329 _ => None,
330 }
331 }
332 fn delegations(&self) -> &[wire::SignedImportJobDelegationV1] {
333 match self {
334 Self::Import(b) => &b.delegations,
335 _ => &[],
336 }
337 }
338 fn member_permission(&self) -> Option<&wire::SignedImportMemberPermissionV1> {
339 match self {
340 Self::Import(b) => b.member_permission.as_ref(),
341 _ => None,
342 }
343 }
344 fn native_authorities(
345 &self,
346 s: &host::HostedWitnessStatementV1,
347 ) -> Option<Vec<NativeAuthority>> {
348 match self {
349 Self::Import(b) => b.native_authorities(s),
350 Self::Native(b) => b.native_authorities(s),
351 }
352 }
353}
354
355pub struct SelectedAuthority<F, B = wire::ImportPublicProofBundleV1> {
358 history: AcceptedHistory,
359 bundle: B,
360 authorize: F,
361 native: BTreeMap<Vec<u8>, Option<Vec<NativeAuthority>>>,
362}
363fn cache_native_authorities(
364 bundle: &impl PublicEvidence,
365) -> BTreeMap<Vec<u8>, Option<Vec<NativeAuthority>>> {
366 bundle
367 .statements()
368 .iter()
369 .filter_map(|signed| {
370 let s = signed.body.as_ref()?;
371 let id = api::witness_trust::statement_signing_digest(s).ok()?;
372 Some((id, bundle.native_authorities(s)))
373 })
374 .collect()
375}
376impl<F> SelectedAuthority<F> {
377 pub fn new(
378 history: AcceptedHistory,
379 bundle: wire::ImportPublicProofBundleV1,
380 authorize: F,
381 ) -> Self {
382 let native = cache_native_authorities(&bundle);
383 Self {
384 history,
385 bundle,
386 authorize,
387 native,
388 }
389 }
390}
391impl<F> SelectedAuthority<F, wire::NativePublicProofBundleV1> {
392 pub fn new_native(
393 history: AcceptedHistory,
394 bundle: wire::NativePublicProofBundleV1,
395 authorize: F,
396 ) -> Self {
397 let native = cache_native_authorities(&bundle);
398 Self {
399 history,
400 bundle,
401 authorize,
402 native,
403 }
404 }
405}
406impl<F> SelectedAuthority<F, PublicProof> {
407 pub fn from_proof(history: AcceptedHistory, bundle: PublicProof, authorize: F) -> Self {
408 let native = cache_native_authorities(&bundle);
409 Self {
410 history,
411 bundle,
412 authorize,
413 native,
414 }
415 }
416}
417impl<F, B: PublicEvidence> SelectedAuthority<F, B> {
418 fn selection<'a>(
419 &'a self,
420 selected: &'a HistoricalSelection,
421 ) -> permission::import_delegation::Selection<'a> {
422 permission::import_delegation::Selection {
423 owner: &selected.owner,
424 keyring: &selected.keyring,
425 spool_genesis_digest: self.history.genesis(),
426 initial_owner_id: self.history.initial_owner(),
427 limits: self.history.limits(),
428 }
429 }
430 fn policy(
431 &self,
432 statement: &host::HostedWitnessStatementV1,
433 ) -> Option<&wire::SignedSpoolPolicy> {
434 self.bundle.policies().iter().find_map(|signed| {
436 let body = signed.body.as_ref()?;
437 (body.spool_uuid == statement.spool_uuid
438 && body.sequence == statement.policy_sequence
439 && body.policy_state_hash == statement.policy_state_hash)
440 .then_some(body.policy.as_ref())
441 .flatten()
442 })
443 }
444 fn policy_revocations(&self, statement: &host::HostedWitnessStatementV1) -> Option<&[Vec<u8>]> {
445 if statement.policy_sequence == 0 && statement.policy_state_hash == [0; 32] {
446 if self.bundle.policies().iter().any(|record| {
449 record.body.as_ref().is_some_and(|body| {
450 body.spool_uuid == statement.spool_uuid
451 && body.sequence == 0
452 && body.policy_state_hash == [0; 32]
453 })
454 }) {
455 return None;
456 }
457 return Some(&[]);
458 }
459 self.policy(statement)
460 .map(|policy| policy.revoked_key_ids.as_slice())
461 }
462}
463impl<
464 B: PublicEvidence,
465 F: Fn(
466 &B,
467 i64,
468 &crate::thread_replication::hosted_trust::TrustTransaction<'_>,
469 ) -> crate::thread_replication::Result<()>,
470> crate::thread_replication::delegated_import::AcceptedAuthority for SelectedAuthority<F, B>
471{
472 fn authorize_import(
473 &self,
474 bundle: &wire::ImportPublicProofBundleV1,
475 now: i64,
476 context: &crate::thread_replication::hosted_trust::TrustTransaction<'_>,
477 ) -> crate::thread_replication::Result<()> {
478 if self.bundle.imported() != Some(bundle) {
479 return Err(Reject::StaleContext.into());
480 }
481 (self.authorize)(&self.bundle, now, context)
482 }
483 fn authorize_native(
484 &self,
485 bundle: &wire::NativePublicProofBundleV1,
486 now: i64,
487 context: &crate::thread_replication::hosted_trust::TrustTransaction<'_>,
488 ) -> crate::thread_replication::Result<()> {
489 if self.bundle.native() != Some(bundle) {
490 return Err(Reject::StaleContext.into());
491 }
492 (self.authorize)(&self.bundle, now, context)
493 }
494 fn for_witness(
495 &self,
496 statement: &host::HostedWitnessStatementV1,
497 ) -> crate::thread_replication::Result<permission::import_delegation::Selection<'_>> {
498 let selected = self
499 .history
500 .for_witness(statement)
501 .map_err(authority_error)?;
502 Ok(self.selection(selected))
503 }
504 fn for_native_binding(
505 &self,
506 binding: &wire::NativeGenesisAuthorityV1,
507 ) -> crate::thread_replication::Result<permission::import_delegation::Selection<'_>> {
508 let id = binding.identity.as_ref().ok_or(Reject::GenesisBinding)?;
509 let selected = self
510 .history
511 .bindings
512 .get(&(
513 id.owner_state_hash.clone(),
514 id.ownership_transfer_sequence,
515 binding.owner_chain_digest.clone(),
516 ))
517 .ok_or(Reject::Root)?;
518 Ok(self.selection(selected))
519 }
520 fn for_policy(
521 &self,
522 policy: &wire::SignedPolicyBody,
523 ) -> crate::thread_replication::Result<permission::import_delegation::Selection<'_>> {
524 let selected = self.history.for_policy(policy).map_err(authority_error)?;
525 Ok(self.selection(selected))
526 }
527 fn import_revoked(
528 &self,
529 statement: &host::HostedWitnessStatementV1,
530 revocation: permission::import_delegation::Revocation<'_>,
531 ) -> bool {
532 let (Ok(selected), Some(revoked)) = (
533 self.history.for_witness(statement),
534 self.policy_revocations(statement),
535 ) else {
536 return true;
537 };
538 match revocation {
539 permission::import_delegation::Revocation::Key(id) => {
540 let known = selected
541 .owner
542 .authority_public_keys()
543 .chain(selected.keyring.authority_public_keys().cloned())
544 .chain(
545 self.bundle
546 .delegations()
547 .iter()
548 .filter_map(|d| d.body.as_ref())
549 .flat_map(|d| {
550 [d.delegating_public_key.clone(), d.job_public_key.clone()]
551 }),
552 )
553 .any(|key| api::hybrid_codec::key_id(&key).as_slice() == id);
554 !known || revoked.iter().any(|key| key == id)
555 }
556 permission::import_delegation::Revocation::Cancellation(namespace, id) => {
557 namespace != api::import_authority::CANCELLATION_NAMESPACE
561 || !self
562 .bundle
563 .delegations()
564 .iter()
565 .filter_map(|d| d.body.as_ref())
566 .any(|d| d.cancellation_id == id)
567 && !self
568 .bundle
569 .member_permission()
570 .into_iter()
571 .filter_map(|p| p.body.as_ref())
572 .any(|p| p.cancellation_id == id)
573 }
574 }
575 }
576 fn native_revoked(
577 &self,
578 statement: &host::HostedWitnessStatementV1,
579 revocation: permission::thread_control_authority::Revocation<'_>,
580 ) -> bool {
581 let (Ok(_), Some(revoked), Some(authorities)) = (
582 self.history.for_witness(statement),
583 self.policy_revocations(statement),
584 api::witness_trust::statement_signing_digest(statement)
585 .ok()
586 .and_then(|id| self.native.get(&id))
587 .and_then(Option::as_ref),
588 ) else {
589 return true;
590 };
591 match revocation {
592 permission::thread_control_authority::Revocation::MintRoot(key) => {
593 !authorities
594 .iter()
595 .any(|a| key == a.envelope.mint_root_public_key)
596 || revoked.contains(&api::hybrid_codec::key_id(key).to_vec())
597 }
598 permission::thread_control_authority::Revocation::Publisher(key) => {
599 !authorities
600 .iter()
601 .any(|a| a.publishers.iter().any(|p| p == key))
602 || revoked.contains(&api::hybrid_codec::key_id(key).to_vec())
603 }
604 permission::thread_control_authority::Revocation::Credential(id) => {
605 !authorities
608 .iter()
609 .any(|a| a.credentials.iter().any(|known| known == id))
610 }
611 }
612 }
613}
614fn authority_error(error: impl std::fmt::Display) -> crate::thread_replication::Error {
615 crate::thread_replication::Error::Invalid(error.to_string())
616}
617
618#[derive(Debug, thiserror::Error)]
619pub enum Error {
620 #[error("HYBRID accepted authority rejected: {0}")]
621 Rejected(#[from] Reject),
622 #[error(transparent)]
623 Owner(#[from] permission::Error),
624}
625
626pub struct HistoricalSelection {
629 pub owner: VerifiedOwnerState,
630 pub keyring: VerifiedCloneKeyring,
631}
632pub struct AcceptedHistory {
633 genesis: [u8; 32],
634 initial_owner: [u8; 32],
635 limits: VerificationLimits,
636 states: BTreeMap<(Vec<u8>, u64), HistoricalSelection>,
637 bindings: BTreeMap<(Vec<u8>, u64, Vec<u8>), HistoricalSelection>,
638}
639
640impl AcceptedHistory {
641 pub fn from_selected_spool(
642 bundle: &wire::ImportPublicProofBundleV1,
643 selected: &VerifiedCloneKeyring,
644 now_seconds: i64,
645 limits: VerificationLimits,
646 ) -> Result<Self, Error> {
647 Self::from_public(bundle, selected, now_seconds, limits)
648 }
649 pub fn from_native_spool(
650 bundle: &wire::NativePublicProofBundleV1,
651 selected: &VerifiedCloneKeyring,
652 now_seconds: i64,
653 limits: VerificationLimits,
654 ) -> Result<Self, Error> {
655 Self::from_public(bundle, selected, now_seconds, limits)
656 }
657 pub fn from_public(
658 bundle: &impl PublicEvidence,
659 selected: &VerifiedCloneKeyring,
660 now_seconds: i64,
661 limits: VerificationLimits,
662 ) -> Result<Self, Error> {
663 bundle.validate()?;
664 let public = bundle.public_history();
665 let pinned = selected.wire();
666 if public.owner_genesis != Some(selected.owner_genesis().signed())
667 || !pinned
668 .ownership_transfers
669 .starts_with(public.ownership_transfers)
670 {
671 return Err(Reject::Root.into());
672 }
673 let genesis = permission::creation::spool_genesis_digest(
674 selected
675 .owner_genesis()
676 .signed()
677 .genesis
678 .as_ref()
679 .ok_or(Reject::Root)?,
680 )?;
681 let mut this = Self {
682 genesis,
683 initial_owner: selected.owner_state().owner_id(),
684 limits,
685 states: BTreeMap::new(),
686 bindings: BTreeMap::new(),
687 };
688 let selectors = bundle
689 .statements()
690 .iter()
691 .map(|signed| {
692 let body = signed.body.as_ref().ok_or(Reject::Canonical)?;
693 Ok((
694 body.owner_state_hash.clone(),
695 body.ownership_transfer_sequence,
696 ))
697 })
698 .chain(bundle.policies().iter().map(|signed| {
699 let body = signed.body.as_ref().ok_or(Reject::Canonical)?;
700 Ok((
701 body.owner_state_hash.clone(),
702 body.ownership_transfer_sequence,
703 ))
704 }))
705 .chain(bundle.binding_selectors().into_iter().map(Ok))
706 .collect::<Result<std::collections::BTreeSet<_>, Reject>>()?;
707 for (hash, sequence) in selectors {
708 let count = usize::try_from(sequence).map_err(|_| Reject::Bounds)?;
709 let transfers = public
710 .ownership_transfers
711 .get(..count)
712 .ok_or(Reject::Root)?;
713 let history = public
714 .owner_histories
715 .iter()
716 .find(|h| h.state_hash == hash)
717 .ok_or(Reject::Root)?;
718 let root = history.root.as_ref().ok_or(Reject::Root)?;
719 let mut owner = permission::verify_owner_root(root)?;
720 for transition in &history.accepted_transitions {
721 owner =
722 permission::apply_accepted_transition(&owner, transition, now_seconds, limits)?;
723 }
724 if owner.state_hash().as_slice() != hash {
725 return Err(Reject::Root.into());
726 }
727 let expected_root = if let Some(last) = transfers.last() {
730 let handoff = last
731 .transfer
732 .as_ref()
733 .and_then(|t| t.acceptance.as_ref())
734 .and_then(|a| a.signed_handoff.as_ref())
735 .and_then(|s| s.handoff.as_ref())
736 .ok_or(Reject::Root)?;
737 pinned
738 .transfer_owner_histories
739 .iter()
740 .find(|h| h.state_hash == handoff.destination_owner_key_state_hash)
741 .and_then(|h| h.root.as_ref())
742 .ok_or(Reject::Root)?
743 } else {
744 pinned.owner_root.as_ref().ok_or(Reject::Root)?
745 };
746 if root != expected_root {
747 return Err(Reject::Root.into());
748 }
749 let initial_history = if let Some(first) = transfers.first() {
750 let handoff = first
751 .transfer
752 .as_ref()
753 .and_then(|t| t.acceptance.as_ref())
754 .and_then(|a| a.signed_handoff.as_ref())
755 .and_then(|s| s.handoff.as_ref())
756 .ok_or(Reject::Root)?;
757 public
758 .owner_histories
759 .iter()
760 .find(|h| h.state_hash == handoff.source_owner_key_state_hash)
761 .ok_or(Reject::Root)?
762 } else {
763 history
764 };
765 if initial_history.root != pinned.owner_root {
766 return Err(Reject::Root.into());
767 }
768 let mut wire = pinned.clone();
769 wire.accepted_transitions = initial_history.accepted_transitions.clone();
770 wire.accepted_state_hash = initial_history.state_hash.clone();
771 wire.ownership_transfers = transfers.to_vec();
772 let mut party_states = std::collections::BTreeSet::new();
774 for transfer in transfers {
775 let handoff = transfer
776 .transfer
777 .as_ref()
778 .and_then(|t| t.acceptance.as_ref())
779 .and_then(|a| a.signed_handoff.as_ref())
780 .and_then(|s| s.handoff.as_ref())
781 .ok_or(Reject::Root)?;
782 party_states.insert(&handoff.source_owner_key_state_hash);
783 party_states.insert(&handoff.destination_owner_key_state_hash);
784 }
785 wire.transfer_owner_histories = public
786 .owner_histories
787 .iter()
788 .filter(|h| party_states.contains(&h.state_hash))
789 .cloned()
790 .collect();
791 let keyring = permission::verify_clone_keyring(wire, now_seconds, limits, &[])?;
792 keyring.verify_current_owner(&owner, now_seconds, limits)?;
793 this.states
794 .insert((hash, sequence), HistoricalSelection { owner, keyring });
795 }
796 if let Some(native) = bundle.native() {
797 for p in &native.genesis_witnesses {
798 let body = p
799 .binding
800 .as_ref()
801 .and_then(|b| b.body.as_ref())
802 .ok_or(Reject::GenesisBinding)?;
803 let id = body.identity.as_ref().ok_or(Reject::GenesisBinding)?;
804 let witnessed = this
805 .states
806 .get(&(id.owner_state_hash.clone(), id.ownership_transfer_sequence))
807 .ok_or(Reject::Root)?;
808 let mut resolved = None;
813 for history in public
814 .owner_histories
815 .iter()
816 .filter(|h| h.root == witnessed.keyring.wire().owner_root)
817 {
818 let mut wire = witnessed.keyring.wire().clone();
819 wire.accepted_transitions = history.accepted_transitions.clone();
820 wire.accepted_state_hash = history.state_hash.clone();
821 let Ok(keyring) =
822 permission::verify_clone_keyring(wire, now_seconds, limits, &[])
823 else {
824 continue;
825 };
826 let selection = permission::import_delegation::Selection {
827 owner: &witnessed.owner,
828 keyring: &keyring,
829 spool_genesis_digest: &this.genesis,
830 initial_owner_id: &this.initial_owner,
831 limits,
832 };
833 if permission::import_delegation::native_lineage(&selection).is_ok_and(
834 |(identity, chain, _)| {
835 body.identity.as_ref() == Some(&identity)
836 && chain == body.owner_chain_digest
837 },
838 ) {
839 if resolved.is_some() {
840 return Err(Reject::Canonical.into());
841 }
842 resolved = Some(HistoricalSelection {
843 owner: witnessed.owner.clone(),
844 keyring,
845 });
846 }
847 }
848 this.bindings.insert(
849 (
850 id.owner_state_hash.clone(),
851 id.ownership_transfer_sequence,
852 body.owner_chain_digest.clone(),
853 ),
854 resolved.ok_or(Reject::Root)?,
855 );
856 }
857 }
858 Ok(this)
859 }
860 pub fn for_witness(
861 &self,
862 statement: &host::HostedWitnessStatementV1,
863 ) -> Result<&HistoricalSelection, Error> {
864 let selected = self
865 .states
866 .get(&(
867 statement.owner_state_hash.clone(),
868 statement.ownership_transfer_sequence,
869 ))
870 .ok_or(Reject::Root)?;
871 if statement.spool_uuid != selected.keyring.owner_genesis().spool_uuid()
872 || statement.spool_genesis_digest != self.genesis
873 || statement.owner_id != selected.owner.owner_id()
874 {
875 return Err(Reject::Root.into());
876 }
877 Ok(selected)
878 }
879 pub fn for_policy(
880 &self,
881 policy: &wire::SignedPolicyBody,
882 ) -> Result<&HistoricalSelection, Error> {
883 let selected = self
884 .states
885 .get(&(
886 policy.owner_state_hash.clone(),
887 policy.ownership_transfer_sequence,
888 ))
889 .ok_or(Reject::Root)?;
890 if policy.spool_uuid != selected.keyring.owner_genesis().spool_uuid()
891 || policy.owner_id != selected.owner.owner_id()
892 {
893 return Err(Reject::Root.into());
894 }
895 Ok(selected)
896 }
897 pub fn genesis(&self) -> &[u8; 32] {
898 &self.genesis
899 }
900 pub fn initial_owner(&self) -> &[u8; 32] {
901 &self.initial_owner
902 }
903 pub fn limits(&self) -> VerificationLimits {
904 self.limits
905 }
906}
907
908fn replace_import_metadata(
909 original: &mut wire::ImportPublicProofBundleV1,
910 refreshed: wire::ImportPublicProofBundleV1,
911) -> Result<(), Reject> {
912 api::import_authority::validate_public_bundle(&refreshed)?;
913 let mut unchanged = refreshed.clone();
914 unchanged.witness_set = original.witness_set.clone();
915 unchanged.history_proofs = original.history_proofs.clone();
916 if &unchanged != original {
917 return Err(Reject::Scope);
918 }
919 *original = refreshed;
920 Ok(())
921}
922
923fn replace_native_metadata(
924 original: &mut wire::NativePublicProofBundleV1,
925 refreshed: wire::NativePublicProofBundleV1,
926) -> Result<(), Reject> {
927 api::native_witness::validate_public_bundle(&refreshed)?;
928 let mut unchanged = refreshed.clone();
929 unchanged.witness_set = original.witness_set.clone();
930 unchanged.history_proofs = original.history_proofs.clone();
931 if &unchanged != original {
932 return Err(Reject::Scope);
933 }
934 *original = refreshed;
935 Ok(())
936}