Skip to content

Commit

Permalink
Merge branch '2.0' into rewards-retention-period
Browse files Browse the repository at this point in the history
  • Loading branch information
thibault-martinez authored Jan 10, 2024
2 parents 9fc88cd + a70e508 commit 2f4958e
Show file tree
Hide file tree
Showing 16 changed files with 218 additions and 531 deletions.
114 changes: 62 additions & 52 deletions bindings/core/src/method_handler/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use iota_sdk::{
api::core::OutputWithMetadataResponse,
block::{
output::{
AccountOutput, BasicOutput, FoundryOutput, MinimumOutputAmount, NftOutput, Output, OutputBuilderAmount,
AccountOutputBuilder, BasicOutputBuilder, FoundryOutputBuilder, MinimumOutputAmount, NftOutputBuilder,
},
payload::Payload,
Block, BlockDto, UnsignedBlockDto,
Expand Down Expand Up @@ -64,40 +64,43 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM
features,
immutable_features,
} => {
let output = Output::from(AccountOutput::try_from_dtos(
if let Some(amount) = amount {
OutputBuilderAmount::Amount(amount)
} else {
OutputBuilderAmount::MinimumAmount(client.get_storage_score_parameters().await?)
},
mana,
&account_id,
foundry_counter,
unlock_conditions,
features,
immutable_features,
)?);
let mut output_builder = if let Some(amount) = amount {
AccountOutputBuilder::new_with_amount(amount, account_id)
} else {
AccountOutputBuilder::new_with_minimum_amount(client.get_storage_score_parameters().await?, account_id)
}
.with_mana(mana)
.with_foundry_counter(foundry_counter)
.with_unlock_conditions(unlock_conditions);

if let Some(features) = features {
output_builder = output_builder.with_features(features);
}
if let Some(immutable_features) = immutable_features {
output_builder = output_builder.with_immutable_features(immutable_features)
}

Response::Output(output)
Response::Output(output_builder.finish_output()?)
}
ClientMethod::BuildBasicOutput {
amount,
mana,
unlock_conditions,
features,
} => {
let output = Output::from(BasicOutput::try_from_dtos(
if let Some(amount) = amount {
OutputBuilderAmount::Amount(amount)
} else {
OutputBuilderAmount::MinimumAmount(client.get_storage_score_parameters().await?)
},
mana,
unlock_conditions,
features,
)?);
let mut output_builder = if let Some(amount) = amount {
BasicOutputBuilder::new_with_amount(amount)
} else {
BasicOutputBuilder::new_with_minimum_amount(client.get_storage_score_parameters().await?)
}
.with_mana(mana)
.with_unlock_conditions(unlock_conditions);

Response::Output(output)
if let Some(features) = features {
output_builder = output_builder.with_features(features);
}

Response::Output(output_builder.finish_output()?)
}
ClientMethod::BuildFoundryOutput {
amount,
Expand All @@ -107,20 +110,25 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM
features,
immutable_features,
} => {
let output = Output::from(FoundryOutput::try_from_dtos(
if let Some(amount) = amount {
OutputBuilderAmount::Amount(amount)
} else {
OutputBuilderAmount::MinimumAmount(client.get_storage_score_parameters().await?)
},
serial_number,
token_scheme,
unlock_conditions,
features,
immutable_features,
)?);
let mut output_builder = if let Some(amount) = amount {
FoundryOutputBuilder::new_with_amount(amount, serial_number, token_scheme)
} else {
FoundryOutputBuilder::new_with_minimum_amount(
client.get_storage_score_parameters().await?,
serial_number,
token_scheme,
)
}
.with_unlock_conditions(unlock_conditions);

Response::Output(output)
if let Some(features) = features {
output_builder = output_builder.with_features(features);
}
if let Some(immutable_features) = immutable_features {
output_builder = output_builder.with_immutable_features(immutable_features)
}

Response::Output(output_builder.finish_output()?)
}
ClientMethod::BuildNftOutput {
amount,
Expand All @@ -130,20 +138,22 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM
features,
immutable_features,
} => {
let output = Output::from(NftOutput::try_from_dtos(
if let Some(amount) = amount {
OutputBuilderAmount::Amount(amount)
} else {
OutputBuilderAmount::MinimumAmount(client.get_storage_score_parameters().await?)
},
mana,
&nft_id,
unlock_conditions,
features,
immutable_features,
)?);
let mut output_builder = if let Some(amount) = amount {
NftOutputBuilder::new_with_amount(amount, nft_id)
} else {
NftOutputBuilder::new_with_minimum_amount(client.get_storage_score_parameters().await?, nft_id)
}
.with_mana(mana)
.with_unlock_conditions(unlock_conditions);

if let Some(features) = features {
output_builder = output_builder.with_features(features);
}
if let Some(immutable_features) = immutable_features {
output_builder = output_builder.with_immutable_features(immutable_features)
}

Response::Output(output)
Response::Output(output_builder.finish_output()?)
}
ClientMethod::BuildBasicBlock { issuer_id, payload } => {
let payload = if let Some(payload) = payload {
Expand Down
1 change: 1 addition & 0 deletions cli/src/wallet_cli/completer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const WALLET_COMMANDS: &[&str] = &[
"claim",
"claimable-outputs",
"clear",
"congestion",
"consolidate",
"create-alias-output",
"create-native-token",
Expand Down
79 changes: 71 additions & 8 deletions cli/src/wallet_cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use iota_sdk::{
types::{
api::plugins::participation::types::ParticipationEventId,
block::{
address::Bech32Address,
address::{Bech32Address, ToBech32Ext},
output::{
unlock_condition::AddressUnlockCondition, AccountId, BasicOutputBuilder, FoundryId, NativeToken,
NativeTokensBuilder, NftId, Output, OutputId, TokenId,
Expand All @@ -24,8 +24,8 @@ use iota_sdk::{
},
utils::ConvertTo,
wallet::{
types::OutputData, ConsolidationParams, CreateNativeTokenParams, MintNftParams, OutputsToClaim,
SendNativeTokenParams, SendNftParams, SendParams, SyncOptions, TransactionOptions, Wallet,
types::OutputData, ConsolidationParams, CreateNativeTokenParams, Error as WalletError, MintNftParams,
OutputsToClaim, SendNativeTokenParams, SendNftParams, SendParams, SyncOptions, TransactionOptions, Wallet,
},
U256,
};
Expand Down Expand Up @@ -81,6 +81,8 @@ pub enum WalletCommand {
},
/// Print details about claimable outputs - if there are any.
ClaimableOutputs,
/// Checks if an account is ready to issue a block.
Congestion { account_id: Option<AccountId> },
/// Consolidate all basic outputs into one address.
Consolidate,
/// Create a new account output.
Expand Down Expand Up @@ -314,7 +316,25 @@ impl FromStr for OutputSelector {

// `accounts` command
pub async fn accounts_command(wallet: &Wallet) -> Result<(), Error> {
print_outputs(wallet.data().await.accounts().cloned().collect(), "Accounts:")
let wallet_data = wallet.data().await;
let accounts = wallet_data.accounts();

println_log_info!("Accounts:\n");

for account in accounts {
let output_id = account.output_id;
let account_id = account.output.as_account().account_id_non_null(&output_id);
let account_address = account_id.to_bech32(wallet.client().get_bech32_hrp().await?);

println_log_info!(
"{:<16} {output_id}\n{:<16} {account_id}\n{:<16} {account_address}\n",
"Output ID:",
"Account ID:",
"Account Address:"
);
}

Ok(())
}

// `address` command
Expand Down Expand Up @@ -460,6 +480,33 @@ pub async fn claimable_outputs_command(wallet: &Wallet) -> Result<(), Error> {
Ok(())
}

// `congestion` command
pub async fn congestion_command(wallet: &Wallet, account_id: Option<AccountId>) -> Result<(), Error> {
let account_id = {
let wallet_data = wallet.data().await;
account_id
.or_else(|| {
wallet_data
.accounts()
.next()
.map(|o| o.output.as_account().account_id_non_null(&o.output_id))
})
.or_else(|| {
wallet_data
.implicit_accounts()
.next()
.map(|o| AccountId::from(&o.output_id))
})
.ok_or(WalletError::NoAccountToIssueBlock)?
};

let congestion = wallet.client().get_account_congestion(&account_id).await?;

println_log_info!("{congestion:#?}");

Ok(())
}

// `consolidate` command
pub async fn consolidate_command(wallet: &Wallet) -> Result<(), Error> {
println_log_info!("Consolidating outputs.");
Expand Down Expand Up @@ -612,10 +659,25 @@ pub async fn implicit_account_transition_command(

// `implicit-accounts` command
pub async fn implicit_accounts_command(wallet: &Wallet) -> Result<(), Error> {
print_outputs(
wallet.data().await.implicit_accounts().cloned().collect(),
"Implicit accounts:",
)
let wallet_data = wallet.data().await;
let implicit_accounts = wallet_data.implicit_accounts();

println_log_info!("Implicit accounts:\n");

for implicit_account in implicit_accounts {
let output_id = implicit_account.output_id;
let account_id = AccountId::from(&output_id);
let account_address = account_id.to_bech32(wallet.client().get_bech32_hrp().await?);

println_log_info!(
"{:<16} {output_id}\n{:<16} {account_id}\n{:<16} {account_address}\n",
"Output ID:",
"Account ID:",
"Account Address:"
);
}

Ok(())
}

// `melt-native-token` command
Expand Down Expand Up @@ -1118,6 +1180,7 @@ pub async fn prompt_internal(
WalletCommand::BurnNft { nft_id } => burn_nft_command(wallet, nft_id).await,
WalletCommand::Claim { output_id } => claim_command(wallet, output_id).await,
WalletCommand::ClaimableOutputs => claimable_outputs_command(wallet).await,
WalletCommand::Congestion { account_id } => congestion_command(wallet, account_id).await,
WalletCommand::Consolidate => consolidate_command(wallet).await,
WalletCommand::CreateAccountOutput => create_account_output_command(wallet).await,
WalletCommand::CreateNativeToken {
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/types/api/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ pub struct CongestionResponse {
pub reference_mana_cost: u64,
/// The Block Issuance Credits of the requested account.
#[serde(with = "string")]
pub block_issuance_credits: u64,
pub block_issuance_credits: i128,
}

/// Response of POST /api/core/v3/blocks.
Expand Down
37 changes: 22 additions & 15 deletions sdk/src/types/block/address/multi.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use alloc::{boxed::Box, vec::Vec};
use alloc::{boxed::Box, collections::BTreeMap, vec::Vec};
use core::{fmt, ops::RangeInclusive};

use crypto::hashes::{blake2b::Blake2b256, Digest};
Expand All @@ -11,10 +11,7 @@ use packable::{bounded::BoundedU8, prefix::BoxedSlicePrefix, Packable, PackableE

use crate::types::block::{address::Address, output::StorageScore, Error};

pub(crate) type WeightedAddressCount =
BoundedU8<{ *MultiAddress::ADDRESSES_COUNT.start() }, { *MultiAddress::ADDRESSES_COUNT.end() }>;

/// An address with an assigned weight.
/// An [`Address`] with an assigned weight.
#[derive(Clone, Debug, Display, Eq, PartialEq, Ord, PartialOrd, Hash, From, AsRef, Deref, Packable)]
#[display(fmt = "{address}")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand All @@ -30,7 +27,9 @@ pub struct WeightedAddress {

impl WeightedAddress {
/// Creates a new [`WeightedAddress`].
pub fn new(address: Address, weight: u8) -> Result<Self, Error> {
pub fn new(address: impl Into<Address>, weight: u8) -> Result<Self, Error> {
let address = address.into();

verify_address::<true>(&address)?;
verify_weight::<true>(&weight)?;

Expand Down Expand Up @@ -69,9 +68,11 @@ fn verify_weight<const VERIFY: bool>(weight: &u8) -> Result<(), Error> {
}
}

/// An address that consists of addresses with weights and a threshold value.
/// The Multi Address can be unlocked if the cumulative weight of all unlocked addresses is equal to or exceeds the
/// threshold.
pub(crate) type WeightedAddressCount =
BoundedU8<{ *MultiAddress::ADDRESSES_COUNT.start() }, { *MultiAddress::ADDRESSES_COUNT.end() }>;

/// An [`Address`] that consists of addresses with weights and a threshold value.
/// It can be unlocked if the cumulative weight of all unlocked addresses is equal to or exceeds the threshold.
#[derive(Clone, Debug, Deref, Eq, PartialEq, Ord, PartialOrd, Hash, Packable)]
#[packable(unpack_error = Error)]
#[packable(verify_with = verify_multi_address)]
Expand All @@ -95,10 +96,15 @@ impl MultiAddress {
/// Creates a new [`MultiAddress`].
#[inline(always)]
pub fn new(addresses: impl IntoIterator<Item = WeightedAddress>, threshold: u16) -> Result<Self, Error> {
let mut addresses = addresses.into_iter().collect::<Box<[_]>>();

addresses.sort_by(|a, b| a.address().cmp(b.address()));

// Using an intermediate BTreeMap to sort the addresses without having to repeatedly packing them.
let addresses = addresses
.into_iter()
.map(|address| (address.address().pack_to_vec(), address))
.collect::<BTreeMap<_, _>>()
.into_values()
.collect::<Box<[_]>>();

verify_addresses::<true>(&addresses)?;
verify_threshold::<true>(&threshold)?;

let addresses = BoxedSlicePrefix::<WeightedAddress, WeightedAddressCount>::try_from(addresses)
Expand Down Expand Up @@ -128,15 +134,15 @@ impl MultiAddress {
pub fn hash(&self) -> [u8; 32] {
let mut digest = Blake2b256::new();

digest.update([MultiAddress::KIND]);
digest.update([Self::KIND]);
digest.update(self.pack_to_vec());

digest.finalize().into()
}
}

fn verify_addresses<const VERIFY: bool>(addresses: &[WeightedAddress]) -> Result<(), Error> {
if VERIFY && !is_unique_sorted(addresses.iter().map(WeightedAddress::address)) {
if VERIFY && !is_unique_sorted(addresses.iter().map(|a| a.address.pack_to_vec())) {
Err(Error::WeightedAddressesNotUniqueSorted)
} else {
Ok(())
Expand All @@ -162,6 +168,7 @@ fn verify_multi_address<const VERIFY: bool>(address: &MultiAddress) -> Result<()
});
}
}

Ok(())
}

Expand Down
Loading

0 comments on commit 2f4958e

Please sign in to comment.