From 0ad3d24aa434487873e94f1fb28e0fce08c8c33e Mon Sep 17 00:00:00 2001 From: moonsettler Date: Sat, 28 Dec 2024 18:50:22 +0100 Subject: [PATCH 1/3] 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 7950e107d8..90d1bc7507 100644 --- a/README.mediawiki +++ b/README.mediawiki @@ -1288,6 +1288,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 47e105378aed7a50a50c404f6bf2757a65816f3e Mon Sep 17 00:00:00 2001 From: Brandon Black Date: Tue, 31 Dec 2024 16:26:47 -0800 Subject: [PATCH 2/3] Revise text for readability and sequence --- bip-0442.md | 309 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 176 insertions(+), 133 deletions(-) diff --git a/bip-0442.md b/bip-0442.md index 706ebba791..43b74f5e69 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,75 @@ This BIP describes a new tapscript opcode `OP_PAIRCOMMIT`, which provides limited vector commitment functionality in tapscript. -When evaluated, the `OP_PAIRCOMMIT` instruction: +## Summary + +When verifying taproot script spends having leaf version 0xc0 (as defined in +[BIP-342]), we propose `OP_PAIRCOMMIT` to replace `OP_SUCCESS205` (0xcd). + +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. -## 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)`. +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 - -```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); +* 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. - uint256 hash = PairCommitHash(vch1, vch2); +[^1]: The number of SHA256 iterations is minimized in the typical use cases. + Since the Tag can be pre-computed as mid-state, it takes only 2 hash + cycles for a commitment to 2 32-byte items or 1 for 2 smaller items. - stack.pop_back(); - stack.pop_back(); - stack.emplace_back(hash.begin(), hash.end()); - break; -} -``` -```c++ -const HashWriter HASHER_PAIRCOMMIT{TaggedHash("PairCommit")}; +## Motivation -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 stack elements 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. + +In a contrived but demonstrative example, if PAIRCOMMIT existed in legacy +script, P2PKH could be extended to pay-to-pubkey-time-hash with scriptPubKey +`2DUP PAIRCOMMIT RIPEMD160 EQUALVERIFY CHECKLOCKTIMEVERIFY DROP +CHECKSIG`. This script format for single signature, time-locked bitcoin could +be transformed into an address format. + +With the ability to commit to pairs of elements, 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 PAIRCOMMIT, the ability to sign +commitments to multiple items enables both of the above but as a form of +delegation. In cases where any single item can be used to unlock an output, +additional off chain signatures be simply can be used as an alternative to +PAIRCOMMIT. However in cases where multiple items must be used for spending +together, PAIRCOMMIT removes the need for costly laddering schemes[^2] to +ensure that all items were authorized together and in order. + +[^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. + +## 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, @@ -92,32 +98,41 @@ highly optimized for just 2 stack elements. ### 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. +To do Lightning Symmetry contracts that don't require the nodes to keep old +states and avoid additional signing round trips (and corresponding signature +validations), 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]. +Using in sequence `OP_CHECKTEMPLATEVERIFY` (`CTV`), `OP_PAIRCOMMIT` (`PC`), +`OP_INTERNALKEY` (`IK`) and `OP_CHECKSIGFROMSTACK` (`CSFS`) we can construct a +rebindable channel that is close to [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. +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] +[^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,97 +140,121 @@ ELSE ENDIF ``` -### Use with future updates +### In [MATT] -Detailed introspection opcodes would also need vector commitments when used -with `OP_CHECKSIGFROMSTACK`. +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]. -`OP_CHECKCONTRACTVERIFY` would also need a way to carry complex data. - -## 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 or hashing, or both. -A reference implementation is provided here: +## Alternative approaches -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. +The following list of alternative approaches were discussed and rejected for +various reasons, either for expanding the scope or for unnecessary complexity: -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_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. -`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) +## Reference Implementation -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`. +### Code -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. +```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); -### Behaviors LNhance tries to avoid introducing + uint256 hash = PairCommitHash(vch1, vch2); -The following behaviors are out of scope for LNhance and should not be enabled -as a side effect without explicit consensus: + stack.pop_back(); + stack.pop_back(); + stack.emplace_back(hash.begin(), hash.end()); + break; +} +``` +```c++ +const HashWriter HASHER_PAIRCOMMIT{TaggedHash("PairCommit")}; -* Fine-grained introspection -* State-carrying covenants -* Bigint operations -* New arithmetic capabilities using lookup tables +uint256 PairCommitHash(const std::vector& x1, const std::vector& x2) +{ + return (HashWriter{HASHER_PAIRCOMMIT} << x1 << x2).GetSHA256(); +} +``` -### Alternative approaches +### PR -The following list of alternative approaches were discussed and rejected for -various reasons, either for expanding the scope or for unnecessary complexity: +https://github.com/lnhance/bitcoin/pull/6/files -* 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 +## Rationale ### 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 | +The following table briefly summarizes the costs associated with force closing +Lightning Symmetry channels enabled by various combinations of proposed script +upgrades. + +| 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 | +| 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 LN-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 -### 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. -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 already cover 128 bits of entropy space, which is over the +practical limits to iterate over and merkelize. Therefore, we conclude that +enabling Merkle commitments in script does not materially extend what +computations are possible to prove in bitcoin script. While `OP_PAIRCOMMIT` is +not limited to a height of 128, greater heights are not practically +computable. ## Backward Compatibility @@ -230,7 +269,7 @@ TBD ## Credits -Jeremy Rubin, Brandon Black, Salvatore Ingala, Anthony Towns, Ademan555 +Jeremy Rubin, Salvatore Ingala, Anthony Towns, Ademan555 ## Copyright @@ -250,9 +289,12 @@ 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 [//]: # (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 +305,4 @@ This document is licensed under the 3-clause BSD license. [//]: # (Internal links) [optimal]: #cost-comparison-of-ln-symmetry-constructions + From 44ab6c6a2342e1a2604947c1d2bc8bc7e95d79ca Mon Sep 17 00:00:00 2001 From: Brandon Black Date: Fri, 7 Feb 2025 21:03:54 -0800 Subject: [PATCH 3/3] more improve words --- bip-0442.md | 102 +++++++++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 45 deletions(-) diff --git a/bip-0442.md b/bip-0442.md index 43b74f5e69..6e6203e37a 100644 --- a/bip-0442.md +++ b/bip-0442.md @@ -23,8 +23,7 @@ When verifying taproot script spends having leaf version 0xc0 (as defined in 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, +* 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 @@ -43,22 +42,16 @@ $$SHA256(SHA256(tag) \\| SHA256(tag) \\| x)$$. * The top two elements are popped from the stack. * $$pc$$ is pushed to the stack. -[^1]: The number of SHA256 iterations is minimized in the typical use cases. - Since the Tag can be pre-computed as mid-state, it takes only 2 hash - cycles for a commitment to 2 32-byte items or 1 for 2 smaller items. +[^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. ## Motivation -Currently, bitcoin lacks a way to commit to multiple stack elements 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. - -In a contrived but demonstrative example, if PAIRCOMMIT existed in legacy -script, P2PKH could be extended to pay-to-pubkey-time-hash with scriptPubKey -`2DUP PAIRCOMMIT RIPEMD160 EQUALVERIFY CHECKLOCKTIMEVERIFY DROP -CHECKSIG`. This script format for single signature, time-locked bitcoin could -be transformed into an address format. +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, PAIRCOMMIT can be generalized to Merklized commitments to a tree of elements with a single hash. On its own, @@ -66,12 +59,13 @@ 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 PAIRCOMMIT, the ability to sign -commitments to multiple items enables both of the above but as a form of -delegation. In cases where any single item can be used to unlock an output, -additional off chain signatures be simply can be used as an alternative to -PAIRCOMMIT. However in cases where multiple items must be used for spending -together, PAIRCOMMIT removes the need for costly laddering schemes[^2] to -ensure that all items were authorized together and in order. +commitments to multiple items enables complex delegation. For example, +delegating to $$key1$$ after time $$t1$$ and $$key2$$ after time $$t2$$. +Without 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 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 @@ -80,7 +74,8 @@ ensure that all items were authorized together and in order. 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. + constructions when committing to any sequence of items. For details, see + the [key laddering post] by Jeremy Rubin and Brandon Black. ## Examples @@ -96,22 +91,26 @@ highly optimized for just 2 stack elements. | PC PC OP_EQUALVERIFY ``` -### Use in LN-Symmetry - -To do Lightning Symmetry contracts that don't require the nodes to keep old -states and avoid additional signing round trips (and corresponding signature -validations), 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. +### Use in Lightning Symmetry -Using in sequence `OP_CHECKTEMPLATEVERIFY` (`CTV`), `OP_PAIRCOMMIT` (`PC`), -`OP_INTERNALKEY` (`IK`) and `OP_CHECKSIGFROMSTACK` (`CSFS`) we can construct a -rebindable channel that is close to [optimal]. +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] +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 @@ -140,6 +139,14 @@ ELSE ENDIF ``` +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]. + ### In [MATT] The Merkelize All The Things (MATT) framework proposes to use `OP_CAT` to @@ -150,7 +157,7 @@ accomplish this[^4]. [^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 or hashing, or both. + requires either length checking, hashing, or both. ## Alternative approaches @@ -219,7 +226,7 @@ https://github.com/lnhance/bitcoin/pull/6/files ## Rationale -### Cost comparison of LN-Symmetry constructions +### Cost comparison of Lightning Symmetry constructions The following table briefly summarizes the costs associated with force closing Lightning Symmetry channels enabled by various combinations of proposed script @@ -234,27 +241,31 @@ upgrades. | 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 +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. + 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 trees can already cover 128 bits of entropy space, which is over the -practical limits to iterate over and merkelize. Therefore, we conclude that -enabling Merkle commitments in script does not materially extend what -computations are possible to prove in bitcoin script. While `OP_PAIRCOMMIT` is -not limited to a height of 128, greater heights are not practically -computable. +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 PAIRCOMMIT is not explicitly limited to a height of 128, greater +heights are not practical to Merkelize. ## Backward Compatibility @@ -278,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] @@ -291,6 +302,7 @@ This document is licensed under the 3-clause BSD license. [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