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

OP_PAIRCOMMIT #1699

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.mediawiki
Original file line number Diff line number Diff line change
Expand Up @@ -1267,6 +1267,13 @@ Those proposing changes should consider that ultimately consent may rest with th
| Gloria Zhao
| Informational
| Draft
|-
| [[bip-PC.md|PC]]
| Consensus (soft fork)
| OP_PAIRCOMMIT
| moonsettler
| Standard
| Draft
|}

<!-- IMPORTANT! See the instructions at the top of this page, do NOT JUST add BIPs here! -->
187 changes: 187 additions & 0 deletions bip-PC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<pre>
BIP: ?
Layer: Consensus (soft fork)
Title: OP_PAIRCOMMIT
Author: moonsettler <[email protected]>
Comments-Summary: No comments yet.
Comments-URI: <links to wiki page for comments>
Status: Draft
Type: Standards Track
Created: 2024-11-08
License: BSD-3-CLAUSE
</pre>

## Abstract

This BIP describes a new tapscript opcode `OP_PAIRCOMMIT` which
provide 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.

## 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.
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 number of SHA256 iterations is minimized in the primary use case we
can optimize for, which is LN-Symmetry. 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);

popstack(stack);
popstack(stack);
stack.emplace_back(hash.begin(), hash.end());
break;
}
```
```c++
const HashWriter HASHER_PAIRCOMMIT{TaggedHash("PairCommit")};

uint256 PairCommitHash(const std::vector<unsigned char>& x1, const std::vector<unsigned char>& 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))

<a> <b> <c> | PC PC <pc-hash> OP_EQUALVERIFY
```

### Use in LN-Symmetry

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
<sig> <state-n-recovery-data> <state-n-hash> | CTV PC IK CSFS <S+1> CLTV DROP
```
before funding sign first state template:
```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
<sig> <state-m-recovery-data> <state-m-hash> | CTV PC IK CSFS <S+n+1> CLTV DROP
ELSE
<settlement-n-hash> CTV
ENDIF
```

## Reference Implementation

A reference implementation is provided here:

https://github.com/lnhance/bitcoin/pull/6/files

moonsettler marked this conversation as resolved.
Show resolved Hide resolved
## 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.

`OP_PAIRCOMMIT` solves this specific problem without introducing a wide range
of potentially controversial new behaviors, such as novel 2-way peg mechanisms.

### Behaviours 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

## 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

## Copyright

This document is licensed under the 3-clause BSD license.

## References

1. LNhance bitcoin repository, [lnhance](https://github.com/lnhance/bitcoin)
2. LN-Symmetry, [eltoo](https://github.com/instagibbs/bolts/blob/eltoo_draft/XX-eltoo-transactions.md)
3. OP_CAT, [BIN-2024-0001](https://github.com/bitcoin-inquisition/binana/blob/master/2024/BIN-2024-0001.md)
4. OP_CHECKTEMPLATEVERIFY, [BIP-119](https://github.com/bitcoin/bips/tree/master/bip-0119)
5. OP_CHECKSIGFROMSTACK, [BIN-2024-0003](https://github.com/bitcoin-inquisition/binana/blob/master/2024/BIN-2024-0003.md)
6. OP_INTERNALKEY, [BIN-2024-0004](https://github.com/bitcoin-inquisition/binana/blob/master/2024/BIN-2024-0004.md)
7. Tagged hash, [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki)
Loading