lnwire: update single funder workflow to use revocation keys

This commit updates the messages sent during a single funder workflow
to utilize revocation keys rather than revocation hashes. This now
matches the latest updates to the commitment transaction.

The changes to the workflow are as follows:
  * the response message now carries the responder’s revocation key
  * the complete message now carries the initiator’s revocation key

Once the initiator receives the response message, it can construct both
versions of the commitment transaction as it now knows the responder’s
commitment key. The initiator then sends their initial revocation key
over to the responder allowing it to construct the commitment
transactions and give the initiator a sig for their version.
This commit is contained in:
Olaoluwa Osuntokun 2016-06-30 11:49:58 -07:00
parent 582b83ada3
commit fd02c1c1aa
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
6 changed files with 92 additions and 81 deletions

@ -28,17 +28,25 @@ type SingleFundingComplete struct {
// CommitSignature is Alice's signature for Bob's version of the
// commitment transaction.
CommitSignature *btcec.Signature
// RevocationKey is the initial key to be used for the revocation
// clause within the self-output of the initiators's commitment
// transaction. Once an initial new state is created, the initiator
// will send a pre-image which will allow the initiator to sweep the
// initiator's funds if the violate the contract.
RevocationKey *btcec.PublicKey
}
// NewSingleFundingComplete creates, and returns a new empty
// SingleFundingResponse.
func NewSingleFundingComplete(chanID uint64, fundingPoint *wire.OutPoint,
commitSig *btcec.Signature) *SingleFundingComplete {
commitSig *btcec.Signature, revokeKey *btcec.PublicKey) *SingleFundingComplete {
return &SingleFundingComplete{
ChannelID: chanID,
FundingOutPoint: fundingPoint,
CommitSignature: commitSig,
RevocationKey: revokeKey,
}
}
@ -50,11 +58,13 @@ func NewSingleFundingComplete(chanID uint64, fundingPoint *wire.OutPoint,
func (s *SingleFundingComplete) Decode(r io.Reader, pver uint32) error {
// ChannelID (8)
// FundingOutPoint (36)
// Commitment Signature (73)
// CommitmentSignature (73)
// RevocationKey (33)
err := readElements(r,
&s.ChannelID,
&s.FundingOutPoint,
&s.CommitSignature)
&s.CommitSignature,
&s.RevocationKey)
if err != nil {
return err
}
@ -71,10 +81,12 @@ func (s *SingleFundingComplete) Encode(w io.Writer, pver uint32) error {
// ChannelID (8)
// FundingOutPoint (36)
// Commitment Signature (73)
// RevocationKey (33)
err := writeElements(w,
s.ChannelID,
s.FundingOutPoint,
s.CommitSignature)
s.CommitSignature,
s.RevocationKey)
if err != nil {
return err
}
@ -92,13 +104,12 @@ func (s *SingleFundingComplete) Command() uint32 {
// MaxPayloadLength returns the maximum allowed payload length for a
// SingleFundingComplete. This is calculated by summing the max length of all
// the fields within a SingleFundingResponse. To enforce a maximum
// DeliveryPkScript size, the size of a P2PKH public key script is used.
// Therefore, the final breakdown is: 8 + 36 + 73 = 117
// the fields within a SingleFundingResponse. Therefore, the final breakdown
// is: 8 + 36 + 73 = 150
//
// This is part of the lnwire.Message interface.
func (s *SingleFundingComplete) MaxPayloadLength(uint32) uint32 {
return 117
return 150
}
// Validate examines each populated field within the SingleFundingComplete for
@ -115,6 +126,8 @@ func (s *SingleFundingComplete) Validate() error {
return fmt.Errorf("commitment signature must be non-nil")
}
// TODO(roasbeef): fin validation
// We're good!
return nil
}
@ -123,9 +136,15 @@ func (s *SingleFundingComplete) Validate() error {
//
// This is part of the lnwire.Message interface.
func (s *SingleFundingComplete) String() string {
var rk []byte
if s.RevocationKey != nil {
rk = s.RevocationKey.SerializeCompressed()
}
return fmt.Sprintf("\n--- Begin SingleFundingComplete ---\n") +
fmt.Sprintf("ChannelID:\t\t\t%d\n", s.ChannelID) +
fmt.Sprintf("FundingOutPoint:\t\t\t%x\n", s.FundingOutPoint) +
fmt.Sprintf("CommitSignature\t\t\t\t%x\n", s.CommitSignature) +
fmt.Sprintf("RevocationKey\t\t\t\t%x\n", rk) +
fmt.Sprintf("--- End SingleFundingComplete ---\n")
}

@ -8,7 +8,7 @@ import (
func TestSingleFundingCompleteWire(t *testing.T) {
// First create a new SFC message.
sfc := NewSingleFundingComplete(22, outpoint1, commitSig1)
sfc := NewSingleFundingComplete(22, outpoint1, commitSig1, pubKey)
// Next encode the SFC message into an empty bytes buffer.
var b bytes.Buffer

@ -1,7 +1,6 @@
package lnwire
import (
"bytes"
"fmt"
"io"
@ -63,12 +62,6 @@ type SingleFundingRequest struct {
// an odd y-coordinate.
ChannelDerivationPoint *btcec.PublicKey
// RevocationHash is the initial revocation hash to be used for the
// initiator's commitment transaction to derive their revocation public
// key as: P + G*revocationHash, where P is the initiator's channel
// public key.
RevocationHash [20]byte
// DeliveryPkScript defines the public key script that the initiator
// would like to use to receive their balance in the case of a
// cooperative close. Only the following script templates are
@ -81,7 +74,7 @@ type SingleFundingRequest struct {
// NewSingleFundingRequest creates, and returns a new empty SingleFundingRequest.
func NewSingleFundingRequest(chanID uint64, chanType uint8, coinType uint64,
fee btcutil.Amount, amt btcutil.Amount, delay uint32, ck,
cdp *btcec.PublicKey, revocation [20]byte, deliveryScript PkScript) *SingleFundingRequest {
cdp *btcec.PublicKey, deliveryScript PkScript) *SingleFundingRequest {
return &SingleFundingRequest{
ChannelID: chanID,
@ -92,7 +85,6 @@ func NewSingleFundingRequest(chanID uint64, chanType uint8, coinType uint64,
CsvDelay: delay,
CommitmentKey: ck,
ChannelDerivationPoint: cdp,
RevocationHash: revocation,
DeliveryPkScript: deliveryScript,
}
}
@ -107,10 +99,10 @@ func (c *SingleFundingRequest) Decode(r io.Reader, pver uint32) error {
// ChannelType (1)
// CoinType (8)
// FeePerKb (8)
// FundingAmount (8)
// CsvDelay (4)
// Channel Derivation Point (32)
// Revocation Hash (20)
// PaymentAmount (8)
// Delay (4)
// Pubkey (33)
// Pubkey (33)
// DeliveryPkScript (final delivery)
err := readElements(r,
&c.ChannelID,
@ -121,7 +113,6 @@ func (c *SingleFundingRequest) Decode(r io.Reader, pver uint32) error {
&c.CsvDelay,
&c.CommitmentKey,
&c.ChannelDerivationPoint,
&c.RevocationHash,
&c.DeliveryPkScript)
if err != nil {
return err
@ -141,9 +132,9 @@ func (c *SingleFundingRequest) Encode(w io.Writer, pver uint32) error {
// CoinType (8)
// FeePerKb (8)
// PaymentAmount (8)
// LockTime (4)
// Revocation Hash (20)
// Pubkey (32)
// Delay (4)
// Pubkey (33)
// Pubkey (33)
// DeliveryPkScript (final delivery)
err := writeElements(w,
c.ChannelID,
@ -154,7 +145,6 @@ func (c *SingleFundingRequest) Encode(w io.Writer, pver uint32) error {
c.CsvDelay,
c.CommitmentKey,
c.ChannelDerivationPoint,
c.RevocationHash,
c.DeliveryPkScript)
if err != nil {
return err
@ -175,11 +165,11 @@ func (c *SingleFundingRequest) Command() uint32 {
// SingleFundingRequest. This is calculated by summing the max length of all
// the fields within a SingleFundingRequest. To enforce a maximum
// DeliveryPkScript size, the size of a P2PKH public key script is used.
// Therefore, the final breakdown is: 8 + 1 + 8 + 8 + 8 + 4 + 33 + 33 + 20 + 25 = 114.
// Therefore, the final breakdown is: 8 + 1 + 8 + 8 + 8 + 4 + 33 + 33 + 25 = 158.
//
// This is part of the lnwire.Message interface.
func (c *SingleFundingRequest) MaxPayloadLength(uint32) uint32 {
return 148
return 158
}
// Validate examines each populated field within the SingleFundingRequest for
@ -212,12 +202,6 @@ func (c *SingleFundingRequest) Validate() error {
//"y-coordinate")
//}
// The revocation hash MUST be non-zero.
var zeroHash [20]byte
if bytes.Equal(c.RevocationHash[:], zeroHash[:]) {
return fmt.Errorf("Initial revocation hash must be non-zero")
}
// The delivery pkScript must be amongst the supported script
// templates.
if !isValidPkScript(c.DeliveryPkScript) {
@ -249,7 +233,6 @@ func (c *SingleFundingRequest) String() string {
fmt.Sprintf("FundingAmount:\t\t\t%s\n", c.FundingAmount.String()) +
fmt.Sprintf("CsvDelay\t\t\t%d\n", c.CsvDelay) +
fmt.Sprintf("ChannelDerivationPoint\t\t\t\t%x\n", serializedPubkey) +
fmt.Sprintf("RevocationHash\t\t\t%x\n", c.RevocationHash) +
fmt.Sprintf("DeliveryPkScript\t\t%x\n", c.DeliveryPkScript) +
fmt.Sprintf("--- End SingleFundingRequest ---\n")
}

@ -8,10 +8,9 @@ import (
func TestSingleFundingRequestWire(t *testing.T) {
// First create a new SFR message.
var rev [20]byte
cdp := pubKey
delivery := PkScript(bytes.Repeat([]byte{0x02}, 25))
sfr := NewSingleFundingRequest(20, 21, 22, 23, 5, 5, cdp, cdp, rev, delivery)
sfr := NewSingleFundingRequest(20, 21, 22, 23, 5, 5, cdp, cdp, delivery)
// Next encode the SFR message into an empty bytes buffer.
var b bytes.Buffer

@ -1,7 +1,6 @@
package lnwire
import (
"bytes"
"fmt"
"io"
@ -17,29 +16,30 @@ type SingleFundingResponse struct {
// the initiated single funder workflow.
ChannelID uint64
// RevocationHash is the initial revocation hash to be used for the
// responders's commitment transaction to derive their revocation public
// key as: P + G*revocationHash, where P is the initiator's channel
// public key.
RevocationHash [20]byte
// ChannelDerivationPoint is an secp256k1 point which will be used to
// derive the public key the responder will use for the half of the
// 2-of-2 multi-sig. Using the channel derivation point (CDP), and the
// responder's identity public key (A), the channel public key is
// computed as: C = A + CDP. In order to be valid all CDP's MUST have
// an odd y-coordinate.
ChannelDerivationPoint *btcec.PublicKey
// CommitmentKey is key the responder to the funding workflow wishes to
// use within their versino of the commitment transaction for any
// delayed (CSV) or immediate outputs to them.
CommitmentKey *btcec.PublicKey
// RevocationKey is the initial key to be used for the revocation
// clause within the self-output of the responder's commitment
// transaction. Once an initial new state is created, the responder
// will send a pre-image which will allow the initiator to sweep the
// responder's funds if the violate the contract.
RevocationKey *btcec.PublicKey
// CsvDelay is the number of blocks to use for the relative time lock
// in the pay-to-self output of both commitment transactions.
CsvDelay uint32
// ChannelDerivationPoint is an secp256k1 point which will be used to
// derive the public key the responder will use for the half of the
// 2-of-2 multi-sig. Using the channel derivation point (CDP), and the
// initiators identity public key (A), the channel public key is
// computed as: C = A + CDP. In order to be valid all CDP's MUST have
// an odd y-coordinate.
ChannelDerivationPoint *btcec.PublicKey
// DeliveryPkScript defines the public key script that the initiator
// would like to use to receive their balance in the case of a
// cooperative close. Only the following script templates are
@ -49,20 +49,23 @@ type SingleFundingResponse struct {
// NewSingleFundingResponse creates, and returns a new empty
// SingleFundingResponse.
func NewSingleFundingResponse(chanID uint64, revocation [20]byte,
ck, cdp *btcec.PublicKey, delay uint32,
deliveryScript PkScript) *SingleFundingResponse {
func NewSingleFundingResponse(chanID uint64, rk, ck, cdp *btcec.PublicKey,
delay uint32, deliveryScript PkScript) *SingleFundingResponse {
return &SingleFundingResponse{
ChannelID: chanID,
RevocationHash: revocation,
CommitmentKey: ck,
CsvDelay: delay,
ChannelDerivationPoint: cdp,
CommitmentKey: ck,
RevocationKey: rk,
CsvDelay: delay,
DeliveryPkScript: deliveryScript,
}
}
// A compile time check to ensure SingleFundingResponse implements the
// lnwire.Message interface.
var _ Message = (*SingleFundingResponse)(nil)
// Decode deserializes the serialized SingleFundingResponse stored in the passed
// io.Reader into the target SingleFundingResponse using the deserialization
// rules defined by the passed protocol version.
@ -70,14 +73,16 @@ func NewSingleFundingResponse(chanID uint64, revocation [20]byte,
// This is part of the lnwire.Message interface.
func (c *SingleFundingResponse) Decode(r io.Reader, pver uint32) error {
// ChannelID (8)
// Revocation Hash (20)
// Pubkey (32)
// ChannelDerivationPoint (33)
// CommitmentKey (33)
// RevocationKey (33)
// CsvDelay (4)
// DeliveryPkScript (final delivery)
err := readElements(r,
&c.ChannelID,
&c.RevocationHash,
&c.CommitmentKey,
&c.ChannelDerivationPoint,
&c.CommitmentKey,
&c.RevocationKey,
&c.CsvDelay,
&c.DeliveryPkScript)
if err != nil {
@ -94,14 +99,16 @@ func (c *SingleFundingResponse) Decode(r io.Reader, pver uint32) error {
// This is part of the lnwire.Message interface.
func (c *SingleFundingResponse) Encode(w io.Writer, pver uint32) error {
// ChannelID (8)
// Revocation Hash (20)
// Channel Derivation Point (32)
// ChannelDerivationPoint (33)
// CommitmentKey (33)
// RevocationKey (33)
// CsvDelay (4)
// DeliveryPkScript (final delivery)
err := writeElements(w,
c.ChannelID,
c.RevocationHash,
c.CommitmentKey,
c.ChannelDerivationPoint,
c.CommitmentKey,
c.RevocationKey,
c.CsvDelay,
c.DeliveryPkScript)
if err != nil {
@ -123,11 +130,11 @@ func (c *SingleFundingResponse) Command() uint32 {
// SingleFundingResponse. This is calculated by summing the max length of all
// the fields within a SingleFundingResponse. To enforce a maximum
// DeliveryPkScript size, the size of a P2PKH public key script is used.
// Therefore, the final breakdown is: 8 + 20 + 33 + 33 + 4 + 25 = 123
// Therefore, the final breakdown is: 8 + (33 * 3) + 8 + 25
//
// This is part of the lnwire.Message interface.
func (c *SingleFundingResponse) MaxPayloadLength(uint32) uint32 {
return 123
return 140
}
// Validate examines each populated field within the SingleFundingResponse for
@ -136,11 +143,6 @@ func (c *SingleFundingResponse) MaxPayloadLength(uint32) uint32 {
//
// This is part of the lnwire.Message interface.
func (c *SingleFundingResponse) Validate() error {
var zeroHash [20]byte
if bytes.Equal(zeroHash[:], c.RevocationHash[:]) {
return fmt.Errorf("revocation has must be non-zero")
}
// The channel derivation point must be non-nil, and have an odd
// y-coordinate.
if c.ChannelDerivationPoint == nil {
@ -166,15 +168,25 @@ func (c *SingleFundingResponse) Validate() error {
//
// This is part of the lnwire.Message interface.
func (c *SingleFundingResponse) String() string {
var serializedPubkey []byte
if &c.ChannelDerivationPoint != nil && c.ChannelDerivationPoint.X != nil {
serializedPubkey = c.ChannelDerivationPoint.SerializeCompressed()
var cdp []byte
var ck []byte
var rk []byte
if &c.ChannelDerivationPoint != nil {
cdp = c.ChannelDerivationPoint.SerializeCompressed()
}
if &c.CommitmentKey != nil {
ck = c.CommitmentKey.SerializeCompressed()
}
if &c.RevocationKey != nil {
rk = c.RevocationKey.SerializeCompressed()
}
return fmt.Sprintf("\n--- Begin SingleFundingResponse ---\n") +
fmt.Sprintf("ChannelID:\t\t\t%d\n", c.ChannelID) +
fmt.Sprintf("RevocationHash\t\t\t%x\n", c.RevocationHash) +
fmt.Sprintf("ChannelDerivationPoint\t\t\t\t%x\n", serializedPubkey) +
fmt.Sprintf("ChannelDerivationPoint\t\t\t\t%x\n", cdp) +
fmt.Sprintf("CommitmentKey\t\t\t\t%x\n", ck) +
fmt.Sprintf("RevocationKey\t\t\t\t%x\n", rk) +
fmt.Sprintf("CsvDelay\t\t%d\n", c.CsvDelay) +
fmt.Sprintf("DeliveryPkScript\t\t%x\n", c.DeliveryPkScript) +
fmt.Sprintf("--- End SingleFundingResponse ---\n")
}

@ -8,10 +8,8 @@ import (
func TestSingleFundingResponseWire(t *testing.T) {
// First create a new SFR message.
var rev [20]byte
cdp := pubKey
delivery := PkScript(bytes.Repeat([]byte{0x02}, 25))
sfr := NewSingleFundingResponse(22, rev, cdp, cdp, 5, delivery)
sfr := NewSingleFundingResponse(22, pubKey, pubKey, pubKey, 5, delivery)
// Next encode the SFR message into an empty bytes buffer.
var b bytes.Buffer