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

Better transitions #2152

Merged
merged 35 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3166271
Add some transitions that the transaction builder can do automaticall…
Mar 6, 2024
ee19baf
Merge branch '2.0' into feat/better-transitions
Mar 6, 2024
ae7fa12
Merge branch '2.0' into feat/better-transitions
Mar 6, 2024
ef8758a
Allow adding remainder amount too
Mar 6, 2024
b907de4
Merge branch 'feat/better-transitions' of https://github.com/iotaledg…
Mar 6, 2024
7165aef
Merge branch '2.0' into feat/better-transitions
Mar 7, 2024
d398cf2
Merge branch '2.0' into feat/better-transitions
Mar 7, 2024
a7501e1
transition outputs with min amount and zero mana
Mar 7, 2024
9039605
Merge branch 'feat/better-transitions' of https://github.com/iotaledg…
Mar 7, 2024
6bfaf99
Merge branch '2.0' into feat/better-transitions
Mar 7, 2024
d7d11c2
Merge branch '2.0' into feat/better-transitions
Mar 7, 2024
a70cae3
Merge branch '2.0' into feat/better-transitions
Mar 8, 2024
7eaa2ef
Update sdk/src/client/api/block_builder/transaction_builder/error.rs
Mar 8, 2024
85e449c
Merge branch '2.0' into feat/better-transitions
Mar 8, 2024
820de32
review
Mar 8, 2024
6a37e11
Merge branch 'feat/better-transitions' of https://github.com/iotaledg…
Mar 8, 2024
59443ba
while let
Mar 8, 2024
5a0d631
little cleanup
Mar 8, 2024
c3eb7f1
Merge branch '2.0' into feat/better-transitions
Mar 11, 2024
95d33bf
Merge branch '2.0' into feat/better-transitions
Mar 12, 2024
251168f
Merge branch '2.0' into feat/better-transitions
Mar 13, 2024
1ee604c
fix borked merge
Mar 14, 2024
dd36793
fix transitions and change priority logic to use a scoring system
Mar 14, 2024
6f55ad2
fix dumb test
Mar 14, 2024
2cffdde
Merge branch '2.0' into feat/better-transitions
Mar 14, 2024
177135a
Merge branch '2.0' into feat/better-transitions
Mar 15, 2024
89c044e
add score based on number of inputs
Mar 15, 2024
4a4484c
Merge branch 'feat/better-transitions' of https://github.com/iotaledg…
Mar 15, 2024
4934679
remove allotment amounts from mana gained. Re-add account mana reduct…
Mar 15, 2024
e9f6dc8
factor in remainder amounts and rework scoring
Mar 18, 2024
467dada
Merge branch '2.0' into feat/better-transitions
Mar 19, 2024
b2debee
Factor calculated allotment into mana required and fix test
Mar 19, 2024
631ed1d
do not select amount or mana that gains nothing
Mar 20, 2024
cacd339
Merge branch '2.0' into feat/better-transitions
Mar 20, 2024
7caddf4
Merge branch '2.0' into feat/better-transitions
Mar 20, 2024
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
5 changes: 4 additions & 1 deletion sdk/src/client/api/block_builder/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use alloc::collections::{BTreeMap, BTreeSet};
use serde::{Deserialize, Serialize};

use crate::{
client::api::transaction_builder::Burn,
client::api::transaction_builder::{transition::Transitions, Burn},
types::block::{
address::Address,
output::{AccountId, OutputId},
Expand All @@ -25,6 +25,8 @@ pub struct TransactionOptions {
pub tagged_data_payload: Option<TaggedDataPayload>,
/// Inputs that must be used for the transaction.
pub required_inputs: BTreeSet<OutputId>,
/// Specifies what needs to be transitioned in the transaction and how.
pub transitions: Option<Transitions>,
/// Specifies what needs to be burned in the transaction.
pub burn: Option<Burn>,
/// A string attached to the transaction.
Expand All @@ -45,6 +47,7 @@ impl Default for TransactionOptions {
remainder_value_strategy: Default::default(),
tagged_data_payload: Default::default(),
required_inputs: Default::default(),
transitions: Default::default(),
burn: Default::default(),
note: Default::default(),
allow_micro_amount: false,
Expand Down
19 changes: 18 additions & 1 deletion sdk/src/client/api/block_builder/transaction_builder/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ use super::Requirement;
use crate::types::block::{
context_input::ContextInputError,
mana::ManaError,
output::{ChainId, NativeTokenError, OutputError, OutputId, TokenId},
output::{feature::FeatureError, AccountId, ChainId, NativeTokenError, OutputError, OutputId, TokenId},
payload::PayloadError,
semantic::TransactionFailureReason,
signature::SignatureError,
slot::EpochIndex,
unlock::UnlockError,
BlockError,
};
Expand All @@ -25,9 +26,16 @@ use crate::types::block::{
pub enum TransactionBuilderError {
#[error("additional inputs required for {0:?}, but additional input selection is disabled")]
AdditionalInputsRequired(Requirement),
#[error("account {0} is already staking")]
AlreadyStaking(AccountId),
/// Can't burn and transition an output at the same time.
#[error("can't burn and transition an output at the same time, chain ID: {0}")]
BurnAndTransition(ChainId),
#[error("account {account_id} cannot end staking until {end_epoch}")]
CannotEndStaking {
account_id: AccountId,
end_epoch: EpochIndex,
},
#[error("mana rewards provided without an associated burn or custom input, output ID: {0}")]
ExtraManaRewards(OutputId),
/// Insufficient amount provided.
Expand Down Expand Up @@ -70,9 +78,15 @@ pub enum TransactionBuilderError {
/// No available inputs were provided to transaction builder.
#[error("no available inputs provided")]
NoAvailableInputsProvided,
#[error("account {0} is not staking")]
NotStaking(AccountId),
/// Required input is not available.
#[error("required input {0} is not available")]
RequiredInputIsNotAvailable(OutputId),
#[error("new staking period {additional_epochs} is less than the minimum {min}")]
StakingPeriodLessThanMin { additional_epochs: u32, min: u32 },
#[error("cannot transition non-implicit-account output {0}")]
TransitionNonImplicitAccount(OutputId),
/// Unfulfillable requirement.
#[error("unfulfillable requirement {0:?}")]
UnfulfillableRequirement(Requirement),
Expand Down Expand Up @@ -103,6 +117,9 @@ pub enum TransactionBuilderError {
/// Unlock errors.
#[error("{0}")]
Unlock(#[from] UnlockError),
/// Feature errors.
#[error("{0}")]
Feature(#[from] FeatureError),
/// Semantic errors.
#[error("{0}")]
Semantic(#[from] TransactionFailureReason),
Expand Down
73 changes: 52 additions & 21 deletions sdk/src/client/api/block_builder/transaction_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::collections::{HashMap, HashSet};
use crypto::keys::bip44::Bip44;
use packable::PackableExt;

pub use self::{burn::Burn, error::TransactionBuilderError, requirement::Requirement};
pub use self::{burn::Burn, error::TransactionBuilderError, requirement::Requirement, transition::Transitions};
use crate::{
client::{
api::{
Expand All @@ -33,8 +33,8 @@ use crate::{
input::{Input, UtxoInput, INPUT_COUNT_RANGE},
mana::ManaAllotment,
output::{
AccountId, AccountOutputBuilder, AnchorOutputBuilder, BasicOutputBuilder, NftOutputBuilder, Output,
OutputId, OUTPUT_COUNT_RANGE,
AccountId, AccountOutputBuilder, BasicOutputBuilder, ChainId, NftOutputBuilder, Output, OutputId,
OUTPUT_COUNT_RANGE,
},
payload::{
signed_transaction::{Transaction, TransactionCapabilities, TransactionCapabilityFlag},
Expand Down Expand Up @@ -189,6 +189,7 @@ pub struct TransactionBuilder {
provided_outputs: Vec<Output>,
added_outputs: Vec<Output>,
addresses: HashSet<Address>,
transitions: Option<Transitions>,
burn: Option<Burn>,
remainders: Remainders,
creation_slot: SlotIndex,
Expand Down Expand Up @@ -216,7 +217,8 @@ pub(crate) struct Remainders {
address: Option<Address>,
data: Vec<RemainderData>,
storage_deposit_returns: Vec<Output>,
added_mana: u64,
added_amount: HashMap<Option<ChainId>, u64>,
added_mana: HashMap<Option<ChainId>, u64>,
}

impl TransactionBuilder {
Expand Down Expand Up @@ -261,6 +263,7 @@ impl TransactionBuilder {
provided_outputs: outputs.into_iter().collect(),
added_outputs: Vec::new(),
addresses,
transitions: None,
burn: None,
remainders: Default::default(),
creation_slot: creation_slot_index.into(),
Expand Down Expand Up @@ -379,25 +382,47 @@ impl TransactionBuilder {
return Err(TransactionBuilderError::InvalidInputCount(self.selected_inputs.len()));
}

if self.remainders.added_mana > 0 {
let remainder_address = self
.get_remainder_address()?
.ok_or(TransactionBuilderError::MissingInputWithEd25519Address)?
.0;
let added_mana = self.remainders.added_mana;
if let Some(output) = self.get_output_for_added_mana(&remainder_address) {
log::debug!("Adding {added_mana} excess input mana to output with address {remainder_address}");
let remainder_address = self
.get_remainder_address()?
.ok_or(TransactionBuilderError::MissingInputWithEd25519Address)?
.0;

let mut added_amount_mana = HashMap::<Option<ChainId>, (u64, u64)>::new();
for (chain_id, added_amount) in self.remainders.added_amount.drain() {
added_amount_mana.entry(chain_id).or_default().0 = added_amount;
}
for (chain_id, added_mana) in self.remainders.added_mana.drain() {
added_amount_mana.entry(chain_id).or_default().1 = added_mana;
}

for (chain_id, (added_amount, added_mana)) in added_amount_mana {
Thoralf-M marked this conversation as resolved.
Show resolved Hide resolved
let mut output = self.get_output_for_remainder(chain_id, &remainder_address);
if output.is_none() {
output = self.get_output_for_remainder(None, &remainder_address);
}
if let Some(output) = output {
log::debug!(
"Adding {added_amount} excess amount and {added_mana} excess mana to output with address {remainder_address} and {chain_id:?}"
);
let new_amount = output.amount() + added_amount;
let new_mana = output.mana() + added_mana;
*output = match output {
Output::Basic(b) => BasicOutputBuilder::from(&*b).with_mana(new_mana).finish_output()?,
Output::Account(a) => AccountOutputBuilder::from(&*a).with_mana(new_mana).finish_output()?,
Output::Anchor(a) => AnchorOutputBuilder::from(&*a).with_mana(new_mana).finish_output()?,
Output::Nft(n) => NftOutputBuilder::from(&*n).with_mana(new_mana).finish_output()?,
Output::Basic(b) => BasicOutputBuilder::from(&*b)
.with_amount(new_amount)
.with_mana(new_mana)
.finish_output()?,
Output::Account(a) => AccountOutputBuilder::from(&*a)
.with_amount(new_amount)
.with_mana(new_mana)
.finish_output()?,
Output::Nft(n) => NftOutputBuilder::from(&*n)
.with_amount(new_amount)
.with_mana(new_mana)
.finish_output()?,
_ => unreachable!(),
};
}
}

// If we're burning generated mana, set the capability flag.
if self.burn.as_ref().map_or(false, |b| b.generated_mana()) {
// Get the mana sums with generated mana to see whether there's a difference.
Expand Down Expand Up @@ -512,25 +537,31 @@ impl TransactionBuilder {
Ok(if added_output { self.added_outputs.last() } else { None })
}

/// Sets the required inputs of an [`TransactionBuilder`].
/// Sets the required inputs of a [`TransactionBuilder`].
pub fn with_required_inputs(mut self, inputs: impl IntoIterator<Item = OutputId>) -> Self {
self.required_inputs = inputs.into_iter().collect();
self
}

/// Sets the burn of an [`TransactionBuilder`].
/// Sets the transitions of a [`TransactionBuilder`].
pub fn with_transitions(mut self, transitions: impl Into<Option<Transitions>>) -> Self {
self.transitions = transitions.into();
self
}

/// Sets the burn of a [`TransactionBuilder`].
pub fn with_burn(mut self, burn: impl Into<Option<Burn>>) -> Self {
self.burn = burn.into();
self
}

/// Sets the remainder address of an [`TransactionBuilder`].
/// Sets the remainder address of a [`TransactionBuilder`].
pub fn with_remainder_address(mut self, address: impl Into<Option<Address>>) -> Self {
self.remainders.address = address.into();
self
}

/// Sets the mana allotments of an [`TransactionBuilder`].
/// Sets the mana allotments of a [`TransactionBuilder`].
pub fn with_mana_allotments(mut self, mana_allotments: impl IntoIterator<Item = (AccountId, u64)>) -> Self {
self.mana_allotments = mana_allotments.into_iter().collect();
self
Expand Down
Loading
Loading