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

BOLT 2/3: watcher-compatible key derivation. #8

Merged
merged 1 commit into from
Nov 19, 2016
Merged
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
39 changes: 16 additions & 23 deletions 02-peer-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.
Expand All @@ -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.


Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -689,19 +682,19 @@ 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]

### Requirements


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.
Expand Down
75 changes: 57 additions & 18 deletions 03-transactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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:
Expand All @@ -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.
Expand All @@ -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

These keys are simply generated by addition from their base points:

pubkey = basepoint + SHA256(per-commit-point || basepoint)*G

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`.

The correspoding private keys can be derived similarly if the basepoint
secrets are known (ie. `localkey` and `local-delayedkey` only):

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`.
secretkey = basepoint-secret + SHA256(basepoint || commit-number)

The `key-offset` is generated using HMAC(`per-commit-secret`, “R”) [FIXME: more detail!].
## `revocationkey` Derivation

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

### revocationkey Derivation
The `per-commit-point` is generated using EC multiplication:

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”.
per-commit-point = per-commit-secret * G

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`.
And this is used to derive the revocation key from the remote node's
`revocation-basepoint`:

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`.
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

Expand Down