lnwire: channels are now identified by outpoint

This commit modifies most of the wire messages to uniquely identify any
*active* channels by their funding output. This allows the wire
protocol to support funding transactions which open several channels in
parallel.

Any pending channels created by partial completion of the funding
workflow are to be identified by a uint64 initialized by both sides as
follows: the initiator of the connection starts from 0, while the
listening node starts from (1 << 63). These pending channel identifiers
are expected to be monotonically increasing with each new funding
workflow between two nodes. This identifier is volatile w.r.t to each
connection initiation.
This commit is contained in:
Olaoluwa Osuntokun 2016-06-20 21:55:07 -07:00
parent 7b7d572984
commit 6c7880ef76
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
25 changed files with 236 additions and 202 deletions

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/wire"
"io"
)
@ -19,8 +20,8 @@ import (
// arrive at an identical closure transaction as they know the order of the
// inputs/outputs.
type CloseComplete struct {
// ChannelID serves to identify which channel is to be closed.
ChannelID uint64
// ChannelPoint serves to identify which channel is to be closed.
ChannelPoint *wire.OutPoint
// ResponderCloseSig is the signature of the responder for the
// transaction which closes the previously active channel.
@ -42,10 +43,10 @@ var _ Message = (*CloseComplete)(nil)
//
// This is part of the lnwire.Message interface.
func (c *CloseComplete) Decode(r io.Reader, pver uint32) error {
// ChannelID (8)
// ChannelPoint (8)
// ResponderCloseSig (73)
err := readElements(r,
&c.ChannelID,
&c.ChannelPoint,
&c.ResponderCloseSig)
if err != nil {
return err
@ -59,10 +60,10 @@ func (c *CloseComplete) Decode(r io.Reader, pver uint32) error {
//
// This is part of the lnwire.Message interface.
func (c *CloseComplete) Encode(w io.Writer, pver uint32) error {
// ChannelID (8)
// ChannelPoint (8)
// ResponderCloseSig (73)
err := writeElements(w,
c.ChannelID,
c.ChannelPoint,
c.ResponderCloseSig)
if err != nil {
return err
@ -84,8 +85,8 @@ func (c *CloseComplete) Command() uint32 {
//
// This is part of the lnwire.Message interface.
func (c *CloseComplete) MaxPayloadLength(uint32) uint32 {
// 8 + 73 + 32
return 113
// 141 + 73 + 32
return 141
}
// Validate performs any necessary sanity checks to ensure all fields present
@ -107,7 +108,7 @@ func (c *CloseComplete) String() string {
}
return fmt.Sprintf("\n--- Begin CloseComplete ---\n") +
fmt.Sprintf("ReservationID:\t\t%d\n", c.ChannelID) +
fmt.Sprintf("ReservationID:\t\t%d\n", c.ChannelPoint) +
fmt.Sprintf("ResponderCloseSig:\t%x\n", serializedSig) +
fmt.Sprintf("--- End CloseComplete ---\n")
}

@ -8,7 +8,7 @@ import (
func TestCloseCompleteEncodeDecode(t *testing.T) {
cc := &CloseComplete{
ChannelID: uint64(12345678),
ChannelPoint: outpoint1,
ResponderCloseSig: commitSig,
}

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcutil"
"io"
@ -20,8 +21,8 @@ import (
// both sides are able to arrive at an identical closure transaction as they
// know the order of the inputs/outputs.
type CloseRequest struct {
// ChannelID serves to identify which channel is to be closed.
ChannelID uint64
// ChannelPoint serves to identify which channel is to be closed.
ChannelPoint *wire.OutPoint
// RequesterCloseSig is the signature of the requester for the fully
// assembled closing transaction.
@ -30,20 +31,34 @@ type CloseRequest struct {
// Fee is the required fee-per-KB the closing transaction must have.
// It is recommended that a "sufficient" fee be paid in order to achieve
// timely channel closure.
// TODO(roasbeef): if initiator always pays fees, then no longer needed.
Fee btcutil.Amount
}
// NewCloseRequest creates a new CloseRequest.
func NewCloseRequest(cp *wire.OutPoint, sig *btcec.Signature) *CloseRequest {
// TODO(roasbeef): update once fees aren't hardcoded
return &CloseRequest{
ChannelPoint: cp,
RequesterCloseSig: sig,
}
}
// A compile time check to ensure CloseRequest implements the lnwire.Message
// interface.
var _ Message = (*CloseRequest)(nil)
// Decode deserializes a serialized CloseRequest stored in the passed io.Reader
// observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (c *CloseRequest) Decode(r io.Reader, pver uint32) error {
// ChannelID (8)
// ChannelPoint (8)
// RequesterCloseSig (73)
// First byte length then sig
// Fee (8)
err := readElements(r,
&c.ChannelID,
&c.ChannelPoint,
&c.RequesterCloseSig,
&c.Fee)
if err != nil {
@ -53,15 +68,6 @@ func (c *CloseRequest) Decode(r io.Reader, pver uint32) error {
return nil
}
// NewCloseRequest creates a new CloseRequest.
func NewCloseRequest() *CloseRequest {
return &CloseRequest{}
}
// A compile time check to ensure CloseRequest implements the lnwire.Message
// interface.
var _ Message = (*CloseRequest)(nil)
// Encode serializes the target CloseRequest into the passed io.Writer observing
// the protocol version specified.
//
@ -71,7 +77,7 @@ func (c *CloseRequest) Encode(w io.Writer, pver uint32) error {
// RequesterCloseSig
// Fee
err := writeElements(w,
c.ChannelID,
c.ChannelPoint,
c.RequesterCloseSig,
c.Fee)
if err != nil {
@ -94,8 +100,8 @@ func (c *CloseRequest) Command() uint32 {
//
// This is part of the lnwire.Message interface.
func (c *CloseRequest) MaxPayloadLength(pver uint32) uint32 {
// 8 + 73 + 8
return 89
// 36 + 73 + 8
return 117
}
// Validate performs any necessary sanity checks to ensure all fields present
@ -122,7 +128,7 @@ func (c *CloseRequest) String() string {
}
return fmt.Sprintf("\n--- Begin CloseRequest ---\n") +
fmt.Sprintf("ChannelID:\t\t%d\n", c.ChannelID) +
fmt.Sprintf("ChannelPoint:\t\t%d\n", c.ChannelPoint) +
fmt.Sprintf("CloseSig\t\t%x\n", serializedSig) +
fmt.Sprintf("Fee:\t\t\t%d\n", c.Fee) +
fmt.Sprintf("--- End CloseRequest ---\n")

@ -1,35 +1,35 @@
package lnwire
import (
"bytes"
"reflect"
"testing"
"github.com/roasbeef/btcutil"
)
var (
closeRequest = &CloseRequest{
ChannelID: uint64(12345678),
RequesterCloseSig: commitSig,
Fee: btcutil.Amount(12345),
}
closeRequestSerializedString = "0000000000bc614e4630440220333835e58e958f5e92b4ff4e6fa2470dac88094c97506b4d6d1f4e23e52cb481022057483ac18d6b9c9c14f0c626694c9ccf8b27b3dbbedfdf6b6c9a9fa9f427a1df0000000000003039"
closeRequestSerializedMessage = "0709110b0000012c000000570000000000bc614e4630440220333835e58e958f5e92b4ff4e6fa2470dac88094c97506b4d6d1f4e23e52cb481022057483ac18d6b9c9c14f0c626694c9ccf8b27b3dbbedfdf6b6c9a9fa9f427a1df0000000000003039"
)
func TestCloseRequestEncodeDecode(t *testing.T) {
// All of these types being passed are of the message interface type
// Test serialization, runs: message.Encode(b, 0)
// Returns bytes
// Compares the expected serialized string from the original
s := SerializeTest(t, closeRequest, closeRequestSerializedString, filename)
cr := &CloseRequest{
ChannelPoint: outpoint1,
RequesterCloseSig: commitSig,
Fee: btcutil.Amount(10000),
}
// Test deserialization, runs: message.Decode(s, 0)
// Makes sure the deserialized struct is the same as the original
newMessage := NewCloseRequest()
DeserializeTest(t, s, newMessage, closeRequest)
// Next encode the CR message into an empty bytes buffer.
var b bytes.Buffer
if err := cr.Encode(&b, 0); err != nil {
t.Fatalf("unable to encode CloseRequest: %v", err)
}
// Test message using Message interface
// Serializes into buf: WriteMessage(buf, message, uint32(1), wire.TestNet3)
// Deserializes into msg: _, msg, _ , err := ReadMessage(buf, uint32(1), wire.TestNet3)
MessageSerializeDeserializeTest(t, closeRequest, closeRequestSerializedMessage)
// Deserialize the encoded CR message into a new empty struct.
cr2 := &CloseRequest{}
if err := cr2.Decode(&b, 0); err != nil {
t.Fatalf("unable to decode CloseRequest: %v", err)
}
// Assert equality of the two instances.
if !reflect.DeepEqual(cr, cr2) {
t.Fatalf("encode/decode error messages don't match %#v vs %#v",
cr, cr2)
}
}

@ -3,12 +3,14 @@ package lnwire
import (
"fmt"
"io"
"github.com/roasbeef/btcd/wire"
)
// CommitRevocation is sent by either side once a CommitSignature message has
// been received, and validated. This message serves to revoke the prior
// commitment transaction, which was the most up to date version until a
// CommitSignature message referencing the specified ChannelID was received.
// CommitSignature message referencing the specified ChannelPoint was received.
// Additionally, this message also piggyback's the next revocation hash that
// Alice should use when constructing the Bob's version of the next commitment
// transaction (which would be done before sending a CommitSignature message).
@ -16,9 +18,9 @@ import (
// modifying Bob's commitment transaction without first asking for a revocation
// hash initially.
type CommitRevocation struct {
// ChannelID uniquely identifies to which currently active channel this
// ChannelPoint uniquely identifies to which currently active channel this
// CommitRevocation applies to.
ChannelID uint64
ChannelPoint *wire.OutPoint
// Revocation is the pre-image to the revocation hash of the now prior
// commitment transaction.
@ -44,11 +46,11 @@ var _ Message = (*CommitRevocation)(nil)
//
// This is part of the lnwire.Message interface.
func (c *CommitRevocation) Decode(r io.Reader, pver uint32) error {
// ChannelID (8)
// ChannelPoint (8)
// NextRevocationHash (20)
// Revocation (20)
err := readElements(r,
&c.ChannelID,
&c.ChannelPoint,
&c.NextRevocationHash,
&c.Revocation,
)
@ -65,7 +67,7 @@ func (c *CommitRevocation) Decode(r io.Reader, pver uint32) error {
// This is part of the lnwire.Message interface.
func (c *CommitRevocation) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.ChannelPoint,
c.NextRevocationHash,
c.Revocation,
)
@ -89,8 +91,8 @@ func (c *CommitRevocation) Command() uint32 {
//
// This is part of the lnwire.Message interface.
func (c *CommitRevocation) MaxPayloadLength(uint32) uint32 {
// 8 + 20 + 20
return 48
// 36 + 20 + 20
return 76
}
// Validate performs any necessary sanity checks to ensure all fields present
@ -107,7 +109,7 @@ func (c *CommitRevocation) Validate() error {
// This is part of the lnwire.Message interface.
func (c *CommitRevocation) String() string {
return fmt.Sprintf("\n--- Begin CommitRevocation ---\n") +
fmt.Sprintf("ChannelID:\t%d\n", c.ChannelID) +
fmt.Sprintf("ChannelPoint:\t%d\n", c.ChannelPoint) +
fmt.Sprintf("NextRevocationHash:\t%x\n", c.NextRevocationHash) +
fmt.Sprintf("Revocation:\t%x\n", c.Revocation) +
fmt.Sprintf("--- End CommitRevocation ---\n")

@ -11,7 +11,7 @@ func TestCommitRevocationEncodeDecode(t *testing.T) {
copy(nextHop[:], nextHopBytes)
cr := &CommitRevocation{
ChannelID: uint64(12345678),
ChannelPoint: outpoint1,
Revocation: revocationHash,
NextRevocationHash: nextHop,
}

@ -5,6 +5,7 @@ import (
"io"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcutil"
)
@ -16,9 +17,9 @@ import (
// messages in order to batch add several HTLC's with a single signature
// covering all implicitly accepted HTLC's.
type CommitSignature struct {
// ChannelID uniquely identifies to which currently active channel this
// ChannelPoint uniquely identifies to which currently active channel this
// CommitSignature applies to.
ChannelID uint64
ChannelPoint *wire.OutPoint
// Fee represents the total miner's fee that was used when constructing
// the new commitment transaction.
@ -47,11 +48,11 @@ var _ Message = (*CommitSignature)(nil)
//
// This is part of the lnwire.Message interface.
func (c *CommitSignature) Decode(r io.Reader, pver uint32) error {
// ChannelID(8)
// ChannelPoint(8)
// Fee(8)
// RequesterCommitSig(73max+2)
err := readElements(r,
&c.ChannelID,
&c.ChannelPoint,
&c.Fee,
&c.CommitSig,
)
@ -69,7 +70,7 @@ func (c *CommitSignature) Decode(r io.Reader, pver uint32) error {
func (c *CommitSignature) Encode(w io.Writer, pver uint32) error {
// TODO(roasbeef): make similar modificaiton to all other encode/decode
// messags
return writeElements(w, c.ChannelID, c.Fee, c.CommitSig)
return writeElements(w, c.ChannelPoint, c.Fee, c.CommitSig)
}
// Command returns the integer uniquely identifying this message type on the
@ -85,8 +86,8 @@ func (c *CommitSignature) Command() uint32 {
//
// This is part of the lnwire.Message interface.
func (c *CommitSignature) MaxPayloadLength(uint32) uint32 {
// 8 + 8 + 73
return 89
// 36 + 8 + 73
return 117
}
// Validate performs any necessary sanity checks to ensure all fields present
@ -114,7 +115,7 @@ func (c *CommitSignature) String() string {
}
return fmt.Sprintf("\n--- Begin CommitSignature ---\n") +
fmt.Sprintf("ChannelID:\t%d\n", c.ChannelID) +
fmt.Sprintf("ChannelPoint:\t%d\n", c.ChannelPoint) +
fmt.Sprintf("Fee:\t\t\t%s\n", c.Fee.String()) +
fmt.Sprintf("CommitSig:\t\t%x\n", serializedSig) +
fmt.Sprintf("--- End CommitSignature ---\n")

@ -12,9 +12,9 @@ func TestCommitSignatureEncodeDecode(t *testing.T) {
copy(revocationHash[:], revocationHashBytes)
commitSignature := &CommitSignature{
ChannelID: uint64(12345678),
Fee: btcutil.Amount(10000),
CommitSig: commitSig,
ChannelPoint: outpoint1,
Fee: btcutil.Amount(10000),
CommitSig: commitSig,
}
// Next encode the CS message into an empty bytes buffer.

@ -3,16 +3,19 @@ package lnwire
import (
"fmt"
"io"
"github.com/roasbeef/btcd/wire"
)
// ErrorGeneric represents a generic error bound to an exact channel. The
// message format is purposefully general in order to allow expressino of a wide
// array of possible errors. Each ErrorGeneric message is directed at a particular
// open channel referenced by ChannelID.
// open channel referenced by ChannelPoint.
type ErrorGeneric struct {
// ChannelID references the active channel in which the error occured
// within.
ChannelID uint64
// ChannelPoint references the active channel in which the error occured
// within. A ChannelPoint of zeroHash:0 denotes this error applies to
// the entire established connection.
ChannelPoint *wire.OutPoint
// ErrorID quickly defines the nature of the error according to error
// type.
@ -38,10 +41,10 @@ var _ Message = (*ErrorGeneric)(nil)
//
// This is part of the lnwire.Message interface.
func (c *ErrorGeneric) Decode(r io.Reader, pver uint32) error {
// ChannelID(8)
// ChannelPoint(8)
// Problem
err := readElements(r,
&c.ChannelID,
&c.ChannelPoint,
&c.ErrorID,
&c.Problem,
)
@ -58,7 +61,7 @@ func (c *ErrorGeneric) Decode(r io.Reader, pver uint32) error {
// This is part of the lnwire.Message interface.
func (c *ErrorGeneric) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.ChannelPoint,
c.ErrorID,
c.Problem,
)
@ -104,7 +107,7 @@ func (c *ErrorGeneric) Validate() error {
// This is part of the lnwire.Message interface.
func (c *ErrorGeneric) String() string {
return fmt.Sprintf("\n--- Begin ErrorGeneric ---\n") +
fmt.Sprintf("ChannelID:\t%d\n", c.ChannelID) +
fmt.Sprintf("ChannelPoint:\t%d\n", c.ChannelPoint) +
fmt.Sprintf("ErrorID:\t%d\n", c.ErrorID) +
fmt.Sprintf("Problem:\t%s\n", c.Problem) +
fmt.Sprintf("--- End ErrorGeneric ---\n")

@ -8,9 +8,9 @@ import (
func TestErrorGenericEncodeDecode(t *testing.T) {
eg := &ErrorGeneric{
ChannelID: uint64(12345678),
ErrorID: 99,
Problem: "Hello world!",
ChannelPoint: outpoint1,
ErrorID: 99,
Problem: "Hello world!",
}
// Next encode the EG message into an empty bytes buffer.

@ -3,31 +3,19 @@ package lnwire
import (
"fmt"
"io"
"github.com/roasbeef/btcd/wire"
)
// ChannelPoint represents a unique state update within currently active
// channel. A channel update across all open chnnels can be uniquely identified
// by a two-tuple: (fundingTXID, HTLCKey). All explicit updates to an open
// channel will reference a ChannelPoint to apply the update to.
type ChannelPoint struct {
// ChannelID references the particular active channel to which this
// HTLCAddReject message is binded to.
ChannelID uint64
// HTLCKey is used to identify which HTLC previously attempted to be
// added via an HTLCAddRequest message is being declined.
HTLCKey HTLCKey
}
// HTLCAddReject is sent by Bob when he wishes to reject a particular HTLC that
// Alice attempted to add via an HTLCAddRequest message. The rejected HTLC is
// referenced by its unique HTLCKey ID. An HTLCAddReject message is bound to a
// single active channel, referenced by a unique ChannelID. Additionally, the
// single active channel, referenced by a unique ChannelPoint. Additionally, the
// HTLCKey of the rejected HTLC is present
type HTLCAddReject struct {
// ChannelID references the particular active channel to which this
// ChannelPoint references the particular active channel to which this
// HTLCAddReject message is binded to.
ChannelID uint64
ChannelPoint *wire.OutPoint
// HTLCKey is used to identify which HTLC previously attempted to be
// added via an HTLCAddRequest message is being declined.
@ -39,10 +27,10 @@ type HTLCAddReject struct {
//
// This is part of the lnwire.Message interface.
func (c *HTLCAddReject) Decode(r io.Reader, pver uint32) error {
// ChannelID (8)
// ChannelPoint (8)
// HTLCKey (8)
err := readElements(r,
&c.ChannelID,
&c.ChannelPoint,
&c.HTLCKey,
)
if err != nil {
@ -67,7 +55,7 @@ var _ Message = (*HTLCAddReject)(nil)
// This is part of the lnwire.Message interface.
func (c *HTLCAddReject) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.ChannelPoint,
c.HTLCKey,
)
@ -91,8 +79,8 @@ func (c *HTLCAddReject) Command() uint32 {
//
// This is part of the lnwire.Message interface.
func (c *HTLCAddReject) MaxPayloadLength(uint32) uint32 {
// 8 + 8
return 16
// 36 + 8
return 44
}
// Validate performs any necessary sanity checks to ensure all fields present
@ -109,7 +97,7 @@ func (c *HTLCAddReject) Validate() error {
// This is part of the lnwire.Message interface.
func (c *HTLCAddReject) String() string {
return fmt.Sprintf("\n--- Begin HTLCAddReject ---\n") +
fmt.Sprintf("ChannelID:\t\t%d\n", c.ChannelID) +
fmt.Sprintf("ChannelPoint:\t\t%d\n", c.ChannelPoint) +
fmt.Sprintf("HTLCKey:\t\t%d\n", c.HTLCKey) +
fmt.Sprintf("--- End HTLCAddReject ---\n")
}

@ -1,32 +1,33 @@
package lnwire
import (
"bytes"
"reflect"
"testing"
)
var (
htlcAddReject = &HTLCAddReject{
ChannelID: uint64(12345678),
HTLCKey: HTLCKey(12345),
}
htlcAddRejectSerializedString = "0000000000bc614e0000000000003039"
htlcAddRejectSerializedMessage = "0709110b000003fc000000100000000000bc614e0000000000003039"
)
func TestHTLCAddRejectEncodeDecode(t *testing.T) {
// All of these types being passed are of the message interface type
// Test serialization, runs: message.Encode(b, 0)
// Returns bytes
// Compares the expected serialized string from the original
s := SerializeTest(t, htlcAddReject, htlcAddRejectSerializedString, filename)
// First create a new HTLCAR message.
rejectReq := &HTLCAddReject{
ChannelPoint: outpoint1,
HTLCKey: 22,
}
// Test deserialization, runs: message.Decode(s, 0)
// Makes sure the deserialized struct is the same as the original
newMessage := NewHTLCAddReject()
DeserializeTest(t, s, newMessage, htlcAddReject)
// Next encode the HTLCAR message into an empty bytes buffer.
var b bytes.Buffer
if err := rejectReq.Encode(&b, 0); err != nil {
t.Fatalf("unable to encode HTLCSettleRequest: %v", err)
}
// Test message using Message interface
// Serializes into buf: WriteMessage(buf, message, uint32(1), wire.TestNet3)
// Deserializes into msg: _, msg, _ , err := ReadMessage(buf, uint32(1), wire.TestNet3)
MessageSerializeDeserializeTest(t, htlcAddReject, htlcAddRejectSerializedMessage)
// Deserialize the encoded HTLCAR message into a new empty struct.
rejectReq2 := &HTLCAddReject{}
if err := rejectReq2.Decode(&b, 0); err != nil {
t.Fatalf("unable to decode HTLCAddReject: %v", err)
}
// Assert equality of the two instances.
if !reflect.DeepEqual(rejectReq, rejectReq2) {
t.Fatalf("encode/decode error messages don't match %#v vs %#v",
rejectReq, rejectReq2)
}
}

@ -3,6 +3,8 @@ package lnwire
import (
"fmt"
"io"
"github.com/roasbeef/btcd/wire"
)
// HTLCAddRequest is the message sent by Alice to Bob when she wishes to add an
@ -13,9 +15,9 @@ import (
// A subsequent CommitSignature message will move the pending HTLC to the newly
// created commitment transaction, marking them as "staged".
type HTLCAddRequest struct {
// ChannelID is the particular active channel that this HTLCAddRequest
// ChannelPoint is the particular active channel that this HTLCAddRequest
// is binded to.
ChannelID uint64
ChannelPoint *wire.OutPoint
// Expiry is the number of blocks after which this HTLC should expire.
// It is the receiver's duty to ensure that the outgoing HTLC has a
@ -72,14 +74,14 @@ var _ Message = (*HTLCAddRequest)(nil)
//
// This is part of the lnwire.Message interface.
func (c *HTLCAddRequest) Decode(r io.Reader, pver uint32) error {
// ChannelID(8)
// ChannelPoint(8)
// Expiry(4)
// Amount(4)
// ContractType(1)
// RedemptionHashes (numOfHashes * 20 + numOfHashes)
// OnionBlog
err := readElements(r,
&c.ChannelID,
&c.ChannelPoint,
&c.Expiry,
&c.Amount,
&c.ContractType,
@ -99,7 +101,7 @@ func (c *HTLCAddRequest) Decode(r io.Reader, pver uint32) error {
// This is part of the lnwire.Message interface.
func (c *HTLCAddRequest) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.ChannelPoint,
c.Expiry,
c.Amount,
c.ContractType,
@ -156,7 +158,7 @@ func (c *HTLCAddRequest) String() string {
}
return fmt.Sprintf("\n--- Begin HTLCAddRequest ---\n") +
fmt.Sprintf("ChannelID:\t%d\n", c.ChannelID) +
fmt.Sprintf("ChannelPoint:\t%d\n", c.ChannelPoint) +
fmt.Sprintf("Expiry:\t\t%d\n", c.Expiry) +
fmt.Sprintf("Amount\t\t%d\n", c.Amount) +
fmt.Sprintf("ContractType:\t%d (%b)\n", c.ContractType, c.ContractType) +

@ -12,7 +12,7 @@ func TestHTLCAddRequestEncodeDecode(t *testing.T) {
// First create a new HTLCAR message.
addReq := &HTLCAddRequest{
ChannelID: uint64(12345678),
ChannelPoint: outpoint1,
Expiry: uint32(144),
Amount: CreditsAmount(123456000),
ContractType: uint8(17),

@ -3,19 +3,21 @@ package lnwire
import (
"fmt"
"io"
"github.com/roasbeef/btcd/wire"
)
// HTLCSettleRequest is sent by Alice to Bob when she wishes to settle a
// particular HTLC referenced by its HTLCKey within a specific active channel
// referenced by ChannelID. The message allows multiple hash preimages to be
// referenced by ChannelPoint. The message allows multiple hash preimages to be
// presented in order to support N-of-M HTLC contracts. A subsequent
// CommitSignature message will be sent by Alice to "lock-in" the removal of the
// specified HTLC, possible containing a batch signature covering several settled
// HTLC's.
type HTLCSettleRequest struct {
// ChannelID references an active channel which holds the HTLC to be
// ChannelPoint references an active channel which holds the HTLC to be
// settled.
ChannelID uint64
ChannelPoint *wire.OutPoint
// HTLCKey denotes the exact HTLC stage within the receiving node's
// commitment transaction to be removed.
@ -28,11 +30,11 @@ type HTLCSettleRequest struct {
}
// NewHTLCSettleRequest returns a new empty HTLCSettleRequest.
func NewHTLCSettleRequest(chanID uint64, key HTLCKey,
func NewHTLCSettleRequest(chanPoint *wire.OutPoint, key HTLCKey,
redemptionProofs [][20]byte) *HTLCSettleRequest {
return &HTLCSettleRequest{
ChannelID: chanID,
ChannelPoint: chanPoint,
HTLCKey: key,
RedemptionProofs: redemptionProofs,
}
@ -47,11 +49,11 @@ var _ Message = (*HTLCSettleRequest)(nil)
//
// This is part of the lnwire.Message interface.
func (c *HTLCSettleRequest) Decode(r io.Reader, pver uint32) error {
// ChannelID(8)
// ChannelPoint(8)
// HTLCKey(8)
// RedemptionProofs(N*20)
err := readElements(r,
&c.ChannelID,
&c.ChannelPoint,
&c.HTLCKey,
&c.RedemptionProofs,
)
@ -68,7 +70,7 @@ func (c *HTLCSettleRequest) Decode(r io.Reader, pver uint32) error {
// This is part of the lnwire.Message interface.
func (c *HTLCSettleRequest) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.ChannelPoint,
c.HTLCKey,
c.RedemptionProofs,
)
@ -92,8 +94,8 @@ func (c *HTLCSettleRequest) Command() uint32 {
//
// This is part of the lnwire.Message interface.
func (c *HTLCSettleRequest) MaxPayloadLength(uint32) uint32 {
// 8 + 8 + (21 * 15)
return 331
// 36 + 8 + (21 * 15)
return 359
}
// Validate performs any necessary sanity checks to ensure all fields present
@ -116,7 +118,7 @@ func (c *HTLCSettleRequest) String() string {
}
return fmt.Sprintf("\n--- Begin HTLCSettleRequest ---\n") +
fmt.Sprintf("ChannelID:\t%d\n", c.ChannelID) +
fmt.Sprintf("ChannelPoint:\t%d\n", c.ChannelPoint) +
fmt.Sprintf("HTLCKey:\t%d\n", c.HTLCKey) +
fmt.Sprintf("RedemptionHashes:") +
redemptionProofs +

@ -11,7 +11,7 @@ func TestHTLCSettleRequestEncodeDecode(t *testing.T) {
copy(redemptionProofs[0][:], bytes.Repeat([]byte{0x09}, 20))
// First create a new HTLCSR message.
settleReq := NewHTLCSettleRequest(22, HTLCKey(23), redemptionProofs)
settleReq := NewHTLCSettleRequest(outpoint1, HTLCKey(23), redemptionProofs)
// Next encode the HTLCSR message into an empty bytes buffer.
var b bytes.Buffer

@ -3,6 +3,8 @@ package lnwire
import (
"fmt"
"io"
"github.com/roasbeef/btcd/wire"
)
// HTLCTimeoutRequest is sent by Alice to Bob in order to timeout a previously
@ -10,9 +12,9 @@ import (
// from the next commitment transaction, with the HTLCTimeoutRequest propgated
// backwards in the route to fully clear the HTLC.
type HTLCTimeoutRequest struct {
// ChannelID is the particular active channel that this HTLCTimeoutRequest
// ChannelPoint is the particular active channel that this HTLCTimeoutRequest
// is binded to.
ChannelID uint64
ChannelPoint *wire.OutPoint
// HTLCKey references which HTLC on the remote node's commitment
// transaction has timed out.
@ -24,10 +26,10 @@ type HTLCTimeoutRequest struct {
//
// This is part of the lnwire.Message interface.
func (c *HTLCTimeoutRequest) Decode(r io.Reader, pver uint32) error {
// ChannelID(8)
// ChannelPoint(8)
// HTLCKey(8)
err := readElements(r,
&c.ChannelID,
&c.ChannelPoint,
&c.HTLCKey,
)
if err != nil {
@ -52,7 +54,7 @@ var _ Message = (*HTLCTimeoutRequest)(nil)
// This is part of the lnwire.Message interface.
func (c *HTLCTimeoutRequest) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.ChannelPoint,
c.HTLCKey,
)
if err != nil {
@ -75,8 +77,8 @@ func (c *HTLCTimeoutRequest) Command() uint32 {
//
// This is part of the lnwire.Message interface.
func (c *HTLCTimeoutRequest) MaxPayloadLength(uint32) uint32 {
// 16
return 16
// 36 + 8
return 44
}
// Validate performs any necessary sanity checks to ensure all fields present
@ -92,7 +94,7 @@ func (c *HTLCTimeoutRequest) Validate() error {
// This is part of the lnwire.Message interface.
func (c *HTLCTimeoutRequest) String() string {
return fmt.Sprintf("\n--- Begin HTLCTimeoutRequest ---\n") +
fmt.Sprintf("ChannelID:\t%d\n", c.ChannelID) +
fmt.Sprintf("ChannelPoint:\t%d\n", c.ChannelPoint) +
fmt.Sprintf("HTLCKey:\t%d\n", c.HTLCKey) +
fmt.Sprintf("--- End HTLCTimeoutRequest ---\n")
}

@ -1,32 +1,33 @@
package lnwire
import (
"bytes"
"reflect"
"testing"
)
var (
htlcTimeoutRequest = &HTLCTimeoutRequest{
ChannelID: uint64(12345678),
HTLCKey: HTLCKey(12345),
}
htlcTimeoutRequestSerializedString = "0000000000bc614e0000000000003039"
htlcTimeoutRequestSerializedMessage = "0709110b00000514000000100000000000bc614e0000000000003039"
)
func TestHTLCTimeoutRequestEncodeDecode(t *testing.T) {
// All of these types being passed are of the message interface type
// Test serialization, runs: message.Encode(b, 0)
// Returns bytes
// Compares the expected serialized string from the original
s := SerializeTest(t, htlcTimeoutRequest, htlcTimeoutRequestSerializedString, filename)
// First create a new HTLCTR message.
timeoutReq := &HTLCTimeoutRequest{
ChannelPoint: outpoint1,
HTLCKey: 22,
}
// Test deserialization, runs: message.Decode(s, 0)
// Makes sure the deserialized struct is the same as the original
newMessage := NewHTLCTimeoutRequest()
DeserializeTest(t, s, newMessage, htlcTimeoutRequest)
// Next encode the HTLCTR message into an empty bytes buffer.
var b bytes.Buffer
if err := timeoutReq.Encode(&b, 0); err != nil {
t.Fatalf("unable to encode HTLCTimeoutRequest: %v", err)
}
// Test message using Message interface
// Serializes into buf: WriteMessage(buf, message, uint32(1), wire.TestNet3)
// Deserializes into msg: _, msg, _ , err := ReadMessage(buf, uint32(1), wire.TestNet3)
MessageSerializeDeserializeTest(t, htlcTimeoutRequest, htlcTimeoutRequestSerializedMessage)
// Deserialize the encoded HTLCTR message into a new empty struct.
timeoutReq2 := &HTLCTimeoutRequest{}
if err := timeoutReq2.Decode(&b, 0); err != nil {
t.Fatalf("unable to decode HTLCTimeoutRequest: %v", err)
}
// Assert equality of the two instances.
if !reflect.DeepEqual(timeoutReq, timeoutReq2) {
t.Fatalf("encode/decode error messages don't match %#v vs %#v",
timeoutReq, timeoutReq2)
}
}

@ -256,7 +256,7 @@ func writeElement(w io.Writer, element interface{}) error {
if _, err := w.Write(idx[:]); err != nil {
return err
}
case wire.OutPoint:
case *wire.OutPoint:
// TODO(roasbeef): consolidate with above
// First write out the previous txid.
var h [32]byte
@ -497,7 +497,7 @@ func readElement(r io.Reader, element interface{}) error {
}
(*e).PreviousOutPoint.Index = binary.BigEndian.Uint32(idxBytes[:])
return nil
case *wire.OutPoint:
case **wire.OutPoint:
// TODO(roasbeef): consolidate with above
var h [32]byte
if _, err = io.ReadFull(r, h[:]); err != nil {
@ -507,15 +507,15 @@ func readElement(r io.Reader, element interface{}) error {
if err != nil {
return err
}
(*e).Hash = *hash
// Index
var idxBytes [4]byte
_, err = io.ReadFull(r, idxBytes[:])
if err != nil {
return err
}
(*e).Index = binary.BigEndian.Uint32(idxBytes[:])
index := binary.BigEndian.Uint32(idxBytes[:])
*e = wire.NewOutPoint(hash, index)
default:
return fmt.Errorf("Unknown type in readElement: %T", e)
}

@ -17,12 +17,13 @@ import (
type SingleFundingComplete struct {
// ChannelID serves to uniquely identify the future channel created by
// the initiated single funder workflow.
// TODO(roasbeef): change all to PendingChannelID, document schema
ChannelID uint64
// FundingOutPoint is the outpoint (txid:index) of the funding
// transaction. With this value, Bob will be able to generate a
// signature for Alice's version of the commitment transaction.
FundingOutPoint wire.OutPoint
FundingOutPoint *wire.OutPoint
// CommitSignature is Alice's signature for Bob's version of the
// commitment transaction.
@ -31,7 +32,7 @@ type SingleFundingComplete struct {
// NewSingleFundingComplete creates, and returns a new empty
// SingleFundingResponse.
func NewSingleFundingComplete(chanID uint64, fundingPoint wire.OutPoint,
func NewSingleFundingComplete(chanID uint64, fundingPoint *wire.OutPoint,
commitSig *btcec.Signature) *SingleFundingComplete {
return &SingleFundingComplete{

@ -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)
// Next encode the SFC message into an empty bytes buffer.
var b bytes.Buffer

@ -48,9 +48,11 @@ type SingleFundingRequest struct {
// CsvDelay is the number of blocks to use for the relative time lock
// in the pay-to-self output of both commitment transactions.
// TODO(roasbeef): bool for seconds or blocks?
CsvDelay uint32
// CommitmentKey...
CommitmentKey *btcec.PublicKey
// ChannelDerivationPoint is an secp256k1 point which will be used to
// derive the public key the initiator will use for the half of the
// 2-of-2 multi-sig. Using the channel derivation point (CDP), and the
@ -76,15 +78,17 @@ type SingleFundingRequest struct {
// NewSingleFundingRequest creates, and returns a new empty SingleFundingRequest.
func NewSingleFundingRequest(chanID uint64, chanType uint8, coinType uint64,
fee btcutil.Amount, delay uint32, cdp *btcec.PublicKey, revocation [20]byte,
deliveryScript PkScript) *SingleFundingRequest {
fee btcutil.Amount, amt btcutil.Amount, delay uint32, ck,
cdp *btcec.PublicKey, revocation [20]byte, deliveryScript PkScript) *SingleFundingRequest {
return &SingleFundingRequest{
ChannelID: chanID,
ChannelType: chanType,
CoinType: coinType,
FeePerKb: fee,
FundingAmount: amt,
CsvDelay: delay,
CommitmentKey: ck,
ChannelDerivationPoint: cdp,
RevocationHash: revocation,
DeliveryPkScript: deliveryScript,
@ -113,6 +117,7 @@ func (c *SingleFundingRequest) Decode(r io.Reader, pver uint32) error {
&c.FeePerKb,
&c.FundingAmount,
&c.CsvDelay,
&c.CommitmentKey,
&c.ChannelDerivationPoint,
&c.RevocationHash,
&c.DeliveryPkScript)
@ -145,6 +150,7 @@ func (c *SingleFundingRequest) Encode(w io.Writer, pver uint32) error {
c.FeePerKb,
c.FundingAmount,
c.CsvDelay,
c.CommitmentKey,
c.ChannelDerivationPoint,
c.RevocationHash,
c.DeliveryPkScript)
@ -167,11 +173,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 + 32 + 20 + 25 = 114.
// Therefore, the final breakdown is: 8 + 1 + 8 + 8 + 8 + 4 + 33 + 33 + 20 + 25 = 114.
//
// This is part of the lnwire.Message interface.
func (c *SingleFundingRequest) MaxPayloadLength(uint32) uint32 {
return 114
return 148
}
// Validate examines each populated field within the SingleFundingRequest for
@ -199,10 +205,10 @@ func (c *SingleFundingRequest) Validate() error {
if c.ChannelDerivationPoint == nil {
return fmt.Errorf("The channel derivation point must be non-nil")
}
if c.ChannelDerivationPoint.Y.Bit(0) != 1 {
return fmt.Errorf("The channel derivation point must have an odd " +
"y-coordinate")
}
//if c.ChannelDerivationPoint.Y.Bit(0) != 1 {
//return fmt.Errorf("The channel derivation point must have an odd " +
//"y-coordinate")
//}
// The revocation hash MUST be non-zero.
var zeroHash [20]byte
@ -231,6 +237,8 @@ func (c *SingleFundingRequest) String() string {
serializedPubkey = c.ChannelDerivationPoint.SerializeCompressed()
}
// TODO(roasbeef): remove string methods?
return fmt.Sprintf("\n--- Begin SingleFundingRequest ---\n") +
fmt.Sprintf("ChannelID:\t\t\t%d\n", c.ChannelID) +
fmt.Sprintf("ChannelType:\t\t\t%x\n", c.ChannelType) +

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

@ -23,6 +23,15 @@ type SingleFundingResponse struct {
// public key.
RevocationHash [20]byte
// 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
// 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
@ -41,11 +50,14 @@ type SingleFundingResponse struct {
// NewSingleFundingResponse creates, and returns a new empty
// SingleFundingResponse.
func NewSingleFundingResponse(chanID uint64, revocation [20]byte,
cdp *btcec.PublicKey, deliveryScript PkScript) *SingleFundingResponse {
ck, cdp *btcec.PublicKey, delay uint32,
deliveryScript PkScript) *SingleFundingResponse {
return &SingleFundingResponse{
ChannelID: chanID,
RevocationHash: revocation,
CommitmentKey: ck,
CsvDelay: delay,
ChannelDerivationPoint: cdp,
DeliveryPkScript: deliveryScript,
}
@ -64,7 +76,9 @@ func (c *SingleFundingResponse) Decode(r io.Reader, pver uint32) error {
err := readElements(r,
&c.ChannelID,
&c.RevocationHash,
&c.CommitmentKey,
&c.ChannelDerivationPoint,
&c.CsvDelay,
&c.DeliveryPkScript)
if err != nil {
return err
@ -86,7 +100,9 @@ func (c *SingleFundingResponse) Encode(w io.Writer, pver uint32) error {
err := writeElements(w,
c.ChannelID,
c.RevocationHash,
c.CommitmentKey,
c.ChannelDerivationPoint,
c.CsvDelay,
c.DeliveryPkScript)
if err != nil {
return err
@ -107,11 +123,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 + 32 + 25 = 85
// Therefore, the final breakdown is: 8 + 20 + 33 + 33 + 4 + 25 = 123
//
// This is part of the lnwire.Message interface.
func (c *SingleFundingResponse) MaxPayloadLength(uint32) uint32 {
return 85
return 123
}
// Validate examines each populated field within the SingleFundingResponse for
@ -130,10 +146,10 @@ func (c *SingleFundingResponse) Validate() error {
if c.ChannelDerivationPoint == nil {
return fmt.Errorf("The channel derivation point must be non-nil")
}
if c.ChannelDerivationPoint.Y.Bit(0) != 1 {
return fmt.Errorf("The channel derivation point must have an odd " +
"y-coordinate")
}
//if c.ChannelDerivationPoint.Y.Bit(0) != 1 {
// return fmt.Errorf("The channel derivation point must have an odd " +
// "y-coordinate")
//}
// The delivery pkScript must be amongst the supported script
// templates.

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