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

BIP442: OP_PAIRCOMMIT #1699

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

BIP442: OP_PAIRCOMMIT #1699

wants to merge 3 commits into from

Conversation

moonsettler
Copy link
Contributor

@moonsettler moonsettler commented Nov 11, 2024

OP_PAIRCOMMIT is the newest member of the LNhance family of opcodes. It provides limited vector commitment functionality in tapscript.

When evaluated, the OP_PAIRCOMMIT instruction:

  • pops the top two values off the stack,
  • takes the "PairCommit" tagged SHA256 hash of the stack elements,
  • pushes the resulting commitment on the top of the stack.

Discussion: https://delvingbitcoin.org/t/op-paircommit-as-a-candidate-for-addition-to-lnhance/1216/12

Copy link
Contributor

@murchandamus murchandamus left a comment

Choose a reason for hiding this comment

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

This document has a few formatting issues, please make sure that the preamble matches the BIP 2 requirements and take a look at the rich diff to see whether it looks the way you intend.

Please note that the BIPs repository also accepts markdown files.

@moonsettler
Copy link
Contributor Author

moonsettler commented Nov 13, 2024

Switched back to markdown. Header now in BIP-2 format.

@moonsettler moonsettler force-pushed the paircommit branch 2 times, most recently from 8f11758 to f3f7f91 Compare November 13, 2024 21:35
@moonsettler
Copy link
Contributor Author

The original create date of OP_PAIRCOMMIT is 2024-03-15 this is the latest revision based on feedback from Anthony Towns.
https://gist.github.com/moonsettler/d7f1fb88e3e54ee7ecb6d69ff126433b/revisions
What date should go to the header?

@jonatack
Copy link
Member

Added a discussion link to the PR description.

The original create date of OP_PAIRCOMMIT is 2024-03-15 this is the latest revision based on feedback from Anthony Towns.
gist.github.com/moonsettler/d7f1fb88e3e54ee7ecb6d69ff126433b/revisions
What date should go to the header?

Perhaps add a changelog with the revision based on Anthony Towns' feedback followed by the initial version. Or use the date of the current draft revision as your starting point.

@murchandamus
Copy link
Contributor

According to BIP 2:

The Created header records the date that the BIP was assigned a number, […]

@moonsettler moonsettler marked this pull request as ready for review November 14, 2024 15:56
@murchandamus
Copy link
Contributor

Has this proposal been sent to the mailing list?

@moonsettler
Copy link
Contributor Author

moonsettler commented Nov 14, 2024

Has this proposal been sent to the mailing list?

Not yet. Wanted to get it into an acceptable shape before I post it there.

Proposed to the mailing list, waiting for feedback.

README.mediawiki Outdated Show resolved Hide resolved
README.mediawiki Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
@moonsettler moonsettler force-pushed the paircommit branch 3 times, most recently from 59249d9 to dfb0670 Compare November 15, 2024 18:24
Copy link
Contributor

@murchandamus murchandamus left a comment

Choose a reason for hiding this comment

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

I would like to see this proposal to get more review from other covenant researchers before it moves forward.

bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
@moonsettler
Copy link
Contributor Author

moonsettler commented Nov 26, 2024

It looks like we gonna have to amend the PAIRCOMMIT BIP with some new use cases.
Turns out within certain practical limitations any computational function can be proven out in the form of a merkle tree.
The root hash of the merkle tree represents the function the leaves represent the inputs and output.
Any 32 bit arithmetic function can certainly be proven out with this method.
CAT itself with a limited set of inputs or limited input sizes can be proven out.
At this point it's an open question if this enables new behaviors not enabled by taproot MAST itself?

Special thanks to: @JeremyRubin @Ademan @bigspider

edit:
Alternatively could consider imposing specific script limits that make PAIRCOMMIT explicitly less capable than MAST itself.

moonsettler

This comment was marked as off-topic.

@Ademan
Copy link

Ademan commented Nov 27, 2024

I think I've changed my mind a bit. We were talking about computing a merkle tree for f(u32,u32) as if it was trivial but after a quick experiment it seems like that would take hundreds of years to compute (am I being dumb here?) Instead, you can compute mul(u32,u32) -> u32 using 3 mul(u16,u16)s which is feasible to compute. The witness size is worse, ~32 * 32 * 3 = 3072 instead of 32 * 64 * 1 = 2048, but computing the tree for mul(u16,u16) is feasible using a naive algorithm on commodity hardware.

The implication of this is that where a function can be decomposed into operations on smaller inputs, PAIRCOMMIT is massively more feasible to use than encoding things into a tap tree.

@bigspider
Copy link
Contributor

I think I've changed my mind a bit. We were talking about computing a merkle tree for f(u32,u32) as if it was trivial but after a quick experiment it seems like that would take hundreds of years to compute (am I being dumb here?) Instead, you can compute mul(u32,u32) -> u32 using 3 mul(u16,u16)s which is feasible to compute. The witness size is worse, ~32 * 32 * 3 = 3072 instead of 32 * 64 * 1 = 2048, but computing the tree for mul(u16,u16) is feasible using a naive algorithm on commodity hardware.

Arithmetic and bitwise operations where inputs & outputs are small enough, can already be done in Script in cheaper ways. Merkle trees as lookup tables are only interesting for functions that are either extremely complex, or where preimages/images are larger than what Script can work with.
Note that you can already do small indexed lookup tables more efficiently by just hard-coding them in Script (that is: push the table on the stack and use OP_PICK to read its entries), and these techniques are widely used (e.g. in BitVM).

The implication of this is that where a function can be decomposed into operations on smaller inputs, PAIRCOMMIT is massively more feasible to use than encoding things into a tap tree.

I think the only substantial difference is that in a Script where you need several lookups, you can do it with Merkle trees, while you can only do a single lookup with a precomputed taptree.

@moonsettler
Copy link
Contributor Author

Proving general computation

Merkle trees can be used to prove out computation where the root of the tree
represents the function and the leaves represent the inputs and output.
There are practical limits to the entropy space for the inputs as it needs
to be iterated over and hashed up.

Currently MAST trees can cover 128 bits of entropy space, which is well over
the practical limits to iterate over and merklize. Therefore we assume this
capability does not materially extend what computations are possible to prove
out in bitcoin script. While OP_PAIRCOMMIT is not limited to a height of 128,
that should not be practically feasible to utilize.

There is a way to reduce the size of the witness for proving out computation,
by eliminating the merkle path inclusion proofs, using OP_CHECKSIGFROMSTACK
together with OP_PAIRCOMMIT. This method involves deleted key assumptions,
most likely using MPC to create an enormous amount of signatures for the stack
elements representing the inputs and the output of the function.

Is this correct? Any suggestions? @Ademan @bigspider

@moonsettler
Copy link
Contributor Author

moonsettler commented Nov 27, 2024

The implication of this is that where a function can be decomposed into operations on smaller inputs, PAIRCOMMIT is massively more feasible to use than encoding things into a tap tree.

This is the main open question I believe. does it or does it not practically expand what we can already do?
For example using PC to emulate smolCAT and using traditional methods with lookup tables could make 32 bit or even 64 bit arithmetics more feasible?

edit:
Within the 32 bit realm we can already use OP_ADD, I see little practical diff between <0x1234> <0x5678> CAT and <0x12340000> <0x5678> ADD.
And it sounds like 64 bit smolCAT would be way too expensive to generate (and also to interact with trustlessly).

(actually the above examples are wrong, because internally bitcoin script uses little endian, but should convey the point)

@Ademan
Copy link

Ademan commented Nov 27, 2024

...

Arithmetic and bitwise operations where inputs & outputs are small enough, can already be done in Script in cheaper ways. Merkle trees as lookup tables are only interesting for functions that are either extremely complex, or where preimages/images are larger than what Script can work with. Note that you can already do small indexed lookup tables more efficiently by just hard-coding them in Script (that is: push the table on the stack and use OP_PICK to read its entries), and these techniques are widely used (e.g. in BitVM).

Even u16,u16 is quite a bit larger than I think is practical as a lookup table, but the efficiency for repeated operations is constant, obviously. The lookup table is less efficient for small numbers of operations (a u8,u8 table is 16k vs 1 u8,u8 proof is 0.4k) but the merkle tree loses quickly when those operations are repeated.

The implication of this is that where a function can be decomposed into operations on smaller inputs, PAIRCOMMIT is massively more feasible to use than encoding things into a tap tree.

I think the only substantial difference is that in a Script where you need several lookups, you can do it with Merkle trees, while you can only do a single lookup with a precomputed taptree.

Right, and the key point is these merkle trees and lookup tables rapidly become infeasible to compute as the input size grows, so multiple smaller lookups is significantly more useful.

EDIT: But your point is well taken that for smaller operations they can already be better accomplished by lookup tables.

@Ademan
Copy link

Ademan commented Nov 27, 2024

...
edit: Within the 32 bit realm we can already use OP_ADD, I see little practical diff between <0x1234> <0x5678> CAT and <0x12340000> <0x5678> ADD. And it sounds like 64 bit smolCAT would be way too expensive to generate (and also to interact with trustlessly).

(actually the above examples are wrong, because internally bitcoin script uses little endian, but should convey the point)

Yeah for arbitrary 8 byte strings smolCAT seems infeasible to compute the table or merkle tree for. After a bit of conversation on IRC it could probably be feasible for arbitrary f(b[4],b[4]) -> b[8] with a custom ASIC¹ or maybe a cluster of FPGAs in a span of ~a few years but that would not be very useful for the average person.

Bit shifts over 32 bit integers seems pretty feasible though, that's f(u32,u6)->u32 (maybe save some space by special casing shift = 0). it seems like my incredibly naive, unoptimized, single-core experiment could calculate that merkle tree in ~96 hours. Of course the proof is ~1.2k and users would likely need multiple, but the lookup table for that wouldn't fit in a block anyway so maybe something new is possible?

You can also separate positive and negative shifts, and maybe break it down into multiple rounds of shifts 1-3 or something (or 1k for a proof for a constant shift)

[1]: afaik existing ASICs operate on block headers so couldn't help

@moonsettler
Copy link
Contributor Author

I think this BIP is already way more verbose than it was supposed to be.

It would be useful if we could reference it by a number.
Can we get a BIP number assigned? Not asking for a merge yet.

@moonsettler
Copy link
Contributor Author

moonsettler commented Dec 5, 2024

Should we add this table to this BIP? And it's not just vBytes but also the number of sigops to consider, which is a cost all nodes on the p2p network have to bear.

edit: I think it looks like this:

Method ChannelS UpdateSc UpdateWi 1-Update 2-Update
APO-annex 2.25 vB 28.25 vB 25 vB 305 vB 461.5 vB
APO-return 2.25 vB 28.25 vB 16.5 vB 338.5 vB 528.5 vB
CTV+CSFS+IKEY 2.75 vB 12.25 vB 24.5 vB 331 vB 513 vB
CTV+CSFS 11 vB 20.5 vB 24.5 vB 347.5 vB 537.75 vB
LNhance 3 vB 12.5 vB 32.75 vB 297.75 vB 446.25 vB
CTV+CSFS+VAULT 18.75 vB 30 vB 35 vB 333.75 vB 502.25 vB
rekey 7.25 vB 16.75 vB 73.75 vB 347.25 vB 541 vB
Method ForceC Update Settle OP_RETURN
APO-annex 1 SigOp 1 SigOp 1 SigOp
APO-return 1 SigOp 1 SigOp 1 SigOp X
CTV+CSFS+IKEY 1 SigOp 1 SigOp CTV X
CTV+CSFS 1 SigOp 1 SigOp CTV X
LNhance 1 SigOp 1 SigOp CTV
CTV+CSFS+VAULT 2* SigOp 2* SigOp CTV
rekey 3 SigOp 3 SigOp CTV

* VAULT is not exactly a SigOp, but close enough. Has a budget cost of 1.2 SigOps.

@JeremyRubin
Copy link
Contributor

tbh i have no idea how to read that table, so might be good to have clearer labeling somehow / break down where the accounting came from?

@moonsettler
Copy link
Contributor Author

@murchandamus
Copy link
Contributor

It would be useful if we could reference it by a number. Can we get a BIP number assigned? Not asking for a merge yet.

Looking at the Motivation section, it seems to me that the main application for this proposal would be a construction that depends on three other undeployed proposals some of which are themselves draft stage or pre-draft. This proposal feels a bit hypothetical at this point. I’ll get back to you next week.

@moonsettler
Copy link
Contributor Author

moonsettler commented Dec 6, 2024

This proposal feels a bit hypothetical at this point.

While PAIRCOMMIT would only be truly useful with certain other future upgrades, it is proposed to activate in a bundle with said updates. We are in the process of trying to reach consensus on said package.

It's unlikely I would withdraw OP_PAIRCOMMIT, unless OP_CAT got activated first.

@jonatack
Copy link
Member

jonatack commented Dec 7, 2024

What date should go to the header?

The Created header records the date that the BIP was assigned a number (see BIP-2).

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

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

A few edit suggestions on first read.

I think this can potentially be assigned a number once the BIP-2 criteria are met. A non-exhaustive list:

"When the BIP draft is complete, a BIP editor will assign the BIP a number, label it as Standards Track, Informational, or Process, and merge the pull request to the BIPs git repository.
"The BIP editors will not unreasonably reject a BIP.
"Reasons for rejecting BIPs include duplication of effort, disregard for formatting rules, being too unfocused or too broad, being technically unsound, not providing proper motivation or addressing backwards compatibility, or not in keeping with the Bitcoin philosophy.
"For a BIP to be accepted it must meet certain minimum criteria.
"It must be a clear and complete description of the proposed enhancement.
"The enhancement must represent a net improvement.
"The proposed implementation, if applicable, must be solid and must not complicate the protocol unduly."

bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
@moonsettler moonsettler requested a review from jonatack December 7, 2024 17:52
Copy link
Contributor

@murchandamus murchandamus left a comment

Choose a reason for hiding this comment

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

Let’s call this BIP 442.

bip-PC.md Outdated

### Cost comparison of LN-Symmetry constructions

| Method | ChannelS | UpdateSc | UpdateWi | 1-Update | 2-Update |
Copy link
Contributor

Choose a reason for hiding this comment

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

The terms "ChannelS", "UpdateSc", "UpdateWi", "1-Update", and "2-Update" seem to not be defined in this document. Perhaps they should be described here to provide context, or if this is in reference to another document, that document should be linked in this section for context.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will try to figure something out, it's Channel Script, Update Script, Update Witness, total cost with 1 or 2 updates.
@reardencode?

bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
bip-PC.md Outdated Show resolved Hide resolved
README.mediawiki Outdated Show resolved Hide resolved
@jonatack jonatack changed the title OP_PAIRCOMMIT BIP442: OP_PAIRCOMMIT Dec 17, 2024
Copy link
Contributor

@reardencode reardencode left a comment

Choose a reason for hiding this comment

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

PAIRCOMMIT continues to grow on me.

Getting the primary benefits of OP_CAT without introducing ugly introspection seems like a clear win for bitcoin.

Better introspection should also be added in follow-up forks. Thanks for your work on this!

Comment on lines +20 to +21
* takes the "PairCommit" tagged SHA256 hash of the stack elements,
* pushes the resulting commitment on the top of the stack.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* takes the "PairCommit" tagged SHA256 hash of the stack elements,
* pushes the resulting commitment on the top of the stack.
* takes the "PairCommit" tagged SHA256 hash of the stack elements' lengths and values,
* pushes the resulting 32-byte hash to the top of the stack.

## Motivation

To do [LN-Symmetry] contracts that don't require the nodes to keep old states,
we need to solve the data availability problem presented by unilateral closes.
Copy link
Contributor

Choose a reason for hiding this comment

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

While this was the original motivation for developing PAIRCOMMIT, I don't think it's the current motivation for introducing it standalone, or including it in LNHANCE.

Perhaps something more like:

When building scripts using OP_CHECKSIGFROMSTACK, it is common to require a commitment to multiple items in a specific sequence and without malleability. OP_PAIRCOMMIT enables efficient commitments of this type. Alternative methods for achieving these multiple commitments require an additional signature checking operation for each additional unordered commitment or 2 additional signature checks for each additional ordered commitment.

One example of such a construction is [LN-Symmetry] which requires a single signature to commit both to the update CTV hash and the settlement CTV hash to ensure O(1) data storage requirements for each channel partner. Another is a complex delegation (delegating to various pubkeys after matching locktimes). Either of these contracts can be achieved without OP_PAIRCOMMIT but they would require the use of a costly OP_RETURN or 2 additional signature checks respectively.

Then (as we discussed) move the remaining symmetry discussion to its own section.


Where `|` denotes concatenation and `tagPC` is calculated according to
[BIP-340] tagged hash as `SHA256("PairCommit")|SHA256("PairCommit")` and
`cs(x)` means `CompactSize(x)`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Where is CompactSize defined?

## Rationale

If `OP_CAT` was available, it could be used to combine multiple stack elements
that get verified with `OP_CHECKSIGFROMSTACK` as a valid state update.
Copy link
Contributor

Choose a reason for hiding this comment

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

Add

Using OP_CAT for this purpose requires additional opcodes to prevent malleability (e.g. 0x0102 0x03 CAT is identical to 0x01 0x0203 CAT).

or similar.

Comment on lines +140 to +141
`OP_PAIRCOMMIT` solves this specific problem without introducing a wide range
of potentially controversial new behaviors, such as novel 2-way peg mechanisms.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
`OP_PAIRCOMMIT` solves this specific problem without introducing a wide range
of potentially controversial new behaviors, such as novel 2-way peg mechanisms.
`OP_PAIRCOMMIT` solves this specific problem without introducing granular introspection
via Andrew Poelstra's CAT and Schnorr Tricks.

a 1-byte `OP_TRUE` public key (substituting for the *taproot internal key*) and
can commit to a number of stack elements as a message.

### Behaviors LNhance tries to avoid introducing
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this section belong in the PC BIP?

There are practical limits to the entropy space for the *inputs* as they need
to be iterated over and hashed into a merkle root.

MAST trees can currently cover 128 bits of entropy space, which is well over
Copy link
Contributor

Choose a reason for hiding this comment

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

MAST -> Taproot?

to be iterated over and hashed into a merkle root.

MAST trees can currently cover 128 bits of entropy space, which is well over
the practical limits to iterate over and merklize. Therefore, we assume this
Copy link
Contributor

Choose a reason for hiding this comment

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

assume -> conclude

the practical limits to iterate over and merklize. Therefore, we assume this
capability does not materially extend what computations are possible to prove
in bitcoin script. While `OP_PAIRCOMMIT` is not limited to a height of 128,
that should not be practically feasible to utilize.
Copy link
Contributor

Choose a reason for hiding this comment

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

should not be -> is

in bitcoin script. While `OP_PAIRCOMMIT` is not limited to a height of 128,
that should not be practically feasible to utilize.

There is a way to reduce the size of the witness for proving computation,
Copy link
Contributor

Choose a reason for hiding this comment

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

I find this paragraph confusing. I think a variant of it probably belongs in the CHECKSIGFROMSTACK BIP - as CSFS can be used generally to sign data into a script and that is not limited to use with PC (although PC extends that capability to merkelized data).

@JeremyRubin
Copy link
Contributor

I get that there is a goal here to avoid introspection...

but it seems that it'd be more generically useful if the function were e.g., a TapBranch function, so then it could be used in the future with some other taproot editing opcodes.

e.g., if OP_TAPBRANCHCOMMIT were to lexicographic sort, then cat, then commit, you could do paircommit like functionality by doing:

<a> <b> TUCK OP_TAPBRANCHCOMMIT OP_TAPBRANCHCOMMIT

this works because while the first commit commits to either a||b or b||a, and also has sliceable errors (e.g., (a||b)[:i] and (a||b)[i:]), the second commit re-commits to <b> and it's length which uniquely determines order and prevents sliceable errors.

One concern: OP_TAPBRANCHCOMMIT is witness "malleable", in that items could show up in the witness stack in either order and get the same result. It'd still be possible to make non-malleable witnesses by requiring the stack elements to be in order with a OP_LEXSORT or equivalent functionality.

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

Successfully merging this pull request may close these issues.

8 participants