Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add block issuer key source for implicit account transition #1702

Merged
merged 14 commits into from
Dec 4, 2023
9 changes: 8 additions & 1 deletion bindings/core/src/method/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -194,7 +195,13 @@ 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,
#[serde(default)]
public_key: Option<String>,
#[serde(default)]
bip_path: Option<Bip44>,
},
/// Returns the implicit accounts of the wallet.
/// Expected response: [`OutputsData`](crate::Response::OutputsData)
ImplicitAccounts,
Expand Down
17 changes: 15 additions & 2 deletions bindings/core/src/method_handler/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use std::time::Duration;

use crypto::signatures::ed25519::PublicKey;
use iota_sdk::{
client::api::{
PreparedTransactionData, PreparedTransactionDataDto, SignedTransactionData, SignedTransactionDataDto,
Expand Down Expand Up @@ -204,8 +205,20 @@ 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_str) = public_key {
let public_key = PublicKey::try_from_bytes(prefix_hex::decode(public_key_str)?)
.map_err(iota_sdk::wallet::Error::other)?;
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
wallet
.prepare_implicit_account_transition(&output_id, Some(public_key))
.await?
} else {
wallet.prepare_implicit_account_transition(&output_id, bip_path).await?
DaughterOfMars marked this conversation as resolved.
Show resolved Hide resolved
};
Response::PreparedTransaction(PreparedTransactionDataDto::from(&data))
}
WalletMethod::ImplicitAccounts => {
Expand Down
20 changes: 16 additions & 4 deletions cli/src/wallet_cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -124,6 +125,7 @@ pub enum WalletCommand {
ImplicitAccountTransition {
/// Identifier of the implicit account output.
output_id: OutputId,
public_key: Option<String>,
},
/// Lists the implicit accounts of the wallet.
ImplicitAccounts,
Expand Down Expand Up @@ -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<String>,
) -> 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()?;
DaughterOfMars marked this conversation as resolved.
Show resolved Hide resolved
let transaction = wallet.implicit_account_transition(&output_id, public_key).await?;

println_log_info!(
"Implicit account transition transaction sent:\n{:?}\n{:?}",
Expand Down Expand Up @@ -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 } => {
Expand Down
6 changes: 6 additions & 0 deletions sdk/src/wallet/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ pub enum Error {
ImplicitAccountNotFound,
}

impl Error {
pub fn other<E: 'static + std::error::Error + Send + Sync>(err: E) -> Self {
thibault-martinez marked this conversation as resolved.
Show resolved Hide resolved
Self::Other(Box::new(err) as _)
}
}

// Serialize type with Display error
impl Serialize for Error {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
Expand Down
64 changes: 46 additions & 18 deletions sdk/src/wallet/operations/transaction/account.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -23,13 +26,27 @@ where
crate::client::Error: From<S::Error>,
{
/// Transitions an implicit account to an account.
pub async fn implicit_account_transition(&self, output_id: &OutputId) -> Result<TransactionWithMetadata> {
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<impl Into<BlockIssuerKeySource>>,
) -> Result<TransactionWithMetadata> {
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<PreparedTransactionData> {
pub async fn prepare_implicit_account_transition(
&self,
output_id: &OutputId,
key_source: Option<impl Into<BlockIssuerKeySource>>,
) -> Result<PreparedTransactionData>
where
crate::wallet::Error: From<S::Error>,
{
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 {
Expand All @@ -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::Bip44Path(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))
Expand All @@ -81,3 +103,9 @@ where
.await
}
}

#[derive(From)]
pub enum BlockIssuerKeySource {
Key(PublicKey),
Bip44Path(Bip44),
}
Loading