-
Notifications
You must be signed in to change notification settings - Fork 39
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 OpSignToContract with tag 0x09
#14
base: master
Are you sure you want to change the base?
Conversation
Manually constructed a |
Niiice! I should test the re-implementability of this by attempting to implement it in Rust or something. We might want to call it OpSignToContractSha256. Also, I wonder if the term "contract" makes sense here - most readers probably won't get the reference going forward, even if it's often been called that in the past in the more niche Bitcoin use-cases. Maybe call it something like EccSigCommitment? |
Or actually, Secp256k1SigCommitment? |
Here is a .ots that uses this going to block 466872 https://download.wpsoftware.net/bitcoin/wizardry/andytoshi.s2c.ots Sadly I can't get a file for which this will verify as the original data was the text ACK name change to Secp256k1SigCommitment, will update the PR Is the tag 0x09 ok? |
Actually how about just |
Update with renames, also changed the copyright year from 2016 to 2017 |
Cool, I like What do you mean by "ots verify adds a newline"? I mean, ots verify shouldn't be modifying file contents at all. You know about the Tag 0x09 is fine for now; I need to do up a tag allocation list... (or maybe make it a note making it clear that we don't have one?) |
Oh! It was actually vim silently adding a You can, in fact, verify the timestamp :) |
Nice! You should add those two files to the repo; just create an examples/ directory like is in the OpenTimestamps client; we can rearrange or whatever else later. |
Ok, added |
Hi @apoelstra this is amazing :) |
You can just set noeol in binary mode in vim to prevent the newline @apoelstra |
@RCasatta The point of Secp256k1Commitment isn't primarily to make timestmamps smaller, but rather cheaper: since they can be done at zero marginal cost people can timestamp stuff while they're performing Bitcoin transactions that they would have otherwise made anyway. Now, it's true that we could get both by spending bare CHECKSIG outputs like the OpenTimestamps server does, but that's not the main intent here; once this is implemented I'd still like the calendars to continue making occasional |
@petertodd I perfectly understand the point that you can leverage a transaction someone would made otherwise, but as you said this must come at zero marginal cost to them otherwise it's not viable. Since the ScriptSig of |
@RCasatta You've got it backwards: Andrews transaction is a normal sized scriptSig; the OpenTimestamps server uses shorter-than-average bare CHECKSIG outputs that can be spent with shorter-than-average scriptSigs. |
@petertodd Perfect, thanks |
@RCasatta Ah, I just noticed that @apoelstra's transaction was using the 65-byte uncompresed pubkey Most transactions these days use compressed pubkeys, which are 32 bytes shorter. |
Yes, sorry, I generated very many keys a very long time ago and have not gotten through them all.. hence the uncompressed keys. This is totally separate from sign-to-contract. |
I would like this to regain attention and I am doing a recap from what I can understand. Relatively weak points on this are:
Mitigations:
Advantages:
In conclusions, I think we should merge this :) Concept ACK |
Sign-to-contract does not introduce a discrete log assumption. It depends only on the security of the underlying hash (in this case, SHA256). |
@apoelstra Do you have a ELI5 explanation as to why that's true? Be good to add that in a comment or something. |
tweak = int.from_bytes(hasher.digest(), 'big') | ||
tweak_pt = SECP256K1_GEN.scalar_mul(tweak) | ||
final_pt = pt.add(tweak_pt) | ||
return final_pt.x.to_bytes(32, 'big') |
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.
With DER encoding the x-coord of the ephemeral key may be encoded in less than 32 bytes, it happens 1 time out of 512.
Something like (final_pt.x.bit_length() + 7) // 8
instead of 32
should fix that.
@LeoComandini in Bitcoin we always serialize points as 33 bytes rather than using DER. Everything is much nicer with fixed-length objects. @petertodd Sure, there is a (almost) bijection between 32-byte numbers For fixed |
opentimestamps/core/secp256k1.py
Outdated
pt = Point.decode(msg[0:33]) | ||
|
||
hasher = hashlib.sha256() | ||
hasher.update(pt.encode()) |
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.
@apoelstra Question: could msg[0:33]
ever be different than the output from pt.encode()
? And what prevents two encoded points from mapping to the same decoded then encoded point?
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.
- Sure. But anything that doesn't
throw
will be identical to the output ofpt.encode()
for some point. - The encoding includes the full x coordinate and the sign of the y coordinate of a point. If two points have the same x-coord and y-sign then they are the same point.
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.
Right, so they should be identical, which means I think we can actually use msg[0:33]
instead of pt.encode()
See, my concern is if they ever aren't, we have a source of mutability which may allow someone to create a false timestamp.
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, I see. Yep, they will always be identical so if you're more comfortable using msg[0:33]
I can do that instead.
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.
Sounds like a good idea.
Also, if we think this should be mathematically impossible, maybe adding an assertion that tests this isn't a bad idea. Worst case is a DoS attack in a situation where something is quite wrong in our understanding.
FYI, looks like @LeoComandini has actually done a thesis that is in part on the subject of EC commitments! https://github.com/LeoComandini/Thesis/blob/master/main.pdf |
Oh, nice! Chapter 5 of Leo's thesis has a proof of exactly what you want, in the random oracle model. @petertodd rebased and added a commit which assets that point-reencoding is unique, and uses the message directly instead of hashing the re-encoding. |
|
||
@UnaryOp._register_op | ||
class OpSecp256k1Commitment(UnaryOp): | ||
"""Map (P || commit) -> [P + sha256(P||commit)G]_x for a given secp256k1 point P |
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.
This description isn't actually correct: the opcode isn't taking a "commit", but rather an arbitrary message, limited only by the MAX_MSG_LENGTH
limitation.
So I'd suggest we change that line to:
Map (P || m) -> [P + sha256(P || m)G]_x for a given secp256k1 point P
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.
Sure, I'll reword this. I had meant "commit" in the sense of "thing that is being committed to".
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.
Thanks!
While other commitment operations work on arbitrary input - modulo the An potential alternative would be to change the semantics in the case of an error to instead, say, output If we did this:
The only drawback I can think of is consensus across buggy implementations: if an implementation accidentally raises an error when it shouldn't, that just causes the timestamp to be unusable immediately. With my proposed semantics you might further build on that timestamp by, say, adding additional opcodes that would then be impossible to fix later. However, as I don't think that risk is unique to my proposal, as you could also have a buggy implementation that thinks a point is valid when it actually isn't. IMO the only lesson there is "don't screw up". :) While I can't think of any other examples off the top of my head of an opcode that would have a similar problem, I can imagine us using this general approach in the future as well. |
I should update this to be consistent with BlockstreamResearch/secp256k1-zkp#111 when that gets merged. Or perhaps I should hold off until the Taproot equivalent. Interesting thought to interpret invalid points as indicating a different kind of commitment. Now I would suggest using a BIP340 tagged hash with |
Holding off until Taproot is fine by me. As you can see, I'm in no rush. :) Good point re: BIP340. |
No description provided.