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 [_, _] if record.signatures[0].public_key < record.signatures[1].public_key => {
38 let local = record
39 .signatures
40 .iter()
41 .find(|s| s.public_key == value.prior_local_key)
42 .ok_or(Error::Protocol("local ownership signature missing"))?;
43 let acceptor = record
44 .signatures
45 .iter()
46 .find(|s| s.public_key == value.accepting_publisher)
47 .ok_or(Error::Protocol("accepting ownership signature missing"))?;
48 let proof = SignedOwnershipClaim {
49 canonical: record.canonical_record.clone(),
50 local_signature: local.signature.clone(),
51 acceptance_signature: acceptor.signature.clone(),
52 };
53 proof
54 .verify()
55 .map_err(|_| Error::Protocol("invalid dual ownership signatures"))?;
56 Ok(ClaimProof::Complete(proof))
57 }
58 _ => Err(Error::Protocol(
59 "ownership requires acceptor or ordered owner and acceptor signatures",
60 )),
61 }
62}
63pub fn encode(proof: &SignedOwnershipClaim) -> Result<SignedRecord, Error> {
64 let value = proof
65 .verify()
66 .map_err(|_| Error::Protocol("invalid dual ownership signatures"))?;
67 let mut signatures = vec![
68 RecordSignature {
69 public_key: value.prior_local_key.to_vec(),
70 signature: proof.local_signature.clone(),
71 },
72 RecordSignature {
73 public_key: value.accepting_publisher.to_vec(),
74 signature: proof.acceptance_signature.clone(),
75 },
76 ];
77 signatures.sort_by(|a, b| a.public_key.cmp(&b.public_key));
78 Ok(SignedRecord {
79 format: FORMAT.into(),
80 canonical_record: proof.canonical.clone(),
81 signatures,
82 })
83}
84pub fn encode_acceptance(proof: &SignedOwnershipAcceptance) -> Result<SignedRecord, Error> {
85 let value = proof
86 .verify()
87 .map_err(|_| Error::Protocol("invalid account acceptance signature"))?;
88 Ok(SignedRecord {
89 format: FORMAT.into(),
90 canonical_record: proof.canonical.clone(),
91 signatures: vec![RecordSignature {
92 public_key: value.accepting_publisher.to_vec(),
93 signature: proof.signature.clone(),
94 }],
95 })
96}
97pub fn decode_resolution(record: &SignedRecord) -> Result<SignedOwnershipResolution, Error> {
98 if record.format != RESOLUTION_FORMAT {
99 return Err(Error::Protocol("ownership resolution format required"));
100 }
101 let value = ThreadOwnershipResolution::decode(&record.canonical_record)
102 .map_err(|_| Error::Protocol("invalid canonical ownership resolution"))?;
103 if record.signatures.len() != 2
104 || record.signatures[0].public_key >= record.signatures[1].public_key
105 {
106 return Err(Error::Protocol(
107 "resolution requires two canonical ordered signatures",
108 ));
109 }
110 let local = record
111 .signatures
112 .iter()
113 .find(|s| s.public_key == value.local_owner)
114 .ok_or(Error::Protocol("local resolution signature missing"))?;
115 let acceptor = record
116 .signatures
117 .iter()
118 .find(|s| s.public_key == value.accepting_publisher)
119 .ok_or(Error::Protocol("accepting resolution signature missing"))?;
120 Ok(SignedOwnershipResolution {
121 canonical: record.canonical_record.clone(),
122 local_signature: local.signature.clone(),
123 acceptance_signature: acceptor.signature.clone(),
124 })
125}
126pub fn encode_resolution(proof: &SignedOwnershipResolution) -> Result<SignedRecord, Error> {
127 let value = ThreadOwnershipResolution::decode(&proof.canonical)
128 .map_err(|_| Error::Protocol("invalid ownership resolution"))?;
129 let mut record = SignedRecord {
130 format: RESOLUTION_FORMAT.into(),
131 canonical_record: proof.canonical.clone(),
132 signatures: vec![
133 RecordSignature {
134 public_key: value.local_owner.to_vec(),
135 signature: proof.local_signature.clone(),
136 },
137 RecordSignature {
138 public_key: value.accepting_publisher.to_vec(),
139 signature: proof.acceptance_signature.clone(),
140 },
141 ],
142 };
143 record
144 .signatures
145 .sort_by(|a, b| a.public_key.cmp(&b.public_key));
146 Ok(record)
147}