mirror of
https://github.com/lightning/bolts.git
synced 2025-02-22 22:25:42 +01:00
BOLT 2/3: watcher-compatible key derivation.
After much discussion with Tadge and Laolu, I think we have something which is nicely outsourcable, and yetnot insanely complex. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
17060cbf9d
commit
32779962b5
2 changed files with 73 additions and 41 deletions
|
@ -64,23 +64,17 @@ desire to set up a new channel.
|
|||
* [4:max-num-htlcs]
|
||||
* [4:feerate-per-kb]
|
||||
* [2:to-self-delay]
|
||||
* [33:Funding pubkey]
|
||||
* [33:HAKD base point]
|
||||
* [33:Refund base point]
|
||||
* [33:funding-pubkey]
|
||||
* [33:revocation-basepoint]
|
||||
* [33:payment-basepoint]
|
||||
* [33:delayed-payment-basepoint]
|
||||
|
||||
|
||||
The `temporary-channel-id` is used to identify this channel until the funding transaction is established. `funding-satoshis` is the amount the sender is putting into the channel. `dust-limit-satoshis` is the threshold below which no HTLC output should be generated for this node’s commitment transaction; ie. HTLCs below this amount are not enforceable onchain. This reflects the reality that tiny outputs are not considered standard transactions and will not propagate through the bitcoin network.
|
||||
`max-htlc-value-in-inflight-msat` is a cap on total value of outstanding HTLCs, which allows a node to limit its exposure to HTLCs; similarly `max-num-htlcs` limits the number of outstanding HTLCs the other node can offer. `channel-reserve-satoshis` is the minimum amount that the other node is to keep as a direct payment. `htlc-minimum-msat` indicates the smallest value HTLC this node wil accept. `feerate-per-kb` indicates the initial fee rate which this side will pay for commitment and HTLC transactions (this can be adjusted later with a `update_fee` message). `to-self-delay` is the number of block that the other nodes to-self outputs must be delayed, using `OP_CHECKSEQUENCEVERIFY` delays; this is how long it will have to wait in case of breakdown before redeeming its own funds.
|
||||
|
||||
|
||||
The `funding-pubkey` is the public key in the 2-of-2 multisig script of the funding transaction output. The `hakd-base-point` is combined with the revocation hash for this commitment transaction to generate a unique revocation key for this commitment transaction (HAKD = homomorphic adversarial key derivation). The `refund-base-point` is similarly used to generate a series of keys for non-HTLC outputs, ensuring that the transaction ID of each commitment transaction is unpredictable by an external observer, even if one commitment transaction is seen: this property is very useful for preserving privacy when outsourcing penalty transactions to third parties.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
The `funding-pubkey` is the public key in the 2-of-2 multisig script of the funding transaction output. The `revocation-basepoint` is combined with the revocation preimage for this commitment transaction to generate a unique revocation key for this commitment transaction. The `payment-basepoint` and `delayed-payment-basepoint` are similarly used to generate a series of keys for any payments to this node: `delayed-payment-basepoint` is used to for payments encumbered by a delau. Varying these keys ensures that the transaction ID of each commitment transaction is unpredictable by an external observer, even if one commitment transaction is seen: this property is very useful for preserving privacy when outsourcing penalty transactions to third parties.
|
||||
|
||||
FIXME: Describe Dangerous feature bit for larger channel amounts.
|
||||
|
||||
|
@ -97,7 +91,7 @@ The sender SHOULD set `to-self-delay` sufficient to ensure the sender
|
|||
can irreversibly spend a commitment transaction output in case of
|
||||
misbehavior by the receiver. The sender SHOULD set `minimum-depth` to
|
||||
an amount where the sender considers reorganizations to be low risk.
|
||||
`funding-pubkey` and `commit-point` MUST be valid DER-encoded
|
||||
`funding-pubkey`, `revocation-basepoint`, `payment-basepoint` and `delayed-payment-basepoint` MUST be valid DER-encoded
|
||||
compressed secp256k1 pubkeys. The sender SHOULD set `feerate-per-kb`
|
||||
to at least the rate it estimates would cause the transaction to be
|
||||
immediately included in a block.
|
||||
|
@ -121,7 +115,7 @@ The receiving node MAY fail the channel if it considers
|
|||
|
||||
The receiver MUST fail the channel if
|
||||
considers `feerate-per-kb` too small for timely processing. The
|
||||
receiver MUST fail the channel if `funding-pubkey` or `commit-point`
|
||||
receiver MUST fail the channel if `funding-pubkey`, `revocation-basepoint`, `payment-basepoint` or `delayed-payment-basepoint`
|
||||
are not be valid DER-encoded compressed secp256k1 pubkeys.
|
||||
|
||||
|
||||
|
@ -161,12 +155,12 @@ acceptance of the new channel.
|
|||
* [4:minimum-depth]
|
||||
* [4:htlc-minimum-msat]
|
||||
* [4:max-num-htlcs]
|
||||
* [32:first-commitment-key-offset]
|
||||
* [2:to-self-delay]
|
||||
* [33:funding-pubkey]
|
||||
* [33:HAKD base point]
|
||||
* [33:Refund base point]
|
||||
|
||||
* [33:revocation-basepoint]
|
||||
* [33:payment-basepoint]
|
||||
* [33:delayed-payment-basepoint]
|
||||
* [33:first-per-commitment-point]
|
||||
|
||||
### Requirements
|
||||
|
||||
|
@ -219,8 +213,7 @@ This message indicates that the funding transaction has reached the `minimum-dep
|
|||
2. data:
|
||||
* [8:temporary-channel-id]
|
||||
* [8:channel-id]
|
||||
* [32:next-key-offset]
|
||||
* [33:next-revocation-halfkey]
|
||||
* [33:next-per-commitment-point]
|
||||
|
||||
The `channel-id` is the unique description of the funding transaction.
|
||||
It is constructed with the most significant 3 bytes as the block
|
||||
|
@ -689,8 +682,8 @@ The description of key derivation is in [BOLT #3](03-transactions.md#key-derivat
|
|||
2. data:
|
||||
* [8:channel-id]
|
||||
* [32:per-commitment-secret]
|
||||
* [32:next-key-offset]
|
||||
* [33:next-revocation-halfkey]
|
||||
* [33:next-per-commitment-point]
|
||||
* [3:padding]
|
||||
* [4:num-htlc-timeouts]
|
||||
* [num-htlc-timeouts*64:htlc-timeout-signature]
|
||||
|
||||
|
@ -698,10 +691,10 @@ The description of key derivation is in [BOLT #3](03-transactions.md#key-derivat
|
|||
|
||||
|
||||
A sending node MUST set `per-commitment-secret` to the secret used to generate keys for the
|
||||
previous commitment transaction, and must set `next-key-offset` and `next-revocation-halfkey` to the values for its next commitment transaction.
|
||||
previous commitment transaction, MUST set `next-per-commitment-point` to the values for its next commitment transaction, and MUST set `padding` to all zeroes.
|
||||
|
||||
|
||||
A receiving node MUST check that `per-commitment-secret` generates the previous `key-offset` and `revocation-halfkey`, and MUST fail if it does not. A receiving node MAY fail if the `per-commitment-secret` was not generated by the protocol in [BOLT #3](03-transactions.md#per-commitment-secret-requirements).
|
||||
A receiving node MUST check that `per-commitment-secret` generates the previous `per-commitment-point`, and MUST fail if it does not. A receiving node MUST ignore the value of `padding`. A receiving node MAY fail if the `per-commitment-secret` was not generated by the protocol in [BOLT #3](03-transactions.md#per-commitment-secret-requirements).
|
||||
|
||||
|
||||
A receiving node MUST fail the channel if any htlc-timeout-signature is not valid, or if num-htlc-timeout is not equal to the number of outputs in the sending node's commitment transaction corresponding to HTLCs offered be the sending node.
|
||||
|
|
|
@ -31,11 +31,11 @@ The reason for the separate transaction stage for HTLC outputs is so that HTLCs
|
|||
|
||||
#### To-Local Output
|
||||
|
||||
This output sends funds back to the owner of this commitment transaction (ie. `<localkey>`), thus must be timelocked using OP_CSV. The output is a version 0 P2WSH, with a witness script:
|
||||
This output sends funds back to the owner of this commitment transaction, thus must be timelocked using OP_CSV. The output is a version 0 P2WSH, with a witness script:
|
||||
|
||||
to-self-delay OP_CHECKSEQUENCEVERIFY OP_DROP <localkey> OP_CHECKSIG
|
||||
to-self-delay OP_CHECKSEQUENCEVERIFY OP_DROP <local-delayedkey> OP_CHECKSIG
|
||||
|
||||
It is spent by a transaction with nSequence field set to `to-self-delay` (which can only be valid after that duration has passed), and witness script `<localsig>`.
|
||||
It is spent by a transaction with nSequence field set to `to-self-delay` (which can only be valid after that duration has passed), and witness script `<local-delayedsig>`.
|
||||
|
||||
#### To-Remote Output
|
||||
|
||||
|
@ -62,7 +62,6 @@ The remote node can redeem the HTLC with the scriptsig:
|
|||
|
||||
Either node can use the HTLC-timeout transaction to time out the HTLC once the HTLC is expired, as show below.
|
||||
|
||||
|
||||
#### Received HTLC Outputs
|
||||
|
||||
This output sends funds to the remote peer after the HTLC timeout, or to an HTLC-success transaction with a successful payment preimage. The output is a P2WSH, with a witness script:
|
||||
|
@ -79,11 +78,11 @@ This output sends funds to the remote peer after the HTLC timeout, or to an HTLC
|
|||
OP_CHECKSIGVERIFY
|
||||
OP_ENDIF
|
||||
|
||||
To timeout the htlc, the local node spends it with the scriptsig:
|
||||
To timeout the htlc, the remote node spends it with the scriptsig:
|
||||
|
||||
<remotesig> 0
|
||||
|
||||
To redeem the HTLC, the HTLC-success transaction is used as detailed below.
|
||||
To redeem the HTLC, the HTLC-success transaction is used as detailed below.
|
||||
|
||||
## HTLC-Timeout and HTLC-Success Transaction
|
||||
These HTLC transactions are almost identical, except the HTLC-Timeout transaction is timelocked. This is also the transaction which can be spent by a valid penalty transaction.
|
||||
|
@ -104,39 +103,79 @@ The witness script for the output is:
|
|||
|
||||
OP_IF
|
||||
# Penalty transaction
|
||||
<revocation pubkey>
|
||||
<revocation-pubkey>
|
||||
OP_ELSE
|
||||
`to-self-delay`
|
||||
OP_CSV
|
||||
OP_DROP
|
||||
<localkey>
|
||||
<local-delayedkey>
|
||||
OP_ENDIF
|
||||
OP_CHECKSIG
|
||||
|
||||
To spend this via penalty, the remote node uses a witness stack `<revocationsig> 1` and to collect the output the local node uses an input with nSequence `to-self-delay` and a witness stack `<localsig> 0`
|
||||
To spend this via penalty, the remote node uses a witness stack `<revocationsig> 1` and to collect the output the local node uses an input with nSequence `to-self-delay` and a witness stack `<local-delayedsig> 0`
|
||||
|
||||
# Key Derivation
|
||||
|
||||
Each commitment transaction uses a unique set of keys; <localkey>, <remotekey> and <revocationkey>. Changing the <localkey> and <remotekey> every time ensures that commitment txids cannot be determined by a third party even it knows another commitment transaction, which helps preserve privacy in the case of outsourced penalties. The <revocationkey> is generated such that the remote node is the only one in possession of the secret key once the commitment transaction has been revoked.
|
||||
Each commitment transaction uses a unique set of keys; `<localkey>` and `<remotekey>`. The HTLC-success and HTLC-timeout transactions use `<local-delayedkey>` and `<revocationkey>`. These are changed every time depending on the
|
||||
`per-commitment-point`.
|
||||
|
||||
Keys change because of the desire for trustless outsourcing of
|
||||
watching for revoked transactions; a "watcher" should not be able to
|
||||
determine what the contents of commitment transaction is, even if
|
||||
given the transaction ID to watch for and can make a resonable guess
|
||||
as to what HTLCs and balances might be included. Nonetheless, to
|
||||
avoid storage for every commitment transaction, it can be given the
|
||||
`per-commit-secret` values (which can be stored compactly) and the
|
||||
`revocation-basepoint` and `delayed-payment-basepoint` to regnerate
|
||||
the scripts required for the penalty transaction: it need only be
|
||||
given (and store) the signatures for each penalty input.
|
||||
|
||||
Changing the `<localkey>` and `<remotekey>` every time ensures that commitment transaction id cannot be guessed: Every commitment transaction uses one of these in its output script. Splitting the `<local-delayedkey>` which is required for the penalty transaction allows that to be shared with the watcher without revealing `<localkey>`; even if both peers use the same watcher, nothing is revealed.
|
||||
|
||||
Finally, even in the case of normal unilateral close, the HTLC-success
|
||||
and/or HTLC-timeout transactions do not reveal anything to the
|
||||
watcher, as it does not know the corresponding `per-commit-secret` and
|
||||
cannot relate the `<local-delayedkey>` or `<revocationkey>` with
|
||||
their bases.
|
||||
|
||||
For efficiency, keys are generated from a series of per-commitment secrets which are generated from a single seed, allowing the receiver to compactly store them (see [below](#efficient-per-commitment-secret-storage)).
|
||||
|
||||
### localkey and remotekey Derivation
|
||||
## `localkey`, `remotekey`, `local-delayedkey` and `remote-delayedkey` Derivation
|
||||
|
||||
The localkey for a commitment transaction is generated by EC addition of the local `refund base point` and the current local `key-offset` multiplied by G (eg. secp256k1_ec_pubkey_tweak_add() from libsecp256k1). The local node knows the secret key corresponding to `refund base point` so can similarly derive the secret key for `localkey`.
|
||||
These keys are simply generated by addition from their base points:
|
||||
|
||||
The `key-offset` is generated using HMAC(`per-commit-secret`, “R”) [FIXME: more detail!].
|
||||
pubkey = basepoint + SHA256(per-commit-point || basepoint)*G
|
||||
|
||||
The remotekey is generated the same way, using the remote `refund base point` and the current `key-offset` from the remote node: this is given by `first-key-offset` (for the initial commitment transaction) and `next-key-offset` for successive transactions.
|
||||
The `localkey` uses the local node's `payment-basepoint`, `remotekey`
|
||||
uses the remote node's `payment-basepoint`, the `local-delayedkey`
|
||||
uses the local node's `delayed-payment-basepoint`, and the
|
||||
`remote-delayedkey` uses the remote node's
|
||||
`delayed-payment-basepoint`.
|
||||
|
||||
### revocationkey Derivation
|
||||
The correspoding private keys can be derived similarly if the basepoint
|
||||
secrets are known (ie. `localkey` and `local-delayedkey` only):
|
||||
|
||||
The local revocation key is derived from both the remote `HAKD basepoint` and a key derived from the local per-commit secret, called the “revocation-halfkey”.
|
||||
secretkey = basepoint-secret + SHA256(basepoint || commit-number)
|
||||
|
||||
The secret key for the `revocation-halfkey` is HMAC(`per-commit-secret`, “T”) [FIXME: more detail!]. The public key corresponding to this secret key is `revocation-halfkey`. Elliptic curve point addition of `revocation-halfkey` and `HAKD basepoint` gives the `revocationkey`.
|
||||
## `revocationkey` Derivation
|
||||
|
||||
Upon revocation, the per-commit secret is revealed to the remote node: this allows it to derive the secret key for `revocation-halfkey`, and it already knows the secret key corresponding to the `HAKD basepoint` so it can derive the secret key corresponding to `revocationkey`.
|
||||
The revocationkey is a blinded key: the remote node provides the base,
|
||||
and the local node provides the blinding factor which it later
|
||||
reveals, so the remote node can use the secret revocationkey for a
|
||||
penalty transaction.
|
||||
|
||||
The `per-commit-point` is generated using EC multiplication:
|
||||
|
||||
per-commit-point = per-commit-secret * G
|
||||
|
||||
And this is used to derive the revocation key from the remote node's
|
||||
`revocation-basepoint`:
|
||||
|
||||
revocationkey = revocation-basepoint * SHA256(revocation-basepoint || per-commit-point) + per-commit-point*SHA256(per-commit-point || revocation-basepoint)
|
||||
|
||||
This construction ensures that neither the node providing the
|
||||
basepoint nor the node providing the per-commit-point can know the
|
||||
private key without the other node's secret.
|
||||
|
||||
### Per-commitment Secret Requirements
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue