From 1ed074f8d47b86ed0d5ac6d0e8869ec9216ea4f3 Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Mon, 27 Nov 2023 15:18:23 -0500 Subject: [PATCH 1/5] Add block issuer key source for implicit account transition --- bindings/core/src/method/wallet.rs | 7 +- bindings/core/src/method_handler/wallet.rs | 14 +++- cli/src/wallet_cli/mod.rs | 20 ++++-- .../wallet/operations/transaction/account.rs | 64 +++++++++++++------ 4 files changed, 80 insertions(+), 25 deletions(-) diff --git a/bindings/core/src/method/wallet.rs b/bindings/core/src/method/wallet.rs index 59be946bda..ba63ad0f24 100644 --- a/bindings/core/src/method/wallet.rs +++ b/bindings/core/src/method/wallet.rs @@ -4,6 +4,7 @@ #[cfg(feature = "stronghold")] use std::path::PathBuf; +use crypto::{keys::bip44::Bip44, signatures::ed25519::PublicKey}; use derivative::Derivative; #[cfg(feature = "events")] use iota_sdk::wallet::events::types::{WalletEvent, WalletEventType}; @@ -194,7 +195,11 @@ pub enum WalletMethod { /// Prepares to transition an implicit account to an account. /// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction) #[serde(rename_all = "camelCase")] - PrepareImplicitAccountTransition { output_id: OutputId }, + PrepareImplicitAccountTransition { + output_id: OutputId, + public_key: Option, + bip_path: Option, + }, /// Returns the implicit accounts of the wallet. /// Expected response: [`OutputsData`](crate::Response::OutputsData) ImplicitAccounts, diff --git a/bindings/core/src/method_handler/wallet.rs b/bindings/core/src/method_handler/wallet.rs index b70d2e6862..4edf6e33b0 100644 --- a/bindings/core/src/method_handler/wallet.rs +++ b/bindings/core/src/method_handler/wallet.rs @@ -210,8 +210,18 @@ pub(crate) async fn call_wallet_method_internal(wallet: &Wallet, method: WalletM let implicit_account_creation_address = wallet.implicit_account_creation_address().await?; Response::Bech32Address(implicit_account_creation_address) } - WalletMethod::PrepareImplicitAccountTransition { output_id } => { - let data = wallet.prepare_implicit_account_transition(&output_id).await?; + WalletMethod::PrepareImplicitAccountTransition { + output_id, + public_key, + bip_path, + } => { + let data = if let Some(public_key) = public_key { + wallet + .prepare_implicit_account_transition(&output_id, Some(public_key)) + .await? + } else { + wallet.prepare_implicit_account_transition(&output_id, bip_path).await? + }; Response::PreparedTransaction(PreparedTransactionDataDto::from(&data)) } WalletMethod::ImplicitAccounts => { diff --git a/cli/src/wallet_cli/mod.rs b/cli/src/wallet_cli/mod.rs index 9600f81abf..8c92b151e3 100644 --- a/cli/src/wallet_cli/mod.rs +++ b/cli/src/wallet_cli/mod.rs @@ -9,6 +9,7 @@ use clap::{CommandFactory, Parser, Subcommand}; use colored::Colorize; use iota_sdk::{ client::request_funds_from_faucet, + crypto::signatures::ed25519::PublicKey, types::{ api::plugins::participation::types::ParticipationEventId, block::{ @@ -124,6 +125,7 @@ pub enum WalletCommand { ImplicitAccountTransition { /// Identifier of the implicit account output. output_id: OutputId, + public_key: Option, }, /// Lists the implicit accounts of the wallet. ImplicitAccounts, @@ -584,8 +586,18 @@ pub async fn implicit_account_creation_address_command(wallet: &Wallet) -> Resul } // `implicit-account-transition` command -pub async fn implicit_account_transition_command(wallet: &Wallet, output_id: OutputId) -> Result<(), Error> { - let transaction = wallet.implicit_account_transition(&output_id).await?; +pub async fn implicit_account_transition_command( + wallet: &Wallet, + output_id: OutputId, + public_key: Option, +) -> Result<(), Error> { + let public_key = public_key + .map(|s| { + PublicKey::try_from_bytes(prefix_hex::decode(s).map_err(|e| Error::Miscellaneous(e.to_string()))?) + .map_err(|e| Error::Miscellaneous(e.to_string())) + }) + .transpose()?; + let transaction = wallet.implicit_account_transition(&output_id, public_key).await?; println_log_info!( "Implicit account transition transaction sent:\n{:?}\n{:?}", @@ -1121,8 +1133,8 @@ pub async fn prompt_internal( WalletCommand::ImplicitAccountCreationAddress => { implicit_account_creation_address_command(wallet).await } - WalletCommand::ImplicitAccountTransition { output_id } => { - implicit_account_transition_command(wallet, output_id).await + WalletCommand::ImplicitAccountTransition { output_id, public_key } => { + implicit_account_transition_command(wallet, output_id, public_key).await } WalletCommand::ImplicitAccounts => implicit_accounts_command(wallet).await, WalletCommand::MeltNativeToken { token_id, amount } => { diff --git a/sdk/src/wallet/operations/transaction/account.rs b/sdk/src/wallet/operations/transaction/account.rs index 099f9f1fcf..dd926a6b33 100644 --- a/sdk/src/wallet/operations/transaction/account.rs +++ b/sdk/src/wallet/operations/transaction/account.rs @@ -1,6 +1,9 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use crypto::{keys::bip44::Bip44, signatures::ed25519::PublicKey}; +use derive_more::From; + use crate::{ client::{api::PreparedTransactionData, secret::SecretManage}, types::block::{ @@ -23,13 +26,27 @@ where crate::client::Error: From, { /// Transitions an implicit account to an account. - pub async fn implicit_account_transition(&self, output_id: &OutputId) -> Result { - self.sign_and_submit_transaction(self.prepare_implicit_account_transition(output_id).await?, None) - .await + pub async fn implicit_account_transition( + &self, + output_id: &OutputId, + key_source: Option>, + ) -> Result { + self.sign_and_submit_transaction( + self.prepare_implicit_account_transition(output_id, key_source).await?, + None, + ) + .await } /// Prepares to transition an implicit account to an account. - pub async fn prepare_implicit_account_transition(&self, output_id: &OutputId) -> Result { + pub async fn prepare_implicit_account_transition( + &self, + output_id: &OutputId, + key_source: Option>, + ) -> Result + where + crate::wallet::Error: From, + { let implicit_account_data = self.data().await.unspent_outputs.get(output_id).cloned(); let implicit_account = if let Some(implicit_account_data) = &implicit_account_data { @@ -42,20 +59,25 @@ where return Err(Error::ImplicitAccountNotFound); }; - let public_key = if let Some(bip_path) = self.bip_path().await { - self.secret_manager - .read() - .await - .generate_ed25519_public_keys( - bip_path.coin_type, - bip_path.account, - bip_path.address_index..bip_path.address_index + 1, - None, - ) - .await?[0] - } else { - // TODO https://github.com/iotaledger/iota-sdk/issues/1666 - todo!() + let key_source = match key_source.map(Into::into) { + Some(key_source) => key_source, + None => self.bip_path().await.ok_or(Error::MissingBipPath)?.into(), + }; + + let public_key = match key_source { + BlockIssuerKeySource::Key(public_key) => public_key, + BlockIssuerKeySource::Path(bip_path) => { + self.secret_manager + .read() + .await + .generate_ed25519_public_keys( + bip_path.coin_type, + bip_path.account, + bip_path.address_index..bip_path.address_index + 1, + None, + ) + .await?[0] + } }; let account = AccountOutput::build_with_amount(implicit_account.amount(), AccountId::from(output_id)) @@ -81,3 +103,9 @@ where .await } } + +#[derive(From)] +pub enum BlockIssuerKeySource { + Key(PublicKey), + Path(Bip44), +} From 59e09eba01880a339906dea35531b12ed757fcc4 Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Mon, 27 Nov 2023 15:21:35 -0500 Subject: [PATCH 2/5] meh --- bindings/core/src/method_handler/wallet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/core/src/method_handler/wallet.rs b/bindings/core/src/method_handler/wallet.rs index 4edf6e33b0..ed5bf2220d 100644 --- a/bindings/core/src/method_handler/wallet.rs +++ b/bindings/core/src/method_handler/wallet.rs @@ -215,9 +215,9 @@ pub(crate) async fn call_wallet_method_internal(wallet: &Wallet, method: WalletM public_key, bip_path, } => { - let data = if let Some(public_key) = public_key { + let data = if public_key.is_some() { wallet - .prepare_implicit_account_transition(&output_id, Some(public_key)) + .prepare_implicit_account_transition(&output_id, public_key) .await? } else { wallet.prepare_implicit_account_transition(&output_id, bip_path).await? From 02888f00e41dd66a33c5a7c327251e1e825c9d0c Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Thu, 30 Nov 2023 09:10:57 -0500 Subject: [PATCH 3/5] add default tags --- bindings/core/src/method/wallet.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bindings/core/src/method/wallet.rs b/bindings/core/src/method/wallet.rs index dbe066ec3d..ac153be111 100644 --- a/bindings/core/src/method/wallet.rs +++ b/bindings/core/src/method/wallet.rs @@ -197,7 +197,9 @@ pub enum WalletMethod { #[serde(rename_all = "camelCase")] PrepareImplicitAccountTransition { output_id: OutputId, + #[serde(default)] public_key: Option, + #[serde(default)] bip_path: Option, }, /// Returns the implicit accounts of the wallet. From 418b19ef38167676098e04c860cf074de6af6df4 Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Thu, 30 Nov 2023 12:32:54 -0500 Subject: [PATCH 4/5] PR suggestions --- bindings/core/src/method/wallet.rs | 2 +- bindings/core/src/method_handler/wallet.rs | 7 +++++-- sdk/src/wallet/error.rs | 6 ++++++ sdk/src/wallet/operations/transaction/account.rs | 4 ++-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/bindings/core/src/method/wallet.rs b/bindings/core/src/method/wallet.rs index ac153be111..517555dfe8 100644 --- a/bindings/core/src/method/wallet.rs +++ b/bindings/core/src/method/wallet.rs @@ -198,7 +198,7 @@ pub enum WalletMethod { PrepareImplicitAccountTransition { output_id: OutputId, #[serde(default)] - public_key: Option, + public_key: Option, #[serde(default)] bip_path: Option, }, diff --git a/bindings/core/src/method_handler/wallet.rs b/bindings/core/src/method_handler/wallet.rs index 41196e98bb..fd583f6725 100644 --- a/bindings/core/src/method_handler/wallet.rs +++ b/bindings/core/src/method_handler/wallet.rs @@ -3,6 +3,7 @@ use std::time::Duration; +use crypto::signatures::ed25519::PublicKey; use iota_sdk::{ client::api::{ PreparedTransactionData, PreparedTransactionDataDto, SignedTransactionData, SignedTransactionDataDto, @@ -215,9 +216,11 @@ pub(crate) async fn call_wallet_method_internal(wallet: &Wallet, method: WalletM public_key, bip_path, } => { - let data = if public_key.is_some() { + let data = if let Some(public_key_str) = public_key { + let public_key = PublicKey::try_from_bytes(prefix_hex::decode(public_key_str)?) + .map_err(iota_sdk::wallet::Error::other)?; wallet - .prepare_implicit_account_transition(&output_id, public_key) + .prepare_implicit_account_transition(&output_id, Some(public_key)) .await? } else { wallet.prepare_implicit_account_transition(&output_id, bip_path).await? diff --git a/sdk/src/wallet/error.rs b/sdk/src/wallet/error.rs index 3e5eea5a01..a490e5a232 100644 --- a/sdk/src/wallet/error.rs +++ b/sdk/src/wallet/error.rs @@ -129,6 +129,12 @@ pub enum Error { ImplicitAccountNotFound, } +impl Error { + pub fn other(err: E) -> Self { + Self::Other(Box::new(err) as _) + } +} + // Serialize type with Display error impl Serialize for Error { fn serialize(&self, serializer: S) -> Result diff --git a/sdk/src/wallet/operations/transaction/account.rs b/sdk/src/wallet/operations/transaction/account.rs index dd926a6b33..5e0138dc42 100644 --- a/sdk/src/wallet/operations/transaction/account.rs +++ b/sdk/src/wallet/operations/transaction/account.rs @@ -66,7 +66,7 @@ where let public_key = match key_source { BlockIssuerKeySource::Key(public_key) => public_key, - BlockIssuerKeySource::Path(bip_path) => { + BlockIssuerKeySource::Bip44Path(bip_path) => { self.secret_manager .read() .await @@ -107,5 +107,5 @@ where #[derive(From)] pub enum BlockIssuerKeySource { Key(PublicKey), - Path(Bip44), + Bip44Path(Bip44), } From 91f762bebae134d7885d18ee44dd76165e5f7000 Mon Sep 17 00:00:00 2001 From: Alex Coats Date: Thu, 30 Nov 2023 12:52:55 -0500 Subject: [PATCH 5/5] nit --- bindings/core/src/method_handler/wallet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/core/src/method_handler/wallet.rs b/bindings/core/src/method_handler/wallet.rs index 59b6699561..afd34cf1c5 100644 --- a/bindings/core/src/method_handler/wallet.rs +++ b/bindings/core/src/method_handler/wallet.rs @@ -212,7 +212,7 @@ pub(crate) async fn call_wallet_method_internal(wallet: &Wallet, method: WalletM } => { let data = if let Some(public_key_str) = public_key { let public_key = PublicKey::try_from_bytes(prefix_hex::decode(public_key_str)?) - .map_err(iota_sdk::wallet::Error::other)?; + .map_err(iota_sdk::wallet::Error::from)?; wallet .prepare_implicit_account_transition(&output_id, Some(public_key)) .await?