This details the exact format of on-chain transactions, which both sides need to agree on to ensure signatures are valid. This consists of the funding transaction output script, the update transactions, and the settlement transactions.
- Mempool Policy and Consensus Changes Required
- Transactions
- Keys
- References
- Authors
The rest of the document presumes a specific set of mempool policy enhancements and consensus changes.
roughly detailed here.
The consensus assumptions are:
- These transactions assume BIP118 in its current written form.
The policy assumptions are:
- Package relay is implemented and deployed, as per this link
- Package RBF is adopted for rule#3 pinning mitigation.
- Ephemeral Anchors are implemented.
- Annex data is allowed, up to at least 33 bytes worth for our purposes. Should update to this format.
- Mitigation for ACP-style pinning on APO (how to stop 0-value junk inputs from being added to APO transactions?)
The biggest difference between this BOLT and BOLT03 is the fact that channel updates now have symmetrical state.
Each party in the relevant channel has an identical view of the transactions being collaboratively created. This means that we no longer rely on revocation and punishment transactions to enforce correctness, but on-chain claim and response periods via two types of transactions:
- update transactions to make a "claim" of latest channel state
- settlement transactions to expand out payments once challenge phase ends.
The same rules for output ordering from BOLT03 applies to update and settlement transactions at time of signing.
All outputs except ephemeral anchors are pay-to-taprootBIP341 (P2TR) outputs. We are ommitting all non-script related witness stack items for brevity such as control block, inner pubkeys, et al.
A <>
designates an empty vector as required for compliance with BIP342
All taproot leaf versions are 0xC0 unless stated otherwise.
Miniscript compatibille scripts are used whenever possible to avoid ambiguity in notation, and allow for further analysis.
- The value is the channel capacity.
TL(n) = 500000000+o+n
-
Where
o
equals the state-masking offset(always 0 for now) -
Where
n
equals the channel state version, starting at 0 -
The funding output script is a P2TR to:
sorted_pubkey1, sorted_pubkey2 = KeySort(<set of funding_pubkey fields negotiated>)
aggregated_key = KeyAgg(sorted_pubkey1, sorted_pubkey2)
tr(aggregated_key, EXPR_UPDATE(0))
- where EXPR_UPDATE(x) =
<1> OP_CHECKSIGVERIFY <TL(x)> OP_CHECKLOCKTIMEVERIFY
if x > 0
with the policy of and(pk(1),after(TL(x)))
else
<1> OP_CHECKSIG
, in the case of x == 0
with the policy of pk(1)
-
Output descriptors as defined by BIP386 and abused by the author.
-
Where
KeyAgg
andKeySort
are defined as per BIP-musig2.
- version: 3
- locktime: TL(n), where
n
is the state number for this update transaction - txin count: 1
txin[0]
outpoint:txid
andoutput_index
from last published statem
output (can be 0, the funding output)txin[0]
sequence: 0xFFFFFFFDtxin[0]
script bytes: 0txin[0]
witness:- annex: 0x50 followed by ''hashTapLeaf(0xC0 || compact_size(size of EXPR_SETTLE(m)) || EXPR_SETTLE(m))''
- control block: 0xC0 marker for tapscript, internel public key, merkle proof unless spending funding tx
- tapscript: EXPR_UPDATE(m+1)
signature_for_inner_pubkey
- txout count: 2
txout[0]
amount: 0txout[0]
script:OP_2
(ephemeral anchor)txout[1]
amount: the channel capacitytxout[1]
script:tr(aggregated_key, {EXPR_UPDATE(n+1), EXPR_SETTLE(n)})
and where EXPR_SETTLE(n) =
<CovSig(TL(n))> <1_G> OP_CHECKSIG
where CovSig(x)
is the SIGHASH_ALL|ANYPREVOUTANYSCRIPT signature of the corresponding settlement transaction with a
locktime of x
, and 1_G
the 33-byte BIP118 public key matching the secp256k1 generator G
, using the BIP340 "Default Signing" nonce derivation function, with no auxiliary input.
and where `signature_for_inner_pubkey uses SIGHASH_ALL|ANYPREVOUTANYSCRIPT.
Note that the locktime must increase monotonically as it's used as the consensus ratchet for allowing rebinding of updates.
FIXME Use some standardized annex formatting to avoid app/consensus collisions, aka Sunny Side of Annex
Anyprevout style covenants are used in the update transaction taptree to avoid requiring additional communication round-trips for forwarding HTLC messages. If we required a standard MuSig2 signature for the settlement transaction, it would be unsafe to hand your counterparty both update and settlement signatures in one step, as the counterparty could ransom your funds by publishing a completed update transaction, and withhold the final settlement signature.
We use the public generator G as a "well known" point, where privkey is 1
,
so the signature can be recreated by any software, including watchtowers. The script
does not need rotation since the signature is committed to directly in the script,
and the signature directly commits to the settlement transaction, including the nlocktime
which is unique per update in a given channel.
The annex
is required to allow recovery of the control block to spend an invalidated update
transaction with the latest update transaction, without requiring particpants to hold all invalidated
settlement states forever. This is due to the signature that is included in the script, which cannot
be otherwise deterministically regenerated without access to the full settlement state.
Alternatively, we could add another round-trip to HTLC additions and forwards,
as well as further complicate the protocol.
The "state-masking offset" is used to hide the total number of updates in the channel from blockchain observers. Future versions of this spec can introduce a randomized negotiation of this value.
The ephemeral anchor will always be of value 0(and the other non-0), so output sorting will result in the ephemeral anchor output being first.
- version: 3
- locktime: corresponding update transaction's locktime
- txin count: 1
txin[0]
outpoint:txid
andoutput_index
from latest committed staten
outputtxin[0]
sequence: set toshared_delay
, initially set in channel open negotiationtxin[0]
script bytes: 0txin[0]
witness:- control block: merkle proof
- tapscript: EXPR_SETTLE(n)
- (no additional witness data needed)
Note there may be additional attached transaction inputs due to the ANYPREVOUTANYSCRIPT signatures which can be used to attach fees during settlement.
Since we are relying on the shared_delay
timelock to ensure that the final update transaction confirmed on the blockchain
is indeed the final update in the channel, we do not require revocation paths or additional delays to spend the outputs
contained in the settlement transaction. We have no second-stage pre-signed transactions, simply outputs that can be
spent by the authorized parties. For balance outputs, these can be immedietly spent by the owners. For HTLC outputs,
these can be spent immediately once the receiving party obtains the preimage, or can be clawed back by the offerer
once the CLTV of the output expires.
The lack of second-stage transactions means that HTLCs must have timeouts longer than the shared_delay
timeout to ensure on-chain enforcement.
Alternative proposals such as layered commitments for eltoo have been proposed, but require increased complexity and additional layer violations.
The amounts for each output MUST be rounded down to whole satoshis. If this amount is less than the dust_limit_satoshis
set by the owner of the output, the output MUST NOT be produced (thus the funds add to fees).
This output sends funds back to the owner of the satoshi amount. It can be claimed without delay. The output is a P2TR of the form:
rawtr(settlement_pubkey)
There are N
copies of this output, one for each channel participant and their associated settlement_pubkey
sent during channel negotiation.
Ideally, we could directly use a raw script to allow maximal flexibility, but we do not want to ensure that all output scripts given are mempool-standard. We do not need one block relative timelock in scripts since we can no longer be pinned by package sizes(bip125 rule#3) or package limits thanks to V3 transactions and ephemeral anchors.
The output encumbers funds to the receipient of the HTLC offer with a primage, or back to the offerer upon timeout. Unlike BOLT03, these require no second stage transactions, and can be signed at any point.
tr(aggregated_key, {EXPR_SUCCESS, EXPR_TIMEOUT})
where EXPR_SUCCESS =
<htlc_pubkey> OP_CHECKSIGVERIFY OP_SIZE <20> OP_EQUALVERIFY OP_HASH160 <H> OP_EQUAL
with a policy of and(pk(htlc_pubkey),hash160(H))
where H
is the payment hash and htlc_pubkey
the recipient pubkey
and EXPR_TIMEOUT =
<htlc_pubkey> OP_CHECKSIGVERIFY N OP_CHECKLOCKTIMEVERIFY
with policy of and(after(N),pk(htlc_pubkey))
where N
is the HTLC expiry blockheight, and htlc_pubkey
is the offerer's pubkey.
The key-spend path is currently unused.
The recipient node can redeem the HTLC with the witness:
<payment_preimage> <recipient_htlc_pubkey_signature>
And the offerer via:
<offerer_htlc_pubkey_signature>
on an otherwise valid transaction.
A single shared anchor output, also known as ephemeral anchor, is attached to the settlement transaction. This MUST be spent in a relay package to be considered for block inclusion.
This output contains the scriptpubkey of:
OP_TRUE
, the output value is the value of all the trimmed output values, summed.
This allows an "empty" spend of the output by anyone, and no known mempool malleabilty vectors.
There is no consensus-level fix for malleability with bare scripts that lack a sighash, so that is the best we are going to do without committing to an expensive script hash.
Outputs with value below dust_limit_satoshis
should not be produced;
these outputs that are not produced are termed "trimmed". A trimmed output is
considered too small to be worth creating and is instead added
to the anchor output's value to be used as CPFP fees.
Fees are handled by spending outputs from the outputs of the settlement transaction, as the settlement transaction includes no fee itself. This relies on package relay and package CPFP.
The settlement transaction:
- for every
to_node
output:- if the amount of any of the settlement transaction
to_node
outputs would be less thandust_limit_satoshis
set by the channel negotiation:- MUST NOT contain that output.
- otherwise:
- MUST be generated as specified in
to_node
Output.
- MUST be generated as specified in
- if the amount of any of the settlement transaction
- for every HTLC:
- if the HTLC amount would be less than
dust_limit_satoshis
:- MUST NOT contain that output.
- otherwise:
- MUST be generated as specified in HTLC Outputs.
- if the HTLC amount would be less than
Note that there are two possible variants for each node.
- version: 3
- locktime: 0
- txin count: 1
txin[0]
outpoint:txid
andoutput_index
fromfunding_created
messagetxin[0]
sequence: 0xFFFFFFFFtxin[0]
script bytes: 0txin[0]
witness:<signature_for_musig2_pubkey>
- txout count: 0, 1 or 2
txout
amount: final balance to be paid to one nodetxout
script: as specified in that node'sscriptpubkey
in itsshutdown
message
Each node offering a signature:
- MUST round each output down to whole satoshis.
- MUST remove any output below its own
dust_limit_satoshis
. - MAY eliminate its own output.
Contrary to ln-penalty based chanels, we have a shared dust_limit_satoshis
and a symmetrical transaction. Users can still opt to remove their own output
and the signature will indicate to the peer which variant has been used.
There will be at least one output, if the funding amount is greater
than twice dust_limit_satoshis
.
The settlement transaction contains no fees, so implementers must rely on CPFP of the single anchor output for propogation and timely inclusion into the blockchain.
Transaction replacement of the CPFP can be a way to increase the feerate of the overall package, even if a counterparty is making CPFP transactions.
These are statically set at 330 satoshis.
This section ties the previous sections together to detail the
algorithm for constructing the update and settlement transactions for both peers:
given that peer's dust_limit_satoshis
:
This transaction is relatively trivial to set up.
- Initialize the update transaction input, output, and locktime as specified in Update Transaction.
- Initialize the settlement transaction input and locktime, as specified in Settlement Transaction.
- Calculate which committed HTLCs and
to_node
outputs need to be trimmed (see Trimmed Outputs). - For every HTLC, if it is not trimmed, add an HTLC output.
- For every
to_node
output, if it is not trimmed, add ato_node
output. - Add a single ephemeral output anchor.
- Sort the outputs into BIP 69+CLTV order.
Greg Sanders gsanders87@gmail.com
This work is licensed under a Creative Commons Attribution 4.0 International License.