-
Notifications
You must be signed in to change notification settings - Fork 208
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
Add dedicated methods for verifying asset/value proofs #195
base: master
Are you sure you want to change the base?
Add dedicated methods for verifying asset/value proofs #195
Conversation
apoelstra
commented
Jul 22, 2022
•
edited
Loading
edited
- implement value proof verification
- implement asset proof verification
- add more tests
3cd525b
to
2f4b275
Compare
/** Verify a rangeproof with a single-value range. Useful as a "proof of value" | ||
* of a Pedersen commitment. Such proofs can be created with `secp256k1_rangeproof_sign` | ||
* by passing an `exp` parameter of -1 and the target value as both `value` and `min_value`. | ||
* Returns 1: Proof was valid and proved the given value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it also the case that min_bits
must be 0 for the rangeproof, (as elements generates it) - should this be documented here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just checked, and interestingly no -- you can set min_bits
to anything and it'll just be ignored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment to this effect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Maybe that part of the comment should be moved to rangeproof_sign
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You gave a ":+1:" but I think this hasn't been fixed.
2f4b275
to
593686c
Compare
utack 9eea081 I don't have time at this stage to test this; wally uses the full proof validation code for blind asset/value proofs since we use that code already for blinding. Once PSBTv2 is in I should be able to switch over and make sure our tests still pass, may be a while though. From an API perspective, it may be nice to have create_value variants for creating single value proofs that take the reduced set of parameters and call the full functions with the repeated/defaulted values (even if its not worth a custom implementation of the actual proof creation). |
Agreed, |
I have added a The memory usage of |
08dfa68
to
9d28292
Compare
@apoelstra rangeproof was the painful case for me, so thanks for that, LGTM. wally (following the Elements impl) only creates surjection proofs with a maximum of 3 used inputs, so for generation even the reduced mode has 12 more unused stack elements than we need. However, we don't enable reduced mode so that we can verify proofs created by others since wally is available for use as a general purpose lib. In an ideal world we'd be able to specify the number we use instead of having a fixed limit, and even better be able to generate with one limit and verify with another. I can see Jade/bitbox using 3/16 for create/verify for example assuming they can both verify on device. |
Note for You could use a narrower unsigned type for |
@jgriffiths i actually have a massive branch (which will need to be rebased now since i reused bits and pieces for this) whose goal is to dramatically reduce the stack space of the rangeproof code. Unfortunately it required a near rewrite of all the logic, pulling the borromean sig stuff out of its own functions and inlining it in the rangeproof/surjectionproof code. Essentially the way it works is by directly using the I'm quite proud of it but I don't think we have the review bandwidth to do anything with it :/ and I'm not sure that I finished it. |
#160 is the start of it |
The eternal problem lol. At this stage things are fine for us; its only if the hardware wallets have issues that we may need to look at lowering resource usage. That time is creeping forward for Jade at least, since the qrcode handling has been improved to the point where we should be able to take psbts from the camera to sign. But until we are ready to resource that we can't commit anyone to review or test either. In the meantime though I'd remove the redundant memset just because its, well, redundant :) |
Have you looked at how we do animated QR PSBTs? A number of both hardware & software bitcoin wallets and services now support it. It is quite mature, has been ported to multiple languages, and has other advantages. |
Hi @ChristopherA - yes, I believe we are targeting that format, however this PR and repo aren't the place to discuss it. Please follow https://github.com/Blockstream/Jade where the PR adding support will land after internal review, or raise an issue there if you have questions/comments, thanks. |
edit: Oh, sorry, I see you're saying it's really redundant because there's another memset that zeroes the array already... Yes, sure, let's remove it... old: So in sense, the dead memsets in the code base currently act as a marker for where we should do proper overwriting in the future, and so we should keep them. |
I don't know how other reviewers feel but I think I'd need some refresher on how the surjection proofs work before I'm a position to review this. Or maybe @apoelstra can walk one of us through the changes. |
@real-or-random the surjection proofs work by taking a single output commitment, a variable number of input commitments, and then doing a ring signature over (output - input_i) for all i. The premise is that the user will only be able to construct such a ring signature if the output is merely a reblinding of one of the inputs. Everything else in this code is just mechanical stuff to make the commitment structure and proof serialization match the deployed surjection proofs in Elements. |
Oh -- and to add, a "ring signature" is just a Schnorr signature in both cases, since we're proving a single value so there is only one key to check. |
@real-or-random I'm going to make another PR on top of this which pulls the asset ID/entropy logic from Elements Core into secp-zkp, so the sooner we can merge this the simpler my life will be. |
Actually, scrap that. On further inspection I think the API separation is already fine. |
Ok I'll try to have a look tomorrow. :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for my the understanding: The benefit here is simply efficiency, right?
In general, I think we could add some more tests that test consistency with the ordinary functions, maybe also with bitflips (because we have many and "bitmaps"/"flags").
src/modules/rangeproof/tests_impl.h
Outdated
test_single_value_proof(0); | ||
test_single_value_proof(12345678); | ||
test_single_value_proof(UINT64_MAX); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: won't hurt to add some randomized cases, too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe add a small loop?
/** Verify a rangeproof with a single-value range. Useful as a "proof of value" | ||
* of a Pedersen commitment. Such proofs can be created with `secp256k1_rangeproof_sign` | ||
* by passing an `exp` parameter of -1 and the target value as both `value` and `min_value`. | ||
* Returns 1: Proof was valid and proved the given value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Maybe that part of the comment should be moved to rangeproof_sign
?
|
||
/* Now we just have a Schnorr signature in (e, s) form. The verification | ||
* equation is e == H(sG - eX || proof params), where X is the difference | ||
* between the output and input. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to extract this code into a single function used by rangeproofs and surjectionproofs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It differs in just enough places that I don't think it's practical.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see, between rangeproofs and surjection proofs ... yes, that might make sense. I thought you meant between proving and verifying.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think it would make sense (e.g., it would have prevented the issue with the duplicate comment, and it will make you change memcmp
only once. ;)) But your goal of making them self-consistent is also good. Whatever you prefer.
src/modules/rangeproof/main_impl.h
Outdated
if (*plen < 73 || (value == 0 && *plen < 65)) { | ||
return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (*plen < 65 || (value != 0 && *plen < 73)) {
or more readable (I think):
if (*plen < (value == 0) ? 65 : 73) {
(This implies that we should have also a test for value == 0
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW there is an explicit test for the size being correct when value == 0
. You can search for the comment DIfferent proof sizes are unfortunate
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah what I meant here is: If we had a (unit) test that calls this function with value == 0
and 65 <= size < 73
, it would have caught this bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lol, there's an even worse bug here -- we never actually set plen
. It looks like I did basically no tests on create_value
.
9d28292
to
533f423
Compare
@real-or-random I have pushed fixup commits which address all your nits except the code-sharing ones. If you'd like I can refactor the code, but I don't think the savings would be super big and it'd undermine the "make these functions as self-contained as possible" goal of writing this. |
You didn't answer these. I guess then an another goal is to have a simple readable "reference-like" implementation? And for the tests: I still think that consistency tests could be useful. (Not sure about the bitflips anymore, I think you've convinced me that we know what we're doing with the header bits and "used input" bitmap.) |
src/modules/rangeproof/main_impl.h
Outdated
secp256k1_sha256_finalize(&sha2, tmpch); | ||
|
||
/* 3. Check computed e against original e */ | ||
return !memcmp(tmpch, &proof[offset], 32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are more memcmp
s, and those could actually matter because they're not in the real code, not in the tests.
src/modules/rangeproof/main_impl.h
Outdated
secp256k1_sha256_write(&sha2, tmpch, 33); | ||
secp256k1_rangeproof_serialize_point(tmpch, &genp); | ||
secp256k1_sha256_write(&sha2, tmpch, 33); | ||
secp256k1_sha256_write(&sha2, proof, offset); /* lol we commit to one extra byte here */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a duplicate of the removed comment.
|
||
/* Now we just have a Schnorr signature in (e, s) form. The verification | ||
* equation is e == H(sG - eX || proof params), where X is the difference | ||
* between the output and input. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think it would make sense (e.g., it would have prevented the issue with the duplicate comment, and it will make you change memcmp
only once. ;)) But your goal of making them self-consistent is also good. Whatever you prefer.
src/modules/rangeproof/tests_impl.h
Outdated
test_single_value_proof(0); | ||
test_single_value_proof(12345678); | ||
test_single_value_proof(UINT64_MAX); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe add a small loop?
/** Verify a rangeproof with a single-value range. Useful as a "proof of value" | ||
* of a Pedersen commitment. Such proofs can be created with `secp256k1_rangeproof_sign` | ||
* by passing an `exp` parameter of -1 and the target value as both `value` and `min_value`. | ||
* Returns 1: Proof was valid and proved the given value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You gave a ":+1:" but I think this hasn't been fixed.
src/modules/rangeproof/main_impl.h
Outdated
if (*plen < 73 || (value == 0 && *plen < 65)) { | ||
return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah what I meant here is: If we had a (unit) test that calls this function with value == 0
and 65 <= size < 73
, it would have caught this bug.
I'll add some more consistency tests, and a test for the size thing. |
One last thing -- @real-or-random should I rename these functions from |
Ah yeah, I haven't really thought about the names. Now that you mention it, I think edit: Oh and I wonder why the tests now fail. |
@real-or-random I think one of the API tests tripped the "providing I'll start running |
...ok, I might not be able to fix this today, but there's another bug in The fix is simple enough -- re-order the operations in edit flight delayed, got it. I did indeed have to rewrite create_value, sorry. running through tests again and then will push |
Ok @real-or-random one more question ... should you be able to "rewind" single-value proofs? I'm going to vote no, it will make my life much harder (I will need to recreate the insane hmac-sha2-based RNG and run a ton of crap through it) and I don't see any real value to it. |
533f423
to
3617dfe
Compare
Ok, this one should work 😅 |
Fixups look good except for one point below. Can you squash? I liked the previous method of computing |
Yep, if you don't think we need them. (I don't know, I don't use these methods :D). |
3617dfe
to
94e7f05
Compare
Rebase/squash only. Will add one more fixup which changes how |
94e7f05
to
a20c1e9
Compare
Ok, rebased and added one more commit with @real-or-random's suggestion to compute |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK a20c1e9
Ok to merge? |