Merge pull request #10 from nkohen/four-key-derivation

Rework and simplify key derivation
This commit is contained in:
Nadav Kohen
2020-02-27 16:55:05 -07:00
committed by GitHub
3 changed files with 84 additions and 115 deletions

View File

@@ -1,32 +0,0 @@
# DLC Key Derivation
Key pairs used in Discreet Log Contracts (DLC) must be generated in a deterministic fashion so that both parties can generate and sign the same transactions. This is accomplished using [BIP 32 keys](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) as is the norm in Bitcoin protocols.
Every DLC will have its own independent extended key root. Let `k_par` be any extended private key whose derivation path ends with a hardened derivation, such as an account extended public key [here](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#path-levels). We recommend ending with a hardened deriation to avoid any considerations of leaking information about keys not in this DLCs derivation subtree. Furthermore, it is recommended to specifically use the following derivation path:
m / coin_type' / event_id'
Where an apostrophe in the path indicates that BIP32 hardened derivation is used. And where `m` is some master extended key, `coin_type` is as defined [here](https://github.com/satoshilabs/slips/blob/master/slip-0044.md), and `event_id` is unique to the DLC at hand.
All keys in this scheme will be derived in the form `key = k_par / phase / path`, where `phase = 0` is for the funding transaction, `phase = 1` is for the contract execution/refund transactions and `phase = 2` is for closing transactions. Note that `path` can be empty.
### Funding Key
The funding key pair is used to create and later sign the [Funding Transaction](Transactions.md#funding-transaction)'s 2-of-2 multisignature output.
Define `k_funding` to be `k_par / 0`.
### Refund Key
The refund key is used to create and sign the refund transaction's relevant P2WPKH output.
Define `k_refund` to be `k_par / 1 / 0`.
### Contract Execution Transaction (CET) Key
Every possible DLC outcome must be known in advance and must have a CET constructed for it. Given some deterministic indexing of these events, we can deterministically derive CET keys. This indexing must start at `1` (as opposed to `0`) since we reserve the `0` index for the [refund/timeout outcome](#refund-key)
There are three different keys each party has for each event: The key in one's own [ToLocalOutput](Transactions.md#CetOutputs), the key in one's counter-party's ToLocalOutput, and the key in one's counter-party's ToRemoteOutput. In order, let these three keys be indexed with `0, 1 and 2` (this will be referenced to as the `key_index`).
Define `k_cet = k_par / 1 / event_index / key_index`.
### Closing Key
Closing keys are used as outputs in [Mutual](Transactions.md#mutual-closing-transaction), [Unilateral](Transactions.md#closing-transaction-unilateral) and [Penalty](Transactions.md#ClosingPenalty) closing transactions. Since Unilateral and Penalty transactions are only constructed by one party and require no signatures from their counter-party, there is no strict requirement that these keys be deterministically derived. However, there are many instances in which it is nice to do so anyway, such as for testing purposes (TODO: Link to test vectors). Let `key_index` be `0`, `1`, and `2` for Mutual, Unilateral, and Penalty closing transactions respectively.
Define `k_closing` to be `k_par / 2 / key_index`.

View File

@@ -28,16 +28,16 @@ The following figure gives a brief overview of the protocol:
The initiating party starts the protocol by sending an `offer` message to the other party.
This message includes the following information:
1. Contract information (an array of triples defined below)
1. Contract information (a map defined below)
1. Oracle information (optional)
1. A's fund amount (satoshi)
2. A's [extended public key](KeyDerivation.md)
3. A's inputs
4. A's change address
5. Estimatesmartfee (satoshi/vbyte)
6. CET CSV delay
7. CET locktime (corresponding to the contract maturity date)
8. Refund locktime
1. A's public keys
1. A's addresses
1. A's funding inputs
1. Estimatesmartfee (satoshi/vbyte)
1. CET CSV delay
1. CET locktime (corresponding to the contract maturity date)
1. Refund locktime
#### Contract information
Contract information consists of a total value (sum of both outputs) and a map to be used to create CETs.
@@ -56,15 +56,26 @@ If both parties already have this information, transmission is unnecessary.
#### A's fund amount (satoshi)
How much A inputs into the contract.
#### A's extended public key
The extended public key to be used to [derive](KeyDerivation.md) public keys for the Fund transaction, refund transaction and CETs.
#### A's public keys
The following public keys:
1. Funding Public Key
- A's public key used in the multi-signature [funding output](Transactions.md#FundingOutputs)
2. CET ToLocal Public Key
- A's public key used in computing the [ToLocal](Transactions.md#CETOutputs) keys in CETs
#### A's addresses
The following addresses:
1. Funding Change Address
- The address to use to send the change if the sum of A's inputs is greater than the fund amount plus the fees.
2. Final Address
- The address to which funds will be sent in [unilateral contract execution](Transactions.md#CETOutputs), [refund](Transactions.md#refund-transaction) and [mutual closing](Transactions.md#mutual-closing-transaction) transactions.
#### A's inputs
The set of UTXOs to be used as input to the fund transaction.
#### A's change address
The address to use to send the change if the sum of A's inputs is greater than the fund amount plus the fees.
#### Estimatesmartfee (satoshi/vbyte)
The fee rate to be used when computing fees for the different transactions.
@@ -84,24 +95,35 @@ It should be set at a later date than the maturity date of the contract.
### Accept
After receiving the `offer` message from A, and after [validating](#offer-validation) the provided information, B creates and sends back an `accept` message containing:
1. B's fund amount (satoshi)
2. B's [extended public key](KeyDerivation.md)
3. B's inputs
4. B's change output
5. CET signatures
6. Refund signature
1. B's public keys
1. B's addresses
1. B's inputs
1. CET signatures
1. Refund signature
#### B's fund amount (satoshi)
How much B inputs into the contract.
#### B's extended public key
The extended public key to be used to [derive](KeyDerivation.md) public keys for the Fund transaction, refund transaction and CETs.
#### B's public keys
The following public keys:
1. Funding Public Key
- B's public key used in the multi-signature [funding output](Transactions.md#FundingOutputs)
2. CET ToLocal Public Key
- B's public key used in computing the [ToLocal](Transactions.md#CETOutputs) keys in CETs
#### B's addresses
The following addresses:
1. Funding Change Address
- The address to use to send the change if the sum of A's inputs is greater than the fund amount plus the fees.
2. Final Address
- The address to which funds will be sent in [unilateral contract execution](Transactions.md#CETOutputs), [refund](Transactions.md#refund-transaction) and [mutual closing](Transactions.md#mutual-closing-transaction) transactions.
#### B's inputs
The set of UTXOs to be used as input to the fund transaction.
#### B's change address
The address to use to send the change if the sum of A's inputs is greater than the fund amount plus the fees.
#### CET signatures
A set of signatures from B, one for each CET input.
@@ -185,3 +207,4 @@ Takatoshi Nakagawa
Ichiro Kuwahara
Thibaut Le Guilly
Nadav Kohen

View File

@@ -1,6 +1,8 @@
# DLC Transactions
It is recommended that all keys be generated/derived as specified [here](KeyDerivation.md#dlc-key-derivation).
## A Note on Key Derivation
There is no strict constraint on how the two keys (Funding and ToLocal) and one address (Final Address) used in a DLC are generated. We do note that absent external considerations, it does seem reasonable to use [BIP 44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) with three sequential address indices. We think this will usually be the best option for implementing key derivation because it is compatible with [normal wallet account discovery](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#account-discovery).
## Funding Transaction
### <a name="FundingKnownValues">Known Values</a>
@@ -39,21 +41,24 @@ Where
OP_2 <Local Funding Public Key> <Remote Funding Public Key> OP_2 OP_CHECKMULTISIG
- Each `Change ScriptPubKey`'s value is at most that of its respective `Sum(Funding Inputs) - Total Collateral - Computed Fees - (Computed CET Fee + Computed ToLocal Closing Fee)/2` with `Computed Fees` being proportional to each party's total input weight and `Computed CET Fee` being the estimated fee for a [Contract Execution Transaction](#contract-execution-transaction) and `Computed ToLocal Closing Fee` being the estimated fee for a [Unilateral Closing Transaction](#ClosingUnilateral)
## Contract Execution Transaction
### <a name="CETKnownValues">Known Values</a>
* Oracle Signature Point: `ECPublicKey`
* Local CET Public Key: `ECPublicKey`
* Local Funding Public Key: `ECPublicKey`
* Local CET ToLocal Public Key: `ECPublicKey`
* Local Payout: `CurrencyUnit`
* Remote CET Public Key: `ECPublicKey`
* Remote Paytout: `CurrencyUnit`
* Remote CET ToLocal Public Key: `ECPublicKey`
* Remote Final Address: `BitcoinAddress`
* Remote Payout: `CurrencyUnit`
* nLockTime: `UInt32`
* Timeout: `UInt32`
* DLC Funding Output: `ScriptPubKey`
* Fee Rate: `FeeUnit`
Where
- `Oracle Signature Point` is the 33-byte public key associated with this CET's outcome
- `Local Funding Public Key` is the local key from the [funding transaction](#funding-transaction)
- Both `CET Public Key`s are 33-byte compressed public keys
- `Local Paytout + Remote Payout = (DLC Funding Output).value`
- `nLockTime` is set to the contract maturity time
@@ -66,40 +71,35 @@ Where
### <a name="CETOutputs">Outputs</a>
* P2WSH(ToLocalOutput)
* ToRemoteOutput
Where
- `P2WSH(ToLocalOutput).value = Local Payout + Computed ToLocal Closing Fee`
- `ToRemoteOutput.value = Remote Payout`
- `ToLocalOutput`'s script is:
OP_IF
<Oracle Signature Point + Local CET Public Key>
<Oracle Signature Point + Local Funding Public Key + SHA256(Local CET ToLocal Public Key)*G>
OP_ELSE
<Timeout> OP_CHECKSEQUENCEVERIFY OP_DROP
<Remote CET Public Key>
<Remote CET ToLocal Public Key>
OP_ENDIF
OP_CHECKSIG
- Note that The addition in the if case is elliptic curve point addition
- `ToRemoteOutput`'s script is:
OP_0 <Hash160(Remote CET Public Key)>
Which is `P2WPKH(Remote CET Public Key)`
- `ToRemoteOutput`'s script corresponds to `Remote CET Final Address`
## Refund Transaction
### <a name="RefundKnownValues">Known Values</a>
* Local Refund Public Key: `ECPublicKey`
* Local Final Address: `BitcoinAddress`
* Total Local Collateral: `CurrencyUnit`
* Remote Refund Public Key: `ECPublicKey`
* Remote Final Address: `BitcoinAddress`
* Total Remote Collateral: `CurrencyUnit`
* Timeout: `UInt32`
* DLC Funding Output: `ScriptPubKey`
* Fee Rate: `FeeUnit`
Where
- Unlike CETs in a DLC, there is only one Refund Transaction that both parties share, similar to how there is only one [Funding Transaction](#funding-transaction)
- Both `Refund Public Key`s are 33-byte compressed public keys
- `Total Local Collateral + Total Remote Collateral = (DLC Funding Output).value`
- `Timeout` is a CLTV locktime set well after the contract maturity time
- `DLC Funding Output` is of the form [specified above](#FundingOutputs)
@@ -115,31 +115,22 @@ Where
- `ToLocalOutput`'s value is `Total Local Collateral + RefundFeeDelta/2`
- `ToRemoteOutput`'s value is `Total Remote Collateral + RefundFeeDelta/2`
- `RefundFeeDelta = Computed CET Fee + Computed ToLocal Closing Fee - Computed Refund Tx Fee` (note that the Refund Transaction is smaller than any CET)
- `ToLocalOutput`'s script is:
- `ToLocalOutput`'s script is that of `Local Final Address`
OP_0 <Hash160(Local Refund Public Key)>
Which is `P2WPKH(Local Refund Public Key)`
- `ToRemoteOutput`'s script is:
OP_0 <Hash160(Remote Refund Public Key)>
Which is `P2WPKH(Remote Refund Public Key)`
- `ToRemoteOutput`'s script is that of `Remote Final Address`
## Mutual Closing Transaction
### <a name="MutualClosingKnownValues">Known Values</a>
* Local Mutual Closing Public Key: `ECPublicKey`
* Local Final Address: `BitcoinAddress`
* Local Payout: `CurrencyUnit`
* Remote Mutual Closing Public Key: `ECPublicKey`
* Remote Final Address: `BitcoinAddress`
* Remote Payout: `CurrencyUnit`
* nLockTime: `UInt32`
* DLC Funding Output: `ScriptPubKey`
* Fee Rate: `FeeUnit`
Where
- After the contract maturity time, Mutual Closing Transaction is created in cooperation for fee reduction and improvement in privacy
- Both `Mutual Closing Public Key`s are 33-byte compressed public keys
- `Local Payout = (Contract Execution Transaction Local Payout).value`
- `Remote Payout = (Contract Execution Transaction Remote Payout).value`
- `nLockTime` is in the past (rather than just using 0)
@@ -152,26 +143,18 @@ Where
### <a name="MutualClosingOutputs">Outputs</a>
* ToLocalOutput
* ToRemoteOutput
Where
- `ToLocalOutput's value is Local Payout + MutualClosingFeeDelta/2`
- `ToRemoteOutput's value is Remote Payout + MutualClosingFeeDelta/2`
- `MutualClosingFeeDelta = Computed CET Fee + Computed ToLocal Closing Fee - Computed MutualClosing Tx Fee (note that the Mutual Closing Transaction is smaller than any CET)`
- `ToLocalOutput`'s script is:
OP_0 <Hash160(Local Mutual Closing Public Key)>
Which is `P2WPKH(Local Mutual Closing Public Key)`
- `ToRemoteOutput`'s script is:
OP_0 <Hash160(Remote Mutual Closing Public Key)>
Which is `P2WPKH(Remote Mutual Closing Public Key)`
- `ToLocalOutput`'s value is `Local Payout + MutualClosingFeeDelta/2`
- `ToRemoteOutput`'s value is `Remote Payout + MutualClosingFeeDelta/2`
- `MutualClosingFeeDelta = Computed CET Fee + Computed ToLocal Closing Fee - Computed MutualClosing Tx Fee` (note that the Mutual Closing Transaction is smaller than any CET)
- `ToLocalOutput`'s script is that of `Local Final Address`
- `ToRemoteOutput`'s script is that of `Remote Final Address`
## <a name="ClosingUnilateral">Closing Transaction (Unilateral)</a>
### <a name="ClosingKnownValues">Known Values</a>
* Local Unilateral Public Key: `ECPublicKey`
* Local Final Address: `BitcoinAddress`
* nLockTime: `UInt32`
* Local Payout: `CurrencyUnit`
* ToLocalOutput: `ScriptPubKey`
@@ -186,19 +169,17 @@ Where
### <a name="ClosingInputs">Inputs</a>
* Input Spending(P2WSH(ToLocalOutput))
### <a name="ClosingOutputs">Outputs</a>
* P2WPKH(Local Unilateral Public Key)
Where
- `P2WPKH(Local Unilateral Public Key)`'s value is `Local Payout`
* One output corresponding to `Local Final Address` with value `Local Payout`
## <a name="ClosingPenalty">Closing Transaction (Penalty)</a>
### <a name="ClosingKnownValues">Known Values</a>
* Local Penalty Public Key: `ECPublicKey`
* Local Address: `BitcoinAddress`
* nLockTime: `UInt32`
* Remote's ToLocalOutput: `ScriptPubKey`
* Fee Rate: `FeeUnit`
Where
- `Local Address` is any unused local address
- `Remote's ToLocalOutput` is of the form [specified above](#CETOutputs)
- `nLockTime` is in the past (rather than just using 0)
- for privacy purposes and also to prevent [fee snipping](https://github.com/zkSNACKs/WalletWasabi/issues/2500)
@@ -207,7 +188,4 @@ Where
### <a name="ClosingInputs">Inputs</a>
* Input Spending(P2WSH(Remote's ToLocalOutput))
### <a name="ClosingOutputs">Outputs</a>
* P2WPKH(Local Justice Public Key)
Where
- `P2WPKH(Local Penalty Public Key)`'s value is `P2WSH(Remote's ToLocalOutput).value - fee`
* One output corresponding to `LocalAddress` with value `P2WSH(Remote's ToLocalOutput).value - fee`