heddle_thread_api/
thread_ownership.rs1use crypto::{
3 thread_ownership_claim::{SignedOwnershipAcceptance, SignedOwnershipClaim},
4 thread_ownership_resolution::SignedOwnershipResolution,
5};
6use heddle_object_model::object::thread_replication::{
7 ownership_claim::{FORMAT, ThreadOwnershipClaim},
8 ownership_resolution::{FORMAT as RESOLUTION_FORMAT, ThreadOwnershipResolution},
9};
10
11use crate::{
12 contract::{RecordSignature, SignedRecord},
13 transport::Error,
14};
15
16pub enum ClaimProof {
17 Acceptance(SignedOwnershipAcceptance),
18 Complete(SignedOwnershipClaim),
19}
20pub fn decode(record: &SignedRecord) -> Result<ClaimProof, Error> {
21 if record.format != FORMAT {
22 return Err(Error::Protocol("ownership claim format required"));
23 }
24 let value = ThreadOwnershipClaim::decode(&record.canonical_record)
25 .map_err(|_| Error::Protocol("invalid canonical ownership claim"))?;
26 match record.signatures.as_slice() {
27 [acceptor] if acceptor.public_key == value.accepting_publisher => {
28 let proof = SignedOwnershipAcceptance {
29 canonical: record.canonical_record.clone(),
30 signature: acceptor.signature.clone(),
31 };
32 proof
33 .verify()
34 .map_err(|_| Error::Protocol("invalid account acceptance signature"))?;
35 Ok(ClaimProof::Acceptance(proof))
36 }
37 [local, acceptor]
38 if local.public_key == value.prior_local_key
39 && acceptor.public_key == value.accepting_publisher =>
40 {
41 let proof = SignedOwnershipClaim {
42 canonical: record.canonical_record.clone(),
43 local_signature: local.signature.clone(),
44 acceptance_signature: acceptor.signature.clone(),
45 };
46 proof
47 .verify()
48 .map_err(|_| Error::Protocol("invalid dual ownership signatures"))?;
49 Ok(ClaimProof::Complete(proof))
50 }
51 _ => Err(Error::Protocol(
52 "ownership requires acceptor or ordered owner and acceptor signatures",
53 )),
54 }
55}
56pub fn encode(proof: &SignedOwnershipClaim) -> Result<SignedRecord, Error> {
57 let value = proof
58 .verify()
59 .map_err(|_| Error::Protocol("invalid dual ownership signatures"))?;
60 Ok(SignedRecord {
61 format: FORMAT.into(),
62 canonical_record: proof.canonical.clone(),
63 signatures: vec![
64 RecordSignature {
65 public_key: value.prior_local_key.to_vec(),
66 signature: proof.local_signature.clone(),
67 },
68 RecordSignature {
69 public_key: value.accepting_publisher.to_vec(),
70 signature: proof.acceptance_signature.clone(),
71 },
72 ],
73 })
74}
75pub fn encode_acceptance(proof: &SignedOwnershipAcceptance) -> Result<SignedRecord, Error> {
76 let value = proof
77 .verify()
78 .map_err(|_| Error::Protocol("invalid account acceptance signature"))?;
79 Ok(SignedRecord {
80 format: FORMAT.into(),
81 canonical_record: proof.canonical.clone(),
82 signatures: vec![RecordSignature {
83 public_key: value.accepting_publisher.to_vec(),
84 signature: proof.signature.clone(),
85 }],
86 })
87}
88pub fn decode_resolution(record: &SignedRecord) -> Result<SignedOwnershipResolution, Error> {
89 if record.format != RESOLUTION_FORMAT {
90 return Err(Error::Protocol("ownership resolution format required"));
91 }
92 let value = ThreadOwnershipResolution::decode(&record.canonical_record)
93 .map_err(|_| Error::Protocol("invalid canonical ownership resolution"))?;
94 let [local, acceptor] = record.signatures.as_slice() else {
95 return Err(Error::Protocol(
96 "resolution needs ordered owner and recipient signatures",
97 ));
98 };
99 if local.public_key != value.local_owner || acceptor.public_key != value.accepting_publisher {
100 return Err(Error::Protocol(
101 "resolution signature roles differ from canonical record",
102 ));
103 }
104 Ok(SignedOwnershipResolution {
105 canonical: record.canonical_record.clone(),
106 local_signature: local.signature.clone(),
107 acceptance_signature: acceptor.signature.clone(),
108 })
109}
110pub fn encode_resolution(proof: &SignedOwnershipResolution) -> Result<SignedRecord, Error> {
111 let value = ThreadOwnershipResolution::decode(&proof.canonical)
112 .map_err(|_| Error::Protocol("invalid ownership resolution"))?;
113 Ok(SignedRecord {
114 format: RESOLUTION_FORMAT.into(),
115 canonical_record: proof.canonical.clone(),
116 signatures: vec![
117 RecordSignature {
118 public_key: value.local_owner.to_vec(),
119 signature: proof.local_signature.clone(),
120 },
121 RecordSignature {
122 public_key: value.accepting_publisher.to_vec(),
123 signature: proof.acceptance_signature.clone(),
124 },
125 ],
126 })
127}