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

Prepared Send #596

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open

Prepared Send #596

wants to merge 33 commits into from

Conversation

davidcaseria
Copy link
Contributor

@davidcaseria davidcaseria commented Feb 11, 2025

Description

Prepare sends by selecting proofs and returning a fee amount before finalizing a send into a token.


Notes to the reviewers


Suggested CHANGELOG Updates

CHANGED

  • Unifies and optimizes the proof selection algorithm to use Wallet::select_proofs.
  • Wallet::send now requires a PreparedSend.
  • WalletDatabase proof state update functions have been consolidated into update_proofs_state.

ADDED

  • Sends should be initiated by calling Wallet::prepare_send.
  • A SendOptions struct controls optional functionality for sends.
  • Allow Amount splitting to target a fee rate amount.
  • Utility functions for Proofs.
  • Utility functions for SendKind.
  • Completed checked arithmetic operations for Amount (i.e., checked_mul and checked_div).

REMOVED

FIXED


Checklist

@davidcaseria davidcaseria marked this pull request as ready for review February 15, 2025 02:03
@thesimplekid thesimplekid self-requested a review February 18, 2025 14:34
@davidcaseria
Copy link
Contributor Author

@thesimplekid two areas I'm looking for feedback:

  1. How should we support CDK users who want to bring their own PreparedSend logic? I made PreparedSend a "private" structure so that users can't modify it, causing issues with our logic.
  2. How should we support memos? I have it in SendOptions but I think I should move it to be a parameter of the send function, possibly with a flag to include it in the Token or keep it internal with the eventual TransactionsDatabase.

Copy link
Collaborator

@thesimplekid thesimplekid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on this PR.

How should we support CDK users who want to bring their own PreparedSend logic? I made PreparedSend a "private" structure so that users can't modify it, causing issues with our logic.

I think we can just added a PreparedSend::new that way they can at least create a PreparedSend to pass to send. But I think maybe better to not worry to much about this for now to keep it simpler and if that need arises we can revisit.

How should we support memos? I have it in SendOptions but I think I should move it to be a parameter of the send function, possibly with a flag to include it in the Token or keep it internal with the eventual TransactionsDatabase.
I agree I think we can move it to the send fn maybe we can have two and internal and an external (included in token) memo and when the former is not set use the external one.

@@ -35,6 +35,8 @@ pub enum State {
///
/// i.e. used to create a token
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not longer used when a proof is included in a token as that is PendingSpent

Comment on lines +106 to +108
pub fn has_tolerance(&self) -> bool {
matches!(self, Self::OnlineTolerance(_) | Self::OfflineTolerance(_))
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful is this returned the tolerance? Option<Amount> instead of a bool?

Comment on lines +388 to +394
selected_proofs.extend(Wallet::select_proofs(
remaining_amount,
remaining_proofs,
active_keyset_id,
&HashMap::new(), // Fees are already calculated
false,
)?);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this handle the edge case where the proof added to pay the fee increased the fee?

Comment on lines +55 to +70
.get_proofs(
Some(self.mint_url.clone()),
Some(self.unit.clone()),
Some(vec![State::Unspent]),
None,
)
.await?
.into_iter()
.filter_map(|p| {
if p.spending_condition.is_none() {
Some(p.proof)
} else {
None
}
})
.collect();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This filter is required because of what you mentioned previously where the get_proofs will return proofs with conditions even if None is passed. I think we should update the get_proofs fn to handle this correctly to avoid the filter here.

I can do this as a small focused pr ahead of merging this. #611

amount: Amount,
proofs: Proofs,
active_keyset_id: Id,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be a Vec<Id> the nuts allow for mints to have multiple active keysets. Cdk mint limits this on the implementation level to only have one active keyset per unit, however on the wallet side I do not think we should assume this and should account for multiple as it is in spec.

Comment on lines +246 to +249
// Update proofs state to pending spent
self.localstore
.update_proofs_state(proofs_to_send.ys()?, State::PendingSpent)
.await?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may have missed it but was the proof set to Reserved in prepare_send. Am I correct that the expectation is the once a proof is selected as part of a prepared send it is reserved and cannot be used in other operations unless explicitly unreserved? This way prepare_send can be called multiple times without conflicting and we do not have to block other operations.

Can we add a doc comment to the send/prepare_send fn explaining this expectation.

}
}

/// Prepared send
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just add a few more lines explaining how the proofs_to_swap vs proofs_to_send should be uses and what they are?

pub amount_split_target: SplitTarget,
/// Send kind
pub send_kind: SendKind,
/// Include fee
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Include fee
/// Include fee
///
/// When this is true the token created will include the amount of fees needed to redeem the token (amount + fee_to_redeem)

/// Send kind
pub send_kind: SendKind,
/// Include fee
pub include_fee: bool,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the better ux is the default behavior is to include the fee always. Do we think that should be the default her in SendOptions or that should be done higher up the stack?

Comment on lines +209 to +211
self.localstore
.update_proofs_state(ys, State::Pending)
.await?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should use Reserved here, and keep Pending as a state used by the mint ie in a melt operation where the ln payment is taking some time.

@thesimplekid
Copy link
Collaborator

Have you done any testing around does this change the proofs that would be selected from a set from previous cdk versions? Do you expect it to?

I would be interested to include some integrations tests on this so we don't change proofs selection accidentally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants