From 63296bfaab7ea49e942413d3fc6f45bb563bdc9e Mon Sep 17 00:00:00 2001 From: moonsettler Date: Sat, 28 Dec 2024 18:50:22 +0100 Subject: [PATCH 1/4] Add: PAIRCOMMIT --- README.mediawiki | 7 ++ bip-0442.md | 265 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 bip-0442.md diff --git a/README.mediawiki b/README.mediawiki index e89ccf5bff..a50ce78d3f 100644 --- a/README.mediawiki +++ b/README.mediawiki @@ -1295,6 +1295,13 @@ Those proposing changes should consider that ultimately consent may rest with th | Gloria Zhao | Informational | Draft +|- +| [[bip-0442.md|442]] +| Consensus (soft fork) +| OP_PAIRCOMMIT +| moonsettler +| Standard +| Draft |} diff --git a/bip-0442.md b/bip-0442.md new file mode 100644 index 0000000000..706ebba791 --- /dev/null +++ b/bip-0442.md @@ -0,0 +1,265 @@ +
+  BIP: 442
+  Layer: Consensus (soft fork)
+  Title: OP_PAIRCOMMIT
+  Author: moonsettler 
+  Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0442
+  Status: Draft
+  Type: Standards Track
+  Created: 2024-12-09
+  License: BSD-3-Clause
+
+ +## Abstract + +This BIP describes a new tapscript opcode `OP_PAIRCOMMIT`, which +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 with size +commitments, +* pushes the resulting 32-byte hash to the top of stack. + +## Motivation + +Currently, bitcoin lacks a way to hash multiple stack elements together. Which +means building Merkle trees or verifying inclusion in a tree is not supported. + +`OP_PAIRCOMMIT` is a simple and efficient tool to commit to two stack elements, +in a way that makes length redistribution attacks infeasible. + +The number of SHA256 iterations is minimized in the typical use cases we can +optimize for. Since the Tag can be pre-computed as mid-state, it would only +take 1 or 2 hash cycles in validation for the unilateral close scenario. + +## Specification + +Repurpose opcode 205 (currently `OP_SUCCESS`) as follows: + +`OP_PAIRCOMMIT` pops two elements off the stack, then concatenates them along +with their size commitments and takes the tagged SHA256 hash of that +concatenated string, then pushes the resulting hash back on the stack. + +Given the stack `[x1, x2]`, where `x2` is at the top of the stack: + +`OP_PAIRCOMMIT` will push `SHA256(tagPC|cs(x1)|x1|cs(x2)|x2)` onto the stack. + +Where `|` denotes concatenation and `tagPC` is calculated according to +[BIP-340] tagged hash as `SHA256("PairCommit")|SHA256("PairCommit")` and +`cs(x)` means `CompactSize(x)`. + +### Implementation + +```c++ +case OP_PAIRCOMMIT: { + // OP_PAIRCOMMIT is only available in Tapscript + // ... + // x1 x2 -- hash + if (stack.size() < 2) { + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + } + const valtype& vch1 = stacktop(-2); + const valtype& vch2 = stacktop(-1); + + uint256 hash = PairCommitHash(vch1, vch2); + + stack.pop_back(); + stack.pop_back(); + stack.emplace_back(hash.begin(), hash.end()); + break; +} +``` +```c++ +const HashWriter HASHER_PAIRCOMMIT{TaggedHash("PairCommit")}; + +uint256 PairCommitHash(const std::vector& x1, const std::vector& x2) +{ + return (HashWriter{HASHER_PAIRCOMMIT} << x1 << x2).GetSHA256(); +} +``` +### Use in script + +`OP_PAIRCOMMIT` can be used to commit to a vector of stack elements in a way +that is not vulnerable to various forms of witness malleability. It is, however, +highly optimized for just 2 stack elements. + +```text +# pc-hash = PC(a, PC(b, c)) + + | PC PC OP_EQUALVERIFY +``` + +### Use in LN-Symmetry + +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. +Channel peers must be able to reconstruct the script that spends an +intermediate state. + +Using in sequence `OP_CHECKTEMPLATEVERIFY`, `OP_PAIRCOMMIT`, `OP_INTERNALKEY` +and `OP_CHECKSIGFROMSTACK` we can construct a rebindable channel that is also +[optimal]. + +The following assembly-like pseudo-code shows a possible LN-Symmetry channel +construction that provides data availability to spend to the latest state from +an earlier state pushed on-chain with a forced close by channel partner. + + +```text +# S = 500000000 +# IK -> A+B + | CTV PC IK CSFS CLTV DROP +``` +before funding, sign the first state: +```text +# state-n-hash { nLockTime(S+n), out(contract, amount(A)+amount(B)) } +# settlement-n-hash { nSequence(2w), out(A, amount(A)), out(B, amount(B)) } +# state-n-recovery-data { settlement-n-hash or state-n-balance } + +# contract for state n < m +IF + | CTV PC IK CSFS CLTV DROP +ELSE + CTV +ENDIF +``` + +### Use with future updates + +Detailed introspection opcodes would also need vector commitments when used +with `OP_CHECKSIGFROMSTACK`. + +`OP_CHECKCONTRACTVERIFY` would also need a way to carry complex data. + +## Reference Implementation + +A reference implementation is provided here: + +https://github.com/lnhance/bitcoin/pull/6/files + +## 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. + +Using `OP_CAT` for this purpose requires additional opcodes to prevent witness +malleability (e.g. `0x0102 0x03 OP_CAT` is identical to `0x01 0x0203 OP_CAT`). + +`OP_PAIRCOMMIT` solves this specific problem without introducing a wide range +of potentially controversial new behaviors like fully detailed introspection, +which includes the ability to inspect parent transactions and novel 2-way peg +mechanisms. ([CAT-tricks-I] and [CAT-tricks-II] by Andrew Poelstra) + +Alternatively `OP_RETURN` could be used to ensure the availability of the state +recovery data, as `OP_CHECKTEMPLATEVERIFY` naturally commits to all outputs. +However, its cost in weight units would be over 4 times higher than that of +using `OP_PAIRCOMMIT`. + +One way to think about the 3 opcodes (`OP_CHECKSIGFROMSTACK`, `OP_INTERNALKEY`, +`OP_PAIRCOMMIT`) is we decompose a `OP_CHECKSIGFROMSTACK` variant that can use +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 + +The following behaviors are out of scope for LNhance and should not be enabled +as a side effect without explicit consensus: + +* Fine-grained introspection +* State-carrying covenants +* Bigint operations +* New arithmetic capabilities using lookup tables + +### Alternative approaches + +The following list of alternative approaches were discussed and rejected for +various reasons, either for expanding the scope or for unnecessary complexity: + +* OP_CAT +* SHA256 streaming opcodes +* Merkle operation opcodes +* 'Kitty' CAT: result or inputs arbitrarily limited in size +* OP_CHECKTEMPLATEVERIFY committing to the taproot annex in tapscript +* OP_CHECKSIGFROMSTACK on n elements as message +* OP_VECTORCOMMIT: generalized form for n > 2 elements +* ReKey: key delegation and multiple use of OP_CHECKSIGFROMSTACK + +### Cost comparison of LN-Symmetry constructions + +| Method | ChannelSc | UpdateSc | UpdateW | ForceC | Contest | Settle | +| :------------ | --------: | -------: | ------: | ------: | ------: | :----: | +| APO-Annex | 8 WU | 113 WU | 100 WU | 1221 WU | 627 WU | SigOp | +| APO-Return | 8 WU | 113 WU | 66 WU | 1359 WU | 765 WU | SigOp | +| CTV+CSFS+IKEY | 10 WU | 48 WU | 98 WU | 1328 WU | 732 WU | CTV | +| CTV+CSFS | 43 WU | 81 WU | 98 WU | 1394 WU | 765 WU | CTV | +| LNhance | 11 WU | 49 WU | 131 WU | 1191 WU | 594 WU | CTV | + +*ChannelSc: channel script, UpdateSc: update script, UpdateW: witness is the +same size for both Force Close and Contest in LN-Symmetry, ForceC: total cost of unilateral close transactions* + +### Proving general computation + +Merkle trees can be used to prove 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 they need +to be iterated over and hashed into a Merkle root. + +Taproot MAST trees can currently cover 128 bits of entropy space, which is over +the practical limits to iterate over and merklize. Therefore, we conclude 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. + +There is a way to reduce the size of the witness for proving 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*. + +## Backward Compatibility + +By constraining the behavior of OP_SUCCESS opcodes, deployment of the BIP +can be done in a backwards-compatible, soft-fork manner. If anyone were to +rely on the OP_SUCCESS behavior of `OP_SUCCESS205`, `OP_PAIRCOMMIT` would +invalidate their spend. + +## Deployment + +TBD + +## Credits + +Jeremy Rubin, Brandon Black, Salvatore Ingala, Anthony Towns, Ademan555 + +## Copyright + +This document is licensed under the 3-clause BSD license. + +## References + +1. LNhance bitcoin repository: [lnhance] +2. LN-Symmetry: [eltoo] +3. OP_CAT: [BIP-347], [BIN-2024-0001] +4. OP_CHECKTEMPLATEVERIFY: [BIP-119] +5. OP_CHECKSIGFROMSTACK: [BIP-348], [BIN-2024-0003] +6. OP_INTERNALKEY: [BIP-349], [BIN-2024-0004] +7. Tagged hash: [BIP-340] + +[lnhance]: https://github.com/lnhance/bitcoin +[eltoo]: https://github.com/instagibbs/bolts/blob/eltoo_draft/XX-eltoo-transactions.md +[CAT-tricks-I]: https://medium.com/blockstream/cat-and-schnorr-tricks-i-faf1b59bd298 +[CAT-tricks-II]: https://medium.com/blockstream/cat-and-schnorr-tricks-ii-2f6ede3d7bb5 + +[//]: # (BIPs referenced) +[BIP-119]: https://github.com/bitcoin/bips/tree/master/bip-0119.mediawiki +[BIP-340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki +[BIP-347]: https://github.com/bitcoin/bips/blob/master/bip-0347.mediawiki +[BIP-348]: https://github.com/bitcoin/bips/blob/master/bip-0348.md +[BIP-349]: https://github.com/bitcoin/bips/blob/master/bip-0349.md +[BIN-2024-0001]: https://github.com/bitcoin-inquisition/binana/blob/master/2024/BIN-2024-0001.md +[BIN-2024-0003]: https://github.com/bitcoin-inquisition/binana/blob/master/2024/BIN-2024-0003.md +[BIN-2024-0004]: https://github.com/bitcoin-inquisition/binana/blob/master/2024/BIN-2024-0004.md + +[//]: # (Internal links) +[optimal]: #cost-comparison-of-ln-symmetry-constructions From 57df145648a8ce255b31df9e6666d95a7a435516 Mon Sep 17 00:00:00 2001 From: moonsettler Date: Sat, 8 Feb 2025 19:34:30 +0100 Subject: [PATCH 2/4] New revision with Brandon Black --- bip-0442.md | 345 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 200 insertions(+), 145 deletions(-) diff --git a/bip-0442.md b/bip-0442.md index 706ebba791..11fdb30005 100644 --- a/bip-0442.md +++ b/bip-0442.md @@ -3,6 +3,7 @@ Layer: Consensus (soft fork) Title: OP_PAIRCOMMIT Author: moonsettler + Brandon Black Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0442 Status: Draft Type: Standards Track @@ -15,70 +16,70 @@ This BIP describes a new tapscript opcode `OP_PAIRCOMMIT`, which 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 with size -commitments, -* pushes the resulting 32-byte hash to the top of stack. - -## Motivation - -Currently, bitcoin lacks a way to hash multiple stack elements together. Which -means building Merkle trees or verifying inclusion in a tree is not supported. +## Summary -`OP_PAIRCOMMIT` is a simple and efficient tool to commit to two stack elements, -in a way that makes length redistribution attacks infeasible. +When verifying taproot script spends having leaf version 0xc0 (as defined in +[BIP-342]), we propose `OP_PAIRCOMMIT` to replace `OP_SUCCESS205` (0xcd). -The number of SHA256 iterations is minimized in the typical use cases we can -optimize for. Since the Tag can be pre-computed as mid-state, it would only -take 1 or 2 hash cycles in validation for the unilateral close scenario. +When evaluated, `OP_PAIRCOMMIT`: +* Pops the top two values off the stack, +* takes the "PairCommit" tagged SHA256 hash of the stack elements with size commitments, +* pushes the resulting 32-byte hash to the top of stack. ## Specification -Repurpose opcode 205 (currently `OP_SUCCESS`) as follows: - -`OP_PAIRCOMMIT` pops two elements off the stack, then concatenates them along -with their size commitments and takes the tagged SHA256 hash of that -concatenated string, then pushes the resulting hash back on the stack. - -Given the stack `[x1, x2]`, where `x2` is at the top of the stack: - -`OP_PAIRCOMMIT` will push `SHA256(tagPC|cs(x1)|x1|cs(x2)|x2)` onto the stack. - -Where `|` denotes concatenation and `tagPC` is calculated according to -[BIP-340] tagged hash as `SHA256("PairCommit")|SHA256("PairCommit")` and -`cs(x)` means `CompactSize(x)`. +The notation below follows that of [BIP-340]. This includes the +$$hash_{tag}(x)$$ notation to refer to +$$SHA256(SHA256(tag) \\| SHA256(tag) \\| x)$$. -### Implementation +* If fewer than 2 elements are on the stack, the script MUST fail and + terminate immediately. +* The top element ($$x2$$) and second to top element ($$x1$$) are read from the + stack. +* Let $$pc$$ be $$hash_{PairCommit}( + compact\\_size(size\\:of\\:x1) \\| x1 \\| + compact\\_size(size\\:of\\:x2) \\| x2)$$[^1] +* The top two elements are popped from the stack. +* $$pc$$ is pushed to the stack. -```c++ -case OP_PAIRCOMMIT: { - // OP_PAIRCOMMIT is only available in Tapscript - // ... - // x1 x2 -- hash - if (stack.size() < 2) { - return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); - } - const valtype& vch1 = stacktop(-2); - const valtype& vch2 = stacktop(-1); +[^1]: The number of SHA256 blocks is minimized in typical use cases. Since the + Tag can be pre-computed as a SHA256 mid-state, it takes only 2 hash cycles + for a commitment to 2 32-byte items or 1 for 2 smaller items. - uint256 hash = PairCommitHash(vch1, vch2); +## Motivation - stack.pop_back(); - stack.pop_back(); - stack.emplace_back(hash.begin(), hash.end()); - break; -} -``` -```c++ -const HashWriter HASHER_PAIRCOMMIT{TaggedHash("PairCommit")}; - -uint256 PairCommitHash(const std::vector& x1, const std::vector& x2) -{ - return (HashWriter{HASHER_PAIRCOMMIT} << x1 << x2).GetSHA256(); -} -``` -### Use in script +Currently, bitcoin lacks a way to commit to multiple data items together. It +is common practice to hash a single item as part of a bitcoin script. Either +in hash/time locked contracts, or in pay-to-pubkey-hash (P2PKH) scripts, but +there is no way to commit to multiple items with a single hash. + +With the ability to commit to pairs of elements, `OP_PAIRCOMMIT` can be +generalized to Merklized commitments to a tree of elements with a single hash. +On its own, this could enable a hash lock contract where the holder of any +pre-image in a Merkle tree can unlock the spend, for example. + +If `OP_CHECKSIGFROMSTACK` is combined with `OP_PAIRCOMMIT`, the ability to sign +commitments to multiple items enables complex delegation. For example, +delegating to $$key1$$ after time $$t1$$ and $$key2$$ after time $$t2$$. +Without `OP_PAIRCOMMIT` these complex delegations can be achieved using key +laddering[^2], at a much greater validation cost. If the same key is simply +used to sign multiple items, they can be combined arbitrarily, but +`OP_PAIRCOMMIT` or key laddering[^2] enforces any desired relationships between +(both sequence and combination) items. + +[^2]: `OP_CHECKSIGFROMSTACK` can commit to a sequence of otherwise + undifferentiated items by requiring a key committed to by the output to + sign an initial laddering key (`key0`) and then having `key0` sign a tuple + of `(item1, key1)` and `key1` signing the tuple `(item2, key2)` etc. There + are some details around ensuring that the items cannot be used as keys + depending on the requirements of the protocol. This is costly both in + bytes and sigops and `OP_PAIRCOMMIT` eliminates the need for such + constructions when committing to any sequence of items. For details, see + the [key laddering post] by Jeremy Rubin and Brandon Black. + +## Examples + +### Committing to more than 2 elements `OP_PAIRCOMMIT` can be used to commit to a vector of stack elements in a way that is not vulnerable to various forms of witness malleability. It is, however, @@ -90,34 +91,47 @@ highly optimized for just 2 stack elements. | PC PC OP_EQUALVERIFY ``` -### Use in LN-Symmetry - -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. -Channel peers must be able to reconstruct the script that spends an -intermediate state. - -Using in sequence `OP_CHECKTEMPLATEVERIFY`, `OP_PAIRCOMMIT`, `OP_INTERNALKEY` -and `OP_CHECKSIGFROMSTACK` we can construct a rebindable channel that is also -[optimal]. - -The following assembly-like pseudo-code shows a possible LN-Symmetry channel -construction that provides data availability to spend to the latest state from -an earlier state pushed on-chain with a forced close by channel partner. - +### Use in Lightning Symmetry + +To create Lightning Symmetry contracts that neither require the nodes to keep +old states nor require additional signing round trips (and corresponding +signature validations), we need to solve a data availability problem presented +by contested closes. Specifically, the second to act channel partner must be +able to reconstruct the script that spends the first update transaction +published. This script includes the hash of the corresponding settlement +transaction which we do not wish to store for all prior states. By having the +channel and update state scripts force the parties to include the settlement +transaction's hash in the witness to in order to publish the corresponding +update transaction, then a later update can use the settlement hash from the +first update transaction's witness to recreate the corresponding spend script. + +The following assembly-like pseudo-code shows a possible Lightning Symmetry +channel construction that ensures sufficient data is available on chain in a +force close to reconstruct the corresponding script and update it with a later +state while only knowing the later state.[^3] The opcodes `OP_CHECKTEMPLATEVERIFY`, +`OP_PAIRCOMMIT`, `OP_INTERNALKEY`, and `OP_CHECKSIGFROMSTACK` are abbreviated +as `CTV`, `PC`, `IK`, and `CSFS` respectively. + +[^3]: Concretely the required data is a full CTV hash of the settlement + transaction when there are open HTLCs, or merely the difference in balance + between the channel partners in other cases. Whether the latter + optimization would be used is an implementation detail not further + discussed here. ```text # S = 500000000 -# IK -> A+B +# internal key = [BIP-327] aggregate key of channel participants +# channel script: | CTV PC IK CSFS CLTV DROP ``` -before funding, sign the first state: + +Before funding, sign the first state. + ```text # state-n-hash { nLockTime(S+n), out(contract, amount(A)+amount(B)) } # settlement-n-hash { nSequence(2w), out(A, amount(A)), out(B, amount(B)) } # state-n-recovery-data { settlement-n-hash or state-n-balance } - -# contract for state n < m +# update script: IF | CTV PC IK CSFS CLTV DROP ELSE @@ -125,103 +139,139 @@ ELSE ENDIF ``` -### Use with future updates +These scripts ensure the necessary data availability as follows: `CTV` commits +to the update transaction's hash, `PC` combines this hash with the next stack +item, and `CSFS` checks a MuSig 2-party signature against the resulting pair +hash. Both parties must sign the same pair hash to produce a channel update, +and they can therefore require each other to include both the update hash and +settlement hash in the witness stack when initiating a force close. This +Lightning Symmetry channel construction is close to [optimal]. -Detailed introspection opcodes would also need vector commitments when used -with `OP_CHECKSIGFROMSTACK`. +### In MATT -`OP_CHECKCONTRACTVERIFY` would also need a way to carry complex data. +The Merkelize All The Things ([MATT]) framework proposes to use `OP_CAT` to +combine multiple items into a single commitment for use with +`OP_CHECKCONTRACTVERIFY`. `OP_PAIRCOMMIT` provides a more ergonomic way to +accomplish this[^4]. -## Reference Implementation +[^4]: `OP_CAT` can be used to commit to multiple items, but it is subject to + byte shifting attacks if used naively. + E.g. `0x0102 || 0x03` equals `0x01 || 0x0203`. Mitigating this correctly + requires either length checking, hashing, or both. -A reference implementation is provided here: +## Alternative approaches -https://github.com/lnhance/bitcoin/pull/6/files +The following list of alternative approaches were discussed and rejected for +various reasons, either for expanding the scope or for unnecessary complexity: -## Rationale +* `OP_CAT`[^4][^7] +* SHA256 streaming opcodes[^7] +* Merkle operation opcodes +* 'Kitty' CAT: `OP_CAT` with result or inputs arbitrarily limited in size +* `OP_CHECKTEMPLATEVERIFY` committing to the taproot annex in tapscript[^5] +* `OP_CHECKSIGFROMSTACK` on n elements as message +* `OP_VECTORCOMMIT`: generalized form for n > 2 elements +* ReKey/Laddering[^2] +* `OP_RETURN`[^6] + +[^5]: As seen in Greg Sanders' [Lightning Symmetry write-up], one additional + item can be committed to by a transaction signature by placing that item + in the Taproot annex. This mechanism is limited to a single additional + item and that item is not made accessible to script making it less useful. +[^6]: `OP_RETURN` can also be used to cause a transaction signature or CTV + hash to commit to additional data items. This is both costly for the user, + as this inexpensive to validate data is pushed into transaction data + instead of witness data and not accessible to script making it less useful + like the annex. +[^7]: `OP_PAIRCOMMIT` is intended to enable more useful bitcoin scripts, in + some cases similar to those enabled by `OP_CAT` but without also enabling + unexpected script behaviors such as those described by Andrew Poelstra in + [CAT-tricks-I] and [CAT-tricks-II] or larger numeric operations. -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. +## Reference Implementation -Using `OP_CAT` for this purpose requires additional opcodes to prevent witness -malleability (e.g. `0x0102 0x03 OP_CAT` is identical to `0x01 0x0203 OP_CAT`). +### Code -`OP_PAIRCOMMIT` solves this specific problem without introducing a wide range -of potentially controversial new behaviors like fully detailed introspection, -which includes the ability to inspect parent transactions and novel 2-way peg -mechanisms. ([CAT-tricks-I] and [CAT-tricks-II] by Andrew Poelstra) +```c++ +case OP_PAIRCOMMIT: { + // OP_PAIRCOMMIT is only available in Tapscript + // ... + // x1 x2 -- hash + if (stack.size() < 2) { + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + } + const valtype& vch1 = stacktop(-2); + const valtype& vch2 = stacktop(-1); -Alternatively `OP_RETURN` could be used to ensure the availability of the state -recovery data, as `OP_CHECKTEMPLATEVERIFY` naturally commits to all outputs. -However, its cost in weight units would be over 4 times higher than that of -using `OP_PAIRCOMMIT`. + uint256 hash = PairCommitHash(vch1, vch2); + + stack.pop_back(); + stack.pop_back(); + stack.emplace_back(hash.begin(), hash.end()); + break; +} +``` +```c++ +const HashWriter HASHER_PAIRCOMMIT{TaggedHash("PairCommit")}; -One way to think about the 3 opcodes (`OP_CHECKSIGFROMSTACK`, `OP_INTERNALKEY`, -`OP_PAIRCOMMIT`) is we decompose a `OP_CHECKSIGFROMSTACK` variant that can use -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. +uint256 PairCommitHash(const std::vector& x1, const std::vector& x2) +{ + return (HashWriter{HASHER_PAIRCOMMIT} << x1 << x2).GetSHA256(); +} +``` -### Behaviors LNhance tries to avoid introducing +### PR -The following behaviors are out of scope for LNhance and should not be enabled -as a side effect without explicit consensus: +https://github.com/lnhance/bitcoin/pull/6/files -* Fine-grained introspection -* State-carrying covenants -* Bigint operations -* New arithmetic capabilities using lookup tables +## Rationale -### Alternative approaches +### Cost comparison of Lightning Symmetry constructions -The following list of alternative approaches were discussed and rejected for -various reasons, either for expanding the scope or for unnecessary complexity: +The following table briefly summarizes the costs associated with force closing +Lightning Symmetry channels enabled by various combinations of proposed script +upgrades. -* OP_CAT -* SHA256 streaming opcodes -* Merkle operation opcodes -* 'Kitty' CAT: result or inputs arbitrarily limited in size -* OP_CHECKTEMPLATEVERIFY committing to the taproot annex in tapscript -* OP_CHECKSIGFROMSTACK on n elements as message -* OP_VECTORCOMMIT: generalized form for n > 2 elements -* ReKey: key delegation and multiple use of OP_CHECKSIGFROMSTACK - -### Cost comparison of LN-Symmetry constructions - -| Method | ChannelSc | UpdateSc | UpdateW | ForceC | Contest | Settle | -| :------------ | --------: | -------: | ------: | ------: | ------: | :----: | -| APO-Annex | 8 WU | 113 WU | 100 WU | 1221 WU | 627 WU | SigOp | -| APO-Return | 8 WU | 113 WU | 66 WU | 1359 WU | 765 WU | SigOp | -| CTV+CSFS+IKEY | 10 WU | 48 WU | 98 WU | 1328 WU | 732 WU | CTV | -| CTV+CSFS | 43 WU | 81 WU | 98 WU | 1394 WU | 765 WU | CTV | -| LNhance | 11 WU | 49 WU | 131 WU | 1191 WU | 594 WU | CTV | +| Method | ChannelSc | UpdateSc | UpdateW | ForceC | Contest | Settle | +| :--------------- | --------: | -------: | ------: | ------: | ------: | :----: | +| APO-Annex | 8 WU | 113 WU | 100 WU | 1221 WU | 627 WU | SigOp | +| APO-Return | 8 WU | 113 WU | 66 WU | 1359 WU | 765 WU | SigOp | +| CTV+CSFS | 43 WU | 81 WU | 98 WU | 1394 WU | 765 WU | CTV | +| CTV+CSFS+IKEY | 10 WU | 48 WU | 98 WU | 1328 WU | 732 WU | CTV | +| CTV+CSFS+IKEY+PC | 11 WU | 49 WU | 131 WU | 1191 WU | 594 WU | CTV | *ChannelSc: channel script, UpdateSc: update script, UpdateW: witness is the -same size for both Force Close and Contest in LN-Symmetry, ForceC: total cost of unilateral close transactions* +same size for both Force Close and Contest in Lightning Symmetry, ForceC: total +cost of unilateral close transactions, Contest: The additional cost to contest +a force closure, Settle: Whether a signature operation or CTV operation is +required to validate the settlement transaction* + +### Proving general computation using trees + +One potential risk of PAIRCOMMIT is that it may enable verification of general +computations on bitcoin. -### Proving general computation +Any script change which enables commitment to pairs of items necessarily +enables commitments to Merkle trees. Merkle trees can be used to prove 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 they need -to be iterated over and hashed into a Merkle root. +There are practical limits to the total number of discrete output values that +can be reached by all possible input values (i.e. the input entropy space) as +they must be enumerated and hashed into a Merkle root. -Taproot MAST trees can currently cover 128 bits of entropy space, which is over -the practical limits to iterate over and merklize. Therefore, we conclude 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. - -There is a way to reduce the size of the witness for proving 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*. +Taproot trees can be 128 levels deep, therefore including up to 2^128 possible +output values. This is over the practical limits to enumerate and merkelize. +Therefore, we conclude that enabling Merkle commitments in script does not +materially extend what computations are possible to validate in bitcoin +script. While `OP_PAIRCOMMIT` is not explicitly limited to a height of 128, greater +heights are not practical to Merkelize. ## Backward Compatibility -By constraining the behavior of OP_SUCCESS opcodes, deployment of the BIP +By constraining the behavior of `OP_SUCCESS` opcodes, deployment of the BIP can be done in a backwards-compatible, soft-fork manner. If anyone were to -rely on the OP_SUCCESS behavior of `OP_SUCCESS205`, `OP_PAIRCOMMIT` would +rely on the `OP_SUCCESS` behavior of `OP_SUCCESS205`, `OP_PAIRCOMMIT` would invalidate their spend. ## Deployment @@ -230,7 +280,7 @@ TBD ## Credits -Jeremy Rubin, Brandon Black, Salvatore Ingala, Anthony Towns, Ademan555 +Jeremy Rubin, Salvatore Ingala, Anthony Towns, Ademan555, Psifour ## Copyright @@ -239,7 +289,7 @@ This document is licensed under the 3-clause BSD license. ## References 1. LNhance bitcoin repository: [lnhance] -2. LN-Symmetry: [eltoo] +2. Lightning Symmetry: [eltoo] 3. OP_CAT: [BIP-347], [BIN-2024-0001] 4. OP_CHECKTEMPLATEVERIFY: [BIP-119] 5. OP_CHECKSIGFROMSTACK: [BIP-348], [BIN-2024-0003] @@ -250,9 +300,13 @@ This document is licensed under the 3-clause BSD license. [eltoo]: https://github.com/instagibbs/bolts/blob/eltoo_draft/XX-eltoo-transactions.md [CAT-tricks-I]: https://medium.com/blockstream/cat-and-schnorr-tricks-i-faf1b59bd298 [CAT-tricks-II]: https://medium.com/blockstream/cat-and-schnorr-tricks-ii-2f6ede3d7bb5 +[MATT]: https://merkle.fun +[Lightning Symmetry write-up]: https://delvingbitcoin.org/t/ln-symmetry-project-recap/359 +[key laddering post]: https://rubin.io/bitcoin/2024/12/02/csfs-ctv-rekey-symmetry/ [//]: # (BIPs referenced) [BIP-119]: https://github.com/bitcoin/bips/tree/master/bip-0119.mediawiki +[BIP-327]: https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki [BIP-340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki [BIP-347]: https://github.com/bitcoin/bips/blob/master/bip-0347.mediawiki [BIP-348]: https://github.com/bitcoin/bips/blob/master/bip-0348.md @@ -263,3 +317,4 @@ This document is licensed under the 3-clause BSD license. [//]: # (Internal links) [optimal]: #cost-comparison-of-ln-symmetry-constructions + From 6375ee19869c6d7c4378a99fd114e96c72d0ecde Mon Sep 17 00:00:00 2001 From: moonsettler Date: Wed, 12 Feb 2025 18:10:20 +0100 Subject: [PATCH 3/4] Fix: Authors and spelling merklize --- README.mediawiki | 2 +- bip-0442.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.mediawiki b/README.mediawiki index a50ce78d3f..8d5f1c4e70 100644 --- a/README.mediawiki +++ b/README.mediawiki @@ -1299,7 +1299,7 @@ Those proposing changes should consider that ultimately consent may rest with th | [[bip-0442.md|442]] | Consensus (soft fork) | OP_PAIRCOMMIT -| moonsettler +| moonsettler, Brandon Black | Standard | Draft |} diff --git a/bip-0442.md b/bip-0442.md index 11fdb30005..2e30c3dc95 100644 --- a/bip-0442.md +++ b/bip-0442.md @@ -149,7 +149,7 @@ Lightning Symmetry channel construction is close to [optimal]. ### In MATT -The Merkelize All The Things ([MATT]) framework proposes to use `OP_CAT` to +The Merklize All The Things ([MATT]) framework proposes to use `OP_CAT` to combine multiple items into a single commitment for use with `OP_CHECKCONTRACTVERIFY`. `OP_PAIRCOMMIT` provides a more ergonomic way to accomplish this[^4]. @@ -261,11 +261,11 @@ can be reached by all possible input values (i.e. the input entropy space) as they must be enumerated and hashed into a Merkle root. Taproot trees can be 128 levels deep, therefore including up to 2^128 possible -output values. This is over the practical limits to enumerate and merkelize. +output values. This is over the practical limits to enumerate and merklize. Therefore, we conclude that enabling Merkle commitments in script does not materially extend what computations are possible to validate in bitcoin script. While `OP_PAIRCOMMIT` is not explicitly limited to a height of 128, greater -heights are not practical to Merkelize. +heights are not practical to Merklize. ## Backward Compatibility From da2b69cc761c5b1acec594b8e9e34d730b8b2692 Mon Sep 17 00:00:00 2001 From: moonsettler Date: Wed, 12 Feb 2025 23:47:06 +0100 Subject: [PATCH 4/4] Fix: header --- bip-0442.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bip-0442.md b/bip-0442.md index 2e30c3dc95..577a73ff18 100644 --- a/bip-0442.md +++ b/bip-0442.md @@ -1,4 +1,4 @@ -
+```
   BIP: 442
   Layer: Consensus (soft fork)
   Title: OP_PAIRCOMMIT
@@ -9,7 +9,7 @@
   Type: Standards Track
   Created: 2024-12-09
   License: BSD-3-Clause
-
+``` ## Abstract