identity_iota_core/rebased/proposals/
upgrade.rs

1// Copyright 2020-2024 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use std::marker::PhantomData;
5
6use iota_interaction::rpc_types::IotaTransactionBlockEffects;
7use product_common::core_client::CoreClientReadOnly;
8use product_common::transaction::transaction_builder::TransactionBuilder;
9
10use crate::rebased::iota::move_calls;
11use crate::rebased::iota::package::identity_package_id;
12use crate::rebased::migration::ControllerToken;
13use async_trait::async_trait;
14use iota_interaction::types::base_types::ObjectID;
15use iota_interaction::types::TypeTag;
16use serde::Deserialize;
17use serde::Serialize;
18
19use crate::rebased::migration::OnChainIdentity;
20use crate::rebased::migration::Proposal;
21use crate::rebased::Error;
22use iota_interaction::MoveType;
23use iota_interaction::OptionalSync;
24
25use super::CreateProposal;
26use super::ExecuteProposal;
27use super::ProposalT;
28
29/// Action for upgrading the version of an on-chain identity to the package's version.
30#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
31pub struct Upgrade;
32
33impl Upgrade {
34  /// Creates a new [`Upgrade`] action.
35  pub const fn new() -> Self {
36    Self
37  }
38}
39
40impl MoveType for Upgrade {
41  fn move_type(package: ObjectID) -> TypeTag {
42    format!("{package}::upgrade_proposal::Upgrade")
43      .parse()
44      .expect("valid utf8")
45  }
46}
47
48#[cfg_attr(not(feature = "send-sync"), async_trait(?Send))]
49#[cfg_attr(feature = "send-sync", async_trait)]
50impl ProposalT for Proposal<Upgrade> {
51  type Action = Upgrade;
52  type Output = ();
53
54  async fn create<'i, C>(
55    _action: Self::Action,
56    expiration: Option<u64>,
57    identity: &'i mut OnChainIdentity,
58    controller_token: &ControllerToken,
59    client: &C,
60  ) -> Result<TransactionBuilder<CreateProposal<'i, Self::Action>>, Error>
61  where
62    C: CoreClientReadOnly + OptionalSync,
63  {
64    if identity.id() != controller_token.controller_of() {
65      return Err(Error::Identity(format!(
66        "token {} doesn't grant access to identity {}",
67        controller_token.id(),
68        identity.id()
69      )));
70    }
71
72    let identity_ref = client
73      .get_object_ref_by_id(identity.id())
74      .await?
75      .expect("identity exists on-chain");
76    let controller_cap_ref = controller_token.controller_ref(client).await?;
77    let sender_vp = identity
78      .controller_voting_power(controller_token.controller_id())
79      .expect("controller exists");
80    let chained_execution = sender_vp >= identity.threshold();
81    let package = identity_package_id(client).await?;
82
83    let tx = move_calls::identity::propose_upgrade(identity_ref, controller_cap_ref, expiration, package)
84      .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?;
85
86    Ok(TransactionBuilder::new(CreateProposal {
87      identity,
88      ptb: bcs::from_bytes(&tx)?,
89      chained_execution,
90      _action: PhantomData,
91    }))
92  }
93
94  async fn into_tx<'i, C>(
95    self,
96    identity: &'i mut OnChainIdentity,
97    controller_token: &ControllerToken,
98    client: &C,
99  ) -> Result<TransactionBuilder<ExecuteProposal<'i, Self::Action>>, Error>
100  where
101    C: CoreClientReadOnly + OptionalSync,
102  {
103    if identity.id() != controller_token.controller_of() {
104      return Err(Error::Identity(format!(
105        "token {} doesn't grant access to identity {}",
106        controller_token.id(),
107        identity.id()
108      )));
109    }
110
111    let proposal_id = self.id();
112    let identity_ref = client
113      .get_object_ref_by_id(identity.id())
114      .await?
115      .expect("identity exists on-chain");
116    let controller_cap_ref = controller_token.controller_ref(client).await?;
117    let package = identity_package_id(client).await?;
118
119    let tx = move_calls::identity::execute_upgrade(identity_ref, controller_cap_ref, proposal_id, package)
120      .map_err(|e| Error::TransactionBuildingFailed(e.to_string()))?;
121
122    Ok(TransactionBuilder::new(ExecuteProposal {
123      identity,
124      ptb: bcs::from_bytes(&tx)?,
125      _action: PhantomData,
126    }))
127  }
128
129  fn parse_tx_effects(_effects: &IotaTransactionBlockEffects) -> Result<Self::Output, Error> {
130    Ok(())
131  }
132}