identity_iota_core/rebased/proposals/
update_did_doc.rs1use iota_interaction::OptionalSync;
4
5use crate::rebased::iota::package::identity_package_id;
6use std::marker::PhantomData;
7
8use crate::rebased::iota::move_calls;
9use crate::rebased::migration::ControllerToken;
10use crate::IotaDocument;
11use async_trait::async_trait;
12use iota_interaction::rpc_types::IotaTransactionBlockEffects;
13use iota_interaction::types::base_types::ObjectID;
14use iota_interaction::types::TypeTag;
15use product_common::core_client::CoreClientReadOnly;
16use product_common::transaction::transaction_builder::TransactionBuilder;
17use serde::Deserialize;
18use serde::Serialize;
19
20use crate::rebased::migration::OnChainIdentity;
21use crate::rebased::migration::Proposal;
22use crate::rebased::Error;
23use iota_interaction::MoveType;
24
25use super::CreateProposal;
26use super::ExecuteProposal;
27use super::ProposalT;
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(into = "UpdateValue::<Option<Vec<u8>>>", from = "UpdateValue::<Option<Vec<u8>>>")]
32pub struct UpdateDidDocument(Option<Vec<u8>>);
33
34impl MoveType for UpdateDidDocument {
35 fn move_type(package: ObjectID) -> TypeTag {
36 use std::str::FromStr;
37
38 TypeTag::from_str(&format!(
39 "{package}::update_value_proposal::UpdateValue<0x1::option::Option<vector<u8>>>"
40 ))
41 .expect("valid TypeTag")
42 }
43}
44
45impl UpdateDidDocument {
46 pub fn new(document: IotaDocument) -> Self {
48 Self(Some(document.pack().expect("a valid IotaDocument is packable")))
49 }
50
51 pub fn deactivate() -> Self {
53 Self(Some(vec![]))
54 }
55
56 pub fn delete() -> Self {
58 Self(None)
59 }
60
61 pub fn did_document_bytes(&self) -> Option<&[u8]> {
63 self.0.as_deref()
64 }
65}
66
67#[cfg_attr(not(feature = "send-sync"), async_trait(?Send))]
68#[cfg_attr(feature = "send-sync", async_trait)]
69impl ProposalT for Proposal<UpdateDidDocument> {
70 type Action = UpdateDidDocument;
71 type Output = ();
72
73 async fn create<'i, C>(
74 action: Self::Action,
75 expiration: Option<u64>,
76 identity: &'i mut OnChainIdentity,
77 controller_token: &ControllerToken,
78 client: &C,
79 ) -> Result<TransactionBuilder<CreateProposal<'i, Self::Action>>, Error>
80 where
81 C: CoreClientReadOnly + OptionalSync,
82 {
83 if identity.id() != controller_token.controller_of() {
84 return Err(Error::Identity(format!(
85 "token {} doesn't grant access to identity {}",
86 controller_token.id(),
87 identity.id()
88 )));
89 }
90 if identity.has_deleted_did() {
91 return Err(Error::Identity("cannot update a deleted DID Document".into()));
92 }
93
94 let package = identity_package_id(client).await?;
95 let identity_ref = client
96 .get_object_ref_by_id(identity.id())
97 .await?
98 .expect("identity exists on-chain");
99 let controller_cap_ref = controller_token.controller_ref(client).await?;
100 let sender_vp = identity
101 .controller_voting_power(controller_token.controller_id())
102 .expect("controller exists");
103 let chained_execution = sender_vp >= identity.threshold();
104 let tx = move_calls::identity::propose_update(
105 identity_ref,
106 controller_cap_ref,
107 action.0.as_deref(),
108 expiration,
109 package,
110 )
111 .await
112 .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?;
113
114 let ptb = bcs::from_bytes(&tx)?;
115
116 Ok(TransactionBuilder::new(CreateProposal {
117 identity,
118 ptb,
119 chained_execution,
120 _action: PhantomData,
121 }))
122 }
123
124 async fn into_tx<'i, C>(
125 self,
126 identity: &'i mut OnChainIdentity,
127 controller_token: &ControllerToken,
128 client: &C,
129 ) -> Result<TransactionBuilder<ExecuteProposal<'i, Self::Action>>, Error>
130 where
131 C: CoreClientReadOnly + OptionalSync,
132 {
133 if identity.id() != controller_token.controller_of() {
134 return Err(Error::Identity(format!(
135 "token {} doesn't grant access to identity {}",
136 controller_token.id(),
137 identity.id()
138 )));
139 }
140 if identity.has_deleted_did() {
141 return Err(Error::Identity("cannot update a deleted DID Document".into()));
142 }
143
144 let proposal_id = self.id();
145 let identity_ref = client
146 .get_object_ref_by_id(identity.id())
147 .await?
148 .expect("identity exists on-chain");
149 let controller_cap_ref = controller_token.controller_ref(client).await?;
150 let package = identity_package_id(client).await?;
151
152 let tx = move_calls::identity::execute_update(identity_ref, controller_cap_ref, proposal_id, package)
153 .await
154 .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?;
155
156 let ptb = bcs::from_bytes(&tx)?;
157
158 Ok(TransactionBuilder::new(ExecuteProposal {
159 identity,
160 ptb,
161 _action: PhantomData,
162 }))
163 }
164
165 fn parse_tx_effects(_tx_response: &IotaTransactionBlockEffects) -> Result<Self::Output, Error> {
166 Ok(())
167 }
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
171struct UpdateValue<V> {
172 new_value: V,
173}
174
175impl From<UpdateDidDocument> for UpdateValue<Option<Vec<u8>>> {
176 fn from(value: UpdateDidDocument) -> Self {
177 Self { new_value: value.0 }
178 }
179}
180
181impl From<UpdateValue<Option<Vec<u8>>>> for UpdateDidDocument {
182 fn from(value: UpdateValue<Option<Vec<u8>>>) -> Self {
183 UpdateDidDocument(value.new_value)
184 }
185}