Various mint bugfixes for swap and melt. SIG_INPUTS+SIG_ALL, locktimes, P2PK+HTLC. Also updates the SIG_ALL message for amount-switching #1212
No reviewers
Labels
No labels
DB & Storage
Deployment
Error Handling & Logging
Maintenance
Payment Backend
backport
backport v0.13.x
backport v0.14.x
backport v0.15.x
bindings
blocked
bug
cdk-sql
ci
cli
deps
documentation
duplicate
enhancement
good first issue
help wanted
invalid
keep-open
ldk-node-ui
migrations
mint
mutation-testing
needs rebase
needs review
new nut
nut change
question
ready
rust-version
rustfmt
stacked hold
stale
testing
wallet
weekly-report
wontfix
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
cashubtc/cdk!1212
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "verify_nut_10_spending_conditions"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Description
This includes updates for the protocol (https://github.com/cashubtc/nuts/pull/302) around how the message for SIG_ALL is constructed (with amount and secret, not keyset_id). However, even before that protocol update was proposed, there were a number of bugs in the mint, and the protocol update depends on those fixes. This PR includes these bugfixes too
SpendingConditionsVerification, which is shared by swap and melt. This centralizes all checking of NUT-10 spending conditions (P2PK, HTLC, SIG_ALL/SIG_INPUTS, locktimes, refunds) into one place. Code re-use is a priority in this design, in order to fix the bugs.process_swap_requestandMint.meltto use this trait with a single simple call to.verify_spending_conditions.nuts/tests/11-test.mdSome issues fixed by this:
verify_p2pkin particular) would reject the SIG_ALL, as it didn't understand SIG_ALL.Testing
Notes
Some smaller details:
get_pubkeys_and_required_sigs_for_p2pkis implemented, to ease the identification of the relevant keys and the number of signatures required. It now also returns a boolean to specifiy if a preimage is required; this is to support HTLC. It compares the current time to the locktime and identifies the relevant keys. This PR uses this function for SIG_ALL and SIG_INPUTS cases, to encourage code reuse.verify_p2pk, which is a method onProof, is rewritten to remove the bug mentioned above and to use the new shared code in order to ensure consistency between SIG_INPUTS and SIG_ALL.verify_p2pkalso asserts that it's NOT called with SIG_ALL; as SIG_ALL verification shouldn't callverify_p2pkcan_sign_sig_allis removed. It's unnecessary to have a wallet-side check of whether a SIG_ALL is valid. If we duplicate the code in the wallet and mint, there is a risk of a discrepancy. (Option: when the wallet creates a swap_request, maybe the wallet could verify it usingverify_spending_conditions_for_swapif we want?)Suggested CHANGELOG Updates
CHANGED
ADDED
REMOVED
FIXED
Checklist
just final-checkbefore committing@ -189,1 +188,3 @@}// Check if we have enough valid signaturesif valid_sig_count >= relevant_num_sigs_required {Ok(())I think these should be more than debug asserts and should return errors at runtime.
Debug asserts should be used for checking our developer assumptions and only in places where we can only reach them in private functions, for example. For things that a user could do, like calling sign_p2pk on the wrong proof type, we should return an error.
I think we can remove these comments. In general, comments are really helpful and we should use more of them to describe why we're doing something rather than how. Since
extend()works directly on the Vec here and the initialization flow is pretty clear from the code itself, these comments might be a bit redundant.That said, leaning towards more comments is a good instinct
@ -105,6 +108,431 @@ impl Secret {}Does this need to be public? Maybe can be just public to the crate seems like an internal fn.
Save a word?
@ -108,0 +165,4 @@let data_pubkey = PublicKey::from_str(secret.secret_data().data())?;primary_keys.push(data_pubkey);// Add any additional pubkeys from conditionsThese are helpful comments. May make sense to even link to parts of spec.
@ -108,0 +376,4 @@// Check if secret is a nut10 secret with conditionsif let Ok(secret) = Secret::try_from(&proof.secret) {// Verify this function isn't being called with SIG_ALL proofs (development check)if let Ok(conditions) = super::Conditions::try_from(Do we check this?
@ -887,132 +860,8 @@ impl From<Tag> for Vec<String> {}impl SwapRequest {I think this version number is wrong. But we don't need to mark as deprecated we can just remove it if its not needed or wrong. We will include it in a minor release since we're pre 1.0 its okay
@ -1246,4 +919,1 @@Err(Error::SpendConditionsNotMet)}}}Lets make sure its right before merging.
Can you clarify this comment.
Should return errors here as well not debug asserts
@ -189,1 +188,3 @@}// Check if we have enough valid signaturesif valid_sig_count >= relevant_num_sigs_required {Ok(())Agreed. I'll return an error to the user instead of the
debug_assert. I'll add new errors in some cases:I assumed (without thinking really) that
verify_p2pkwas private and wouldn't be called directly by the user. But I was wrong, it's public and users might therefore call it directly.====
Thanks for all the comments yesterday, I'll try to work through them today
Agreed. I've removed those comments. I'll push it later today
@ -887,132 +860,8 @@ impl From<Tag> for Vec<String> {}impl SwapRequest {Agreed. I've removed it (
verify_sig_all), and will push it later todayThere is another option, if anybody wants to keep a public function with the same name and which is about SIG_ALL checks. The trait gives this class a function called
verify_full_sig_all_check. We could rename that function toverify_sig_alland make it public@ -1246,4 +919,1 @@Err(Error::SpendConditionsNotMet)}}}I've now removed that, including that warning.
I added that warning earlier, to remind myself to look at that function. Anyway, it's removed now. It wasn't handling locktime
Done. Errors instead of
debug_assertI've rewritten that comment in the code. Copied here also:
@ -105,6 +108,431 @@ impl Secret {}You're right. Changed from
pubtopub(crate)@ -105,6 +108,431 @@ impl Secret {}Agreed. Changing from
VerificationForSpendingConditionstoSpendingConditionVerification@ -108,0 +376,4 @@// Check if secret is a nut10 secret with conditionsif let Ok(secret) = Secret::try_from(&proof.secret) {// Verify this function isn't being called with SIG_ALL proofs (development check)if let Ok(conditions) = super::Conditions::try_from(That function is private and is only called from one place, where
verify_spending_conditionshas already verified that there is no SIG_ALL in any input. So the checking is done thereI think I've read and dealt with all the comments that you made yesterday, @thesimplekid
Although, before I forget, there are a couple of strange things about this PR now:
#[ignore]. If I remember correctly, all of them are based on the test vectors for SIG_ALL and I haven't updated them into this PR yetdataandpreimageneed to be converted to and from hex. The tests currently pass, but my code her perhaps doesn't follow the spec. I had hoped that theHTLCWitnessobject would (de-)serialize automatically, or perhaps have a method to do any conversion@ -108,0 +376,4 @@// Check if secret is a nut10 secret with conditionsif let Ok(secret) = Secret::try_from(&proof.secret) {// Verify this function isn't being called with SIG_ALL proofs (development check)if let Ok(conditions) = super::Conditions::try_from(We should add a debug assert then since its something in our control we're committing not to do.
Here's an update on the latest batch of changes, just pushed above:
verify_htlc_preimagewhich does the hex processing and verifies that the preimage has the required hash. This is shared between both the SIG_INPUTS path and the SIG_ALL path. Also, a new methodpreimage_datainHTLCWitnessto help with this. And tidied upcreate_test_hash_and_preimageas a test helper in these tests.process_swap_requestandprocess_melt_requestare making the right decision to accept or reject. Our goal is not just to have correct individual functions, such asProof.verify_htlc(), but to ensure that they are being calling correctly byprocess_*_requestNext, I'll look at @lescuer97's #1208. I'll probably do a
git checkoutand then run those 29 tests from this PR (they're all under/crates/cdk/src/mint/swap/spending_conditions, in order to see how that code does with those tests.TODOs (today hopefully):
just format, and anything else related to linting#[ignore]tests? Maybe can be activated again the with new test vectors. Answer: resolved, those tests have now been updated to the new test vectors that are copied in fromnuts/tests/11-test.mdverify_inputsnow doing redundant checks? (Answer: no, it's not. Nothing go do hereNice work mostly superficial organization comments other then that looks good.
@ -91,6 +91,32 @@ impl SwapRequest {}We don't include the keyset_id anymore right?
Same here we don't include keyset_id
Can you use where here https://github.com/cashubtc/cdk/blob/main/CODE_STYLE.md#generics
@ -108,0 +194,4 @@witness: &super::nut14::HTLCWitness,secret: &Secret,) -> Result<(), super::nut14::Error> {use bitcoin::hashes::sha256::Hash as Sha256Hash;Can we move this to the top with other imports
@ -0,0 +21,4 @@use crate::amount::to_unit;use crate::nuts::MeltQuoteState;use crate::types::PaymentProcessorKey;use crate::util::unix_time;We should rename this test mod to just test and not spending conditions.
I don't think we should need the cfg test here since the whole mod is a test.
@ -3,12 +3,15 @@ use cdk_prometheus::METRICS;use swap_saga::SwapSaga;We should rename this test mod to just test and not spending conditions.
Correct. This comment is out of date
@ -91,6 +91,32 @@ impl SwapRequest {}Correct. This comment is out of date
I've just pushed a few changes. I think I've taken account of all remaining review comments, @thesimplekid
wherewith generics, and brought theuse std...to the topspending_conditionsfolders totests, as they contain just teststest_helpersfoldercfg(test)linesjust formatorigin/main@ -108,0 +165,4 @@let data_pubkey = PublicKey::from_str(secret.secret_data().data())?;primary_keys.push(data_pubkey);// Add any additional pubkeys from conditionsI added a few more words, including quotes from the NUTS, to the comment at the top of this code