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

Split provided outputs in ISA #2089

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 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
5 changes: 4 additions & 1 deletion bindings/core/src/method/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,10 @@ pub enum WalletMethod {
/// Prepare transaction.
/// Expected response: [`PreparedTransaction`](crate::Response::PreparedTransaction)
PrepareTransaction {
outputs: Vec<Output>,
#[serde(default)]
immutable_outputs: Vec<Output>,
#[serde(default)]
mutable_outputs: Vec<Output>,
#[serde(default)]
options: Option<TransactionOptions>,
},
Expand Down
10 changes: 8 additions & 2 deletions bindings/core/src/method_handler/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,14 @@ pub(crate) async fn call_wallet_method_internal(wallet: &Wallet, method: WalletM
// let data = wallet.prepare_stop_participating(event_id).await?;
// Response::PreparedTransaction(data)
// }
WalletMethod::PrepareTransaction { outputs, options } => {
let data = wallet.prepare_transaction(outputs, options).await?;
WalletMethod::PrepareTransaction {
immutable_outputs,
mutable_outputs,
options,
} => {
let data = wallet
.prepare_transaction(mutable_outputs, immutable_outputs, options)
.await?;
Response::PreparedTransaction(data)
}
// #[cfg(feature = "participation")]
Expand Down
38 changes: 25 additions & 13 deletions sdk/src/client/api/block_builder/input_selection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ pub struct InputSelection {
forbidden_inputs: HashSet<OutputId>,
selected_inputs: Vec<InputSigningData>,
context_inputs: HashSet<ContextInput>,
provided_outputs: Vec<Output>,
added_outputs: Vec<Output>,
immutable_outputs: Vec<Output>,
mutable_outputs: Vec<Output>,
addresses: HashSet<Address>,
burn: Option<Burn>,
remainders: Remainders,
Expand Down Expand Up @@ -85,7 +85,6 @@ impl InputSelection {
/// Creates a new [`InputSelection`].
pub fn new(
available_inputs: impl IntoIterator<Item = InputSigningData>,
outputs: impl IntoIterator<Item = Output>,
addresses: impl IntoIterator<Item = Address>,
creation_slot_index: impl Into<SlotIndex>,
latest_slot_commitment_id: SlotCommitmentId,
Expand Down Expand Up @@ -119,8 +118,8 @@ impl InputSelection {
forbidden_inputs: HashSet::new(),
selected_inputs: Vec::new(),
context_inputs: HashSet::new(),
provided_outputs: outputs.into_iter().collect(),
added_outputs: Vec::new(),
immutable_outputs: Vec::new(),
mutable_outputs: Vec::new(),
addresses,
burn: None,
remainders: Default::default(),
Expand Down Expand Up @@ -199,13 +198,14 @@ impl InputSelection {
/// Selects inputs that meet the requirements of the outputs to satisfy the semantic validation of the overall
/// transaction. Also creates a remainder output and chain transition outputs if required.
pub fn select(mut self) -> Result<PreparedTransactionData, Error> {
if !OUTPUT_COUNT_RANGE.contains(&(self.provided_outputs.len() as u16)) {
let num_provided_outputs = self.immutable_outputs.len() + self.mutable_outputs.len();
if !OUTPUT_COUNT_RANGE.contains(&(num_provided_outputs as u16)) {
// If burn or mana allotments are provided, outputs will be added later, in the other cases it will just
// create remainder outputs.
if !self.provided_outputs.is_empty()
if num_provided_outputs > 0
|| (self.burn.is_none() && self.mana_allotments.is_empty() && self.required_inputs.is_empty())
{
return Err(Error::InvalidOutputCount(self.provided_outputs.len()));
return Err(Error::InvalidOutputCount(num_provided_outputs));
}
}

Expand Down Expand Up @@ -271,9 +271,9 @@ impl InputSelection {
}

let outputs = self
.provided_outputs
.immutable_outputs
.into_iter()
.chain(self.added_outputs)
.chain(self.mutable_outputs)
.chain(self.remainders.storage_deposit_returns)
.chain(self.remainders.data.iter().map(|r| r.output.clone()))
.collect::<Vec<_>>();
Expand Down Expand Up @@ -343,8 +343,8 @@ impl InputSelection {
// - the issuer feature doesn't need to be verified as the chain is not new
// - input doesn't need to be checked for as we just transitioned it
// - foundry account requirement should have been met already by a prior `required_account_nft_addresses`
self.added_outputs.push(output);
added_output = self.added_outputs.last();
self.mutable_outputs.push(output);
added_output = self.mutable_outputs.last();
}

if let Some(requirement) = self.required_account_nft_addresses(&input)? {
Expand All @@ -362,6 +362,18 @@ impl InputSelection {
Ok(added_output)
}

/// Sets outputs that can be mutated by [`InputSelection`].
pub fn with_mutable_outputs(mut self, outputs: impl IntoIterator<Item = Output>) -> Self {
self.mutable_outputs = outputs.into_iter().collect();
self
}

/// Sets outputs that cannot be mutated by [`InputSelection`].
pub fn with_immutable_outputs(mut self, outputs: impl IntoIterator<Item = Output>) -> Self {
self.immutable_outputs = outputs.into_iter().collect();
self
}

/// Sets the required inputs of an [`InputSelection`].
pub fn with_required_inputs(mut self, inputs: impl IntoIterator<Item = OutputId>) -> Self {
self.required_inputs = inputs.into_iter().collect();
Expand Down Expand Up @@ -446,7 +458,7 @@ impl InputSelection {
}

pub(crate) fn non_remainder_outputs(&self) -> impl Iterator<Item = &Output> {
self.provided_outputs.iter().chain(&self.added_outputs)
self.immutable_outputs.iter().chain(&self.mutable_outputs)
}

pub(crate) fn remainder_outputs(&self) -> impl Iterator<Item = &Output> {
Expand Down
5 changes: 2 additions & 3 deletions sdk/src/client/api/block_builder/input_selection/remainder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ impl InputSelection {

fn output_for_added_mana_exists(&self, remainder_address: &Address) -> bool {
// Find the first value that matches the remainder address
self.non_remainder_outputs().any(|o| {
self.mutable_outputs.iter().any(|o| {
(o.is_basic() || o.is_account() || o.is_anchor() || o.is_nft())
&& o.unlock_conditions()
.map_or(true, |uc| uc.expiration().is_none() && uc.timelock().is_none())
Expand All @@ -187,9 +187,8 @@ impl InputSelection {
]);
// Remove those that do not have an ordering and sort
let ordered_outputs = self
.provided_outputs
.mutable_outputs
.iter_mut()
.chain(&mut self.added_outputs)
.filter(|o| {
o.unlock_conditions()
.map_or(true, |uc| uc.expiration().is_none() && uc.timelock().is_none())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ impl InputSelection {

fn reduce_funds_of_chains(&mut self, amount_selection: &mut AmountSelection) -> Result<(), Error> {
// Only consider automatically transitioned outputs.
for output in self.added_outputs.iter_mut() {
for output in self.mutable_outputs.iter_mut() {
let diff = amount_selection.missing_amount();
let amount = output.amount();
let minimum_amount = output.minimum_amount(self.protocol_parameters.storage_score_parameters());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ impl InputSelection {
needs_commitment_context = true;
}
}
for output in self
.provided_outputs
.iter_mut()
.chain(&mut self.added_outputs)
.filter(|o| o.is_delegation())
{
if self.non_remainder_outputs().any(|o| o.is_delegation()) {
log::debug!("Adding commitment context input for delegation output");
needs_commitment_context = true;
}
// TODO: move this to the delegation functions
for output in self.mutable_outputs.iter_mut().filter(|o| o.is_delegation()) {
// Created delegations have their start epoch set, and delayed delegations have their end set
if output.as_delegation().delegation_id().is_null() {
let start_epoch = self
Expand All @@ -77,8 +77,6 @@ impl InputSelection {
.with_end_epoch(end_epoch)
.finish_output()?;
}
log::debug!("Adding commitment context input for delegation output");
needs_commitment_context = true;
}
// BlockIssuanceCreditContextInput requires a CommitmentContextInput.
if self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,8 @@ impl InputSelection {
.as_mut()
.ok_or(Error::UnfulfillableRequirement(Requirement::Mana))?;
if let Some(output) = self
.provided_outputs
.mutable_outputs
.iter_mut()
.chain(&mut self.added_outputs)
.filter(|o| o.is_account() && o.mana() != 0)
.find(|o| o.as_account().account_id() == issuer_id)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl InputSelection {
pub(crate) fn outputs_requirements(&mut self) {
let inputs = self.available_inputs.iter().chain(self.selected_inputs.iter());

for output in self.provided_outputs.iter().chain(&self.added_outputs) {
for output in self.immutable_outputs.iter().chain(&self.mutable_outputs) {
let is_created = match output {
// Add an account requirement if the account output is transitioning and then required in the inputs.
Output::Account(account_output) => {
Expand Down
1 change: 1 addition & 0 deletions sdk/src/wallet/operations/output_claiming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ where
self.prepare_transaction(
// We only need to provide the NFT outputs, ISA automatically creates basic outputs as remainder outputs
nft_outputs_to_send,
None,
TransactionOptions {
required_inputs: outputs_to_claim
.iter()
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/wallet/operations/output_consolidation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ where
..Default::default()
});

self.prepare_transaction([], options).await
self.prepare_transaction(None, None, options).await
}

/// Determines whether an output should be consolidated or not.
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/wallet/operations/transaction/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ where
..Default::default()
};

self.prepare_transaction(vec![account], transaction_options.clone())
self.prepare_transaction(vec![account], None, transaction_options.clone())
.await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ where
*options.mana_allotments.entry(account_id).or_default() += mana;
}

self.prepare_transaction([], options).await
self.prepare_transaction(None, None, options).await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ where
)?))
.finish_output()?];
// Input selection will detect that we're melting native tokens and add the required inputs if available
self.prepare_transaction(outputs, options).await
self.prepare_transaction(outputs, None, options).await
}

/// Find and return unspent `OutputData` for given `account_id` and `foundry_id`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ impl Wallet {

// The empty list of outputs is used. Outputs will be generated by
// the input selection algorithm based on the content of the [`Burn`] object.
self.prepare_transaction([], options).await
self.prepare_transaction(None, None, options).await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ where

let outputs = [account_output_builder.finish_output()?];

self.prepare_transaction(outputs, options).await
self.prepare_transaction(outputs, None, options).await
}

/// Gets an existing account output.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ where
.add_unlock_condition(AddressUnlockCondition::new(address))
.finish_output()?;

let transaction = self.prepare_transaction([output], options).await?;
let transaction = self.prepare_transaction([output], None, options).await?;

Ok(PreparedCreateDelegationTransaction {
delegation_id: DelegationId::from(&transaction.transaction.id().into_output_id(0)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,6 @@ where
}
};

self.prepare_transaction(outputs, None).await
self.prepare_transaction(outputs, None, None).await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ where
}, // Native Tokens will be added automatically in the remainder output in try_select_inputs()
];

self.prepare_transaction(outputs, options)
self.prepare_transaction(outputs, None, options)
.await
.map(|transaction| PreparedCreateNativeTokenTransaction { token_id, transaction })
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,6 @@ where
// Native Tokens will be added automatically in the remainder output in try_select_inputs()
];

self.prepare_transaction(outputs, options).await
self.prepare_transaction(outputs, None, options).await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,6 @@ where
outputs.push(nft_builder.finish_output()?);
}

self.prepare_transaction(outputs, options).await
self.prepare_transaction(outputs, None, options).await
}
}
2 changes: 1 addition & 1 deletion sdk/src/wallet/operations/transaction/high_level/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,6 @@ where
}
}

self.prepare_transaction(outputs, options).await
self.prepare_transaction(outputs, None, options).await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,6 @@ where

let output = output_builder.finish_output()?;

self.prepare_transaction(vec![output], options).await
self.prepare_transaction(vec![output], None, options).await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,6 @@ where
)
}

self.prepare_transaction(outputs, options).await
self.prepare_transaction(outputs, None, options).await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,6 @@ where
};
}

self.prepare_transaction(outputs, options).await
self.prepare_transaction(outputs, None, options).await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ where
))
.finish_output()?;

let transaction = self.prepare_transaction([output], options).await?;
let transaction = self.prepare_transaction([output], None, options).await?;

Ok(transaction)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ where
.with_features(features)
.finish_output()?;

let transaction = self.prepare_transaction([output], options).await?;
let transaction = self.prepare_transaction([output], None, options).await?;

Ok(transaction)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ where

let output = output_builder.finish_output()?;

let transaction = self.prepare_transaction([output], options).await?;
let transaction = self.prepare_transaction([output], None, options).await?;

Ok(transaction)
}
Expand Down
Loading
Loading