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

Rework block signing API #1461

Merged
merged 25 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions bindings/core/src/method/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use iota_sdk::{
NativeToken, NftId, OutputId, TokenScheme,
},
payload::{dto::PayloadDto, signed_transaction::TransactionId},
BlockId, BlockWrapperDto,
BlockId, IssuerId, SignedBlockDto,
},
utils::serde::{option_string, string},
};
Expand Down Expand Up @@ -128,10 +128,13 @@ pub enum ClientMethod {
query_params: Vec<String>,
request_object: Option<String>,
},
/// Build a block containing the specified payload and post it to the network.
PostBlockPayload {
/// The payload to send
payload: PayloadDto,
#[serde(rename_all = "camelCase")]
BuildBasicBlock {
Thoralf-M marked this conversation as resolved.
Show resolved Hide resolved
/// The issuer's ID.
issuer_id: IssuerId,
/// The block payload.
#[serde(default)]
payload: Option<PayloadDto>,
},
//////////////////////////////////////////////////////////////////////
// Node core API
Expand All @@ -157,7 +160,7 @@ pub enum ClientMethod {
/// Post block (JSON)
PostBlock {
/// Block
block: BlockWrapperDto,
block: SignedBlockDto,
},
/// Post block (raw)
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -337,8 +340,9 @@ pub enum ClientMethod {
address: Bech32Address,
},
/// Returns a block ID from a block
#[serde(rename_all = "camelCase")]
BlockId {
Thoralf-M marked this conversation as resolved.
Show resolved Hide resolved
/// Block
block: BlockWrapperDto,
signed_block: SignedBlockDto,
},
}
9 changes: 9 additions & 0 deletions bindings/core/src/method/secret_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crypto::keys::bip44::Bip44;
use derivative::Derivative;
use iota_sdk::{
client::api::{GetAddressesOptions, PreparedTransactionDataDto},
types::block::UnsignedBlockDto,
utils::serde::bip44::Bip44Def,
};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -61,6 +62,14 @@ pub enum SecretManagerMethod {
/// Prepared transaction data
prepared_transaction_data: PreparedTransactionDataDto,
},
// Sign a block.
#[serde(rename_all = "camelCase")]
SignBlock {
unsigned_block: UnsignedBlockDto,
Thoralf-M marked this conversation as resolved.
Show resolved Hide resolved
/// Chain to sign the essence hash with
#[serde(with = "Bip44Def")]
chain: Bip44,
},
/// Store a mnemonic in the Stronghold vault
#[cfg(feature = "stronghold")]
#[cfg_attr(docsrs, doc(cfg(feature = "stronghold")))]
Expand Down
4 changes: 2 additions & 2 deletions bindings/core/src/method/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use iota_sdk::types::block::{
protocol::ProtocolParameters,
signature::Ed25519Signature,
slot::SlotCommitment,
BlockWrapperDto,
SignedBlockDto,
};
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -83,7 +83,7 @@ pub enum UtilsMethod {
#[serde(rename_all = "camelCase")]
BlockId {
/// Block
block: BlockWrapperDto,
block: SignedBlockDto,
/// Network Protocol Parameters
protocol_parameters: ProtocolParameters,
},
Expand Down
15 changes: 10 additions & 5 deletions bindings/core/src/method_handler/call_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ use std::pin::Pin;

use futures::Future;
use iota_sdk::{
client::{secret::SecretManager, Client},
client::{
secret::{DowncastSecretManager, SecretManage},
Client,
},
wallet::Wallet,
};
use tokio::sync::RwLock;

use crate::{
method::{ClientMethod, SecretManagerMethod, WalletMethod},
Expand Down Expand Up @@ -78,10 +80,13 @@ pub fn call_utils_method(method: UtilsMethod) -> Response {
}

/// Call a secret manager method.
pub async fn call_secret_manager_method(
secret_manager: &RwLock<SecretManager>,
pub async fn call_secret_manager_method<S: SecretManage + DowncastSecretManager>(
secret_manager: &S,
method: SecretManagerMethod,
) -> Response {
) -> Response
where
iota_sdk::client::Error: From<S::Error>,
{
log::debug!("Secret manager method: {method:?}");
let result =
convert_async_panics(|| async { call_secret_manager_method_internal(secret_manager, method).await }).await;
Expand Down
54 changes: 24 additions & 30 deletions bindings/core/src/method_handler/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
#[cfg(feature = "mqtt")]
use iota_sdk::client::mqtt::{MqttPayload, Topic};
use iota_sdk::{
client::{request_funds_from_faucet, secret::SecretManager, Client},
client::{request_funds_from_faucet, Client},
types::{
api::core::OutputWithMetadataResponse,
block::{
output::{
dto::OutputDto, AccountOutput, BasicOutput, FoundryOutput, NftOutput, Output, OutputBuilderAmount, Rent,
},
payload::Payload,
BlockWrapper, BlockWrapperDto,
SignedBlock, SignedBlockDto, UnsignedBlockDto,
},
TryFromDto,
},
Expand Down Expand Up @@ -161,6 +161,19 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM

Response::Output(OutputDto::from(&output))
}
ClientMethod::BuildBasicBlock { issuer_id, payload } => {
let payload = if let Some(payload) = payload {
Some(Payload::try_from_dto_with_params(
payload,
&client.get_protocol_parameters().await?,
)?)
} else {
None
};
Response::UnsignedBlock(UnsignedBlockDto::from(
&client.build_basic_block(issuer_id, payload).await?,
))
}
#[cfg(feature = "mqtt")]
ClientMethod::ClearListeners { topics } => {
client.unsubscribe(topics).await?;
Expand All @@ -171,25 +184,6 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM
ClientMethod::GetNetworkId => Response::NetworkId(client.get_network_id().await?.to_string()),
ClientMethod::GetBech32Hrp => Response::Bech32Hrp(client.get_bech32_hrp().await?),
ClientMethod::GetProtocolParameters => Response::ProtocolParameters(client.get_protocol_parameters().await?),
ClientMethod::PostBlockPayload { payload } => {
let block = client
.build_basic_block::<SecretManager>(
todo!("issuer id"),
todo!("issuing time"),
None,
Some(Payload::try_from_dto_with_params(
payload,
&client.get_protocol_parameters().await?,
)?),
todo!("secret manager"),
todo!("chain"),
)
.await?;

let block_id = client.block_id(&block).await?;

Response::BlockIdWithBlock(block_id, BlockWrapperDto::from(&block))
}
#[cfg(not(target_family = "wasm"))]
ClientMethod::UnhealthyNodes => Response::UnhealthyNodes(client.unhealthy_nodes().await.into_iter().collect()),
ClientMethod::GetHealth { url } => Response::Bool(client.get_health(&url).await?),
Expand All @@ -199,22 +193,22 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM
ClientMethod::GetIssuance => Response::Issuance(client.get_issuance().await?),
ClientMethod::PostBlockRaw { block_bytes } => Response::BlockId(
client
.post_block_raw(&BlockWrapper::unpack_strict(
.post_block_raw(&SignedBlock::unpack_strict(
&block_bytes[..],
&client.get_protocol_parameters().await?,
)?)
.await?,
),
ClientMethod::PostBlock { block } => Response::BlockId(
client
.post_block(&BlockWrapper::try_from_dto_with_params(
.post_block(&SignedBlock::try_from_dto_with_params(
block,
client.get_protocol_parameters().await?,
)?)
.await?,
),
ClientMethod::GetBlock { block_id } => {
Response::Block(BlockWrapperDto::from(&client.get_block(&block_id).await?))
Response::SignedBlock(SignedBlockDto::from(&client.get_block(&block_id).await?))
}
ClientMethod::GetBlockMetadata { block_id } => {
Response::BlockMetadata(client.get_block_metadata(&block_id).await?)
Expand All @@ -229,9 +223,9 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM
ClientMethod::GetOutputMetadata { output_id } => {
Response::OutputMetadata(client.get_output_metadata(&output_id).await?)
}
ClientMethod::GetIncludedBlock { transaction_id } => Response::Block(BlockWrapperDto::from(
&client.get_included_block(&transaction_id).await?,
)),
ClientMethod::GetIncludedBlock { transaction_id } => {
Response::SignedBlock(SignedBlockDto::from(&client.get_included_block(&transaction_id).await?))
}
ClientMethod::GetIncludedBlockMetadata { transaction_id } => {
Response::BlockMetadata(client.get_included_block_metadata(&transaction_id).await?)
}
Expand Down Expand Up @@ -276,7 +270,7 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM
.find_blocks(&block_ids)
.await?
.iter()
.map(BlockWrapperDto::from)
.map(SignedBlockDto::from)
.collect(),
),
ClientMethod::FindInputs { addresses, amount } => {
Expand Down Expand Up @@ -317,9 +311,9 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM
.await?;
Response::CustomJson(data)
}
ClientMethod::BlockId { block } => {
ClientMethod::BlockId { signed_block: block } => {
let protocol_parameters = client.get_protocol_parameters().await?;
let block = BlockWrapper::try_from_dto_with_params(block, &protocol_parameters)?;
let block = SignedBlock::try_from_dto_with_params(block, &protocol_parameters)?;
Response::BlockId(block.id(&protocol_parameters))
}
};
Expand Down
87 changes: 69 additions & 18 deletions bindings/core/src/method_handler/secret_manager.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,73 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

#[cfg(feature = "ledger_nano")]
use iota_sdk::client::secret::ledger_nano::LedgerSecretManager;
#[cfg(feature = "stronghold")]
use iota_sdk::client::secret::stronghold::StrongholdSecretManager;
use iota_sdk::{
client::{
api::PreparedTransactionData,
secret::{SecretManage, SecretManager},
api::{GetAddressesOptions, PreparedTransactionData},
secret::{DowncastSecretManager, SecretManage, SignBlock},
},
types::{
block::{address::ToBech32Ext, core::UnsignedBlock, unlock::Unlock, SignedBlockDto},
TryFromDto,
},
types::{block::unlock::Unlock, TryFromDto},
};
use tokio::sync::RwLock;

use crate::{method::SecretManagerMethod, response::Response, Result};

/// Call a secret manager method.
pub(crate) async fn call_secret_manager_method_internal(
secret_manager: &RwLock<SecretManager>,
pub(crate) async fn call_secret_manager_method_internal<S: SecretManage + DowncastSecretManager>(
secret_manager: &S,
method: SecretManagerMethod,
) -> Result<Response> {
let secret_manager = secret_manager.read().await;
) -> Result<Response>
where
iota_sdk::client::Error: From<S::Error>,
{
let response = match method {
SecretManagerMethod::GenerateEd25519Addresses { options } => {
let addresses = secret_manager.generate_ed25519_addresses(options).await?;
SecretManagerMethod::GenerateEd25519Addresses {
options:
GetAddressesOptions {
coin_type,
account_index,
range,
bech32_hrp,
options,
},
} => {
let addresses = secret_manager
.generate_ed25519_addresses(coin_type, account_index, range, options)
.await
.map_err(iota_sdk::client::Error::from)?
.into_iter()
.map(|a| a.to_bech32(bech32_hrp))
.collect();
Response::GeneratedEd25519Addresses(addresses)
}
SecretManagerMethod::GenerateEvmAddresses { options } => {
let addresses = secret_manager.generate_evm_addresses(options).await?;
SecretManagerMethod::GenerateEvmAddresses {
options:
GetAddressesOptions {
coin_type,
account_index,
range,
bech32_hrp: _,
options,
},
} => {
let addresses = secret_manager
.generate_evm_addresses(coin_type, account_index, range, options)
.await
.map_err(iota_sdk::client::Error::from)?
.into_iter()
.map(|a| prefix_hex::encode(a.as_ref()))
.collect();
Response::GeneratedEvmAddresses(addresses)
}
#[cfg(feature = "ledger_nano")]
SecretManagerMethod::GetLedgerNanoStatus => {
if let SecretManager::LedgerNano(secret_manager) = &*secret_manager {
if let Some(secret_manager) = secret_manager.downcast::<LedgerSecretManager>() {
Response::LedgerNanoStatus(secret_manager.get_ledger_nano_status().await)
} else {
return Err(iota_sdk::client::Error::SecretManagerMismatch.into());
Expand All @@ -40,28 +78,41 @@ pub(crate) async fn call_secret_manager_method_internal(
} => {
let transaction = &secret_manager
.sign_transaction(PreparedTransactionData::try_from_dto(prepared_transaction_data)?)
.await?;
.await
.map_err(iota_sdk::client::Error::from)?;
Response::SignedTransaction(transaction.into())
}
SecretManagerMethod::SignBlock { unsigned_block, chain } => Response::SignedBlock(SignedBlockDto::from(
&UnsignedBlock::try_from_dto(unsigned_block)?
.sign_ed25519(secret_manager, chain)
.await?,
)),
SecretManagerMethod::SignatureUnlock {
transaction_signing_hash,
chain,
} => {
let transaction_signing_hash: [u8; 32] = prefix_hex::decode(transaction_signing_hash)?;
let unlock: Unlock = secret_manager
.signature_unlock(&transaction_signing_hash, chain)
.await?;
.await
.map_err(iota_sdk::client::Error::from)?;

Response::SignatureUnlock(unlock)
}
SecretManagerMethod::SignEd25519 { message, chain } => {
let msg: Vec<u8> = prefix_hex::decode(message)?;
let signature = secret_manager.sign_ed25519(&msg, chain).await?;
let signature = secret_manager
.sign_ed25519(&msg, chain)
.await
.map_err(iota_sdk::client::Error::from)?;
Response::Ed25519Signature(signature)
}
SecretManagerMethod::SignSecp256k1Ecdsa { message, chain } => {
let msg: Vec<u8> = prefix_hex::decode(message)?;
let (public_key, signature) = secret_manager.sign_secp256k1_ecdsa(&msg, chain).await?;
let (public_key, signature) = secret_manager
.sign_secp256k1_ecdsa(&msg, chain)
.await
.map_err(iota_sdk::client::Error::from)?;
Response::Secp256k1EcdsaSignature {
public_key: prefix_hex::encode(public_key.to_bytes()),
signature: prefix_hex::encode(signature.to_bytes()),
Expand All @@ -70,7 +121,7 @@ pub(crate) async fn call_secret_manager_method_internal(
#[cfg(feature = "stronghold")]
SecretManagerMethod::StoreMnemonic { mnemonic } => {
let mnemonic = crypto::keys::bip39::Mnemonic::from(mnemonic);
if let SecretManager::Stronghold(secret_manager) = &*secret_manager {
if let Some(secret_manager) = secret_manager.downcast::<StrongholdSecretManager>() {
secret_manager.store_mnemonic(mnemonic).await?;
Response::Ok
} else {
Expand Down
Loading
Loading