diff --git a/lnwire/single_funding_response.go b/lnwire/single_funding_response.go index aee25c625..9abdc1043 100644 --- a/lnwire/single_funding_response.go +++ b/lnwire/single_funding_response.go @@ -1,36 +1,58 @@ package lnwire import ( + "bytes" "fmt" - "github.com/roasbeef/btcd/btcec" - "github.com/roasbeef/btcd/wire" - "github.com/roasbeef/btcutil" "io" + + "github.com/roasbeef/btcd/btcec" ) -type FundingResponse struct { - ChannelID uint64 - RevocationHash [20]byte - Pubkey *btcec.PublicKey - CommitSig *btcec.Signature // Requester's Commitment - DeliveryPkScript PkScript // *MUST* be either P2PKH or P2SH - ChangePkScript PkScript // *MUST* be either P2PKH or P2SH +// SingleFundingResponse is the message Bob sends to Alice after she initiates +// the single funder channel workflow via a SingleFundingRequest message. Once +// Alice receives Bob's reponse, then she has all the items neccessary to +// construct the funding transaction, and both commitment transactions. +type SingleFundingResponse struct { + // ChannelID serves to uniquely identify the future channel created by + // 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 + // 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 + // supported: P2PKH, P2WKH, P2SH, and P2WSH. + DeliveryPkScript PkScript } -func (c *FundingResponse) Decode(r io.Reader, pver uint32) error { +// 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. +// +// 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) - // CommitSig (73) // DeliveryPkScript (final delivery) - // ChangePkScript (change for extra from inputs) err := readElements(r, &c.ChannelID, &c.RevocationHash, - &c.Pubkey, - &c.CommitSig, - &c.DeliveryPkScript, - &c.ChangePkScript) + &c.ChannelDerivationPoint, + &c.DeliveryPkScript) if err != nil { return err } @@ -38,27 +60,27 @@ func (c *FundingResponse) Decode(r io.Reader, pver uint32) error { return nil } -// Creates a new FundingResponse -func NewFundingResponse() *FundingResponse { - return &FundingResponse{} +// NewSingleFundingResponse creates, and returns a new empty +// SingleFundingResponse. +func NewSingleFundingResponse() *SingleFundingResponse { + return &SingleFundingResponse{} } -// Serializes the item from the FundingResponse struct -// Writes the data to w -func (c *FundingResponse) Encode(w io.Writer, pver uint32) error { +// Encode serializes the target SingleFundingResponse into the passed io.Writer +// implementation. Serialization will observe the rules defined by the passed +// protocol version. +// +// This is part of the lnwire.Message interface. +func (c *SingleFundingResponse) Encode(w io.Writer, pver uint32) error { // ChannelID (8) // Revocation Hash (20) - // Pubkey (32) - // CommitSig (73) + // Channel Derivation Point (32) // DeliveryPkScript (final delivery) - // ChangePkScript (change for extra from inputs) err := writeElements(w, c.ChannelID, c.RevocationHash, - c.Pubkey, - c.CommitSig, - c.DeliveryPkScript, - c.ChangePkScript) + c.ChannelDerivationPoint, + c.DeliveryPkScript) if err != nil { return err } @@ -66,69 +88,70 @@ func (c *FundingResponse) Encode(w io.Writer, pver uint32) error { return nil } -func (c *FundingResponse) Command() uint32 { - return CmdFundingResponse +// Command returns the uint32 code which uniquely identifies this message as a +// SingleFundingRequest on the wire. +// +// This is part of the lnwire.Message interface. +func (c *SingleFundingResponse) Command() uint32 { + return CmdSingleFundingResponse } -func (c *FundingResponse) MaxPayloadLength(uint32) uint32 { - return 186 +// MaxPayloadLength returns the maximum allowed payload length for a +// 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 +// +// This is part of the lnwire.Message interface. +func (c *SingleFundingResponse) MaxPayloadLength(uint32) uint32 { + return 85 } -// Makes sure the struct data is valid (e.g. no negatives or invalid pkscripts) -func (c *FundingResponse) Validate() error { - var err error - - // No negative values - if c.ResponderFundingAmount < 0 { - return fmt.Errorf("ResponderFundingAmount cannot be negative") +// Validate examines each populated field within the SingleFundingResponse for +// field sanity. For example, all fields MUST NOT be negative, and all pkScripts +// must belong to the allowed set of public key scripts. +// +// 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") } - if c.ResponderReserveAmount < 0 { - return fmt.Errorf("ResponderReserveAmount cannot be negative") + // The channel derivation point must be non-nil, and have an odd + // y-coordinate. + 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.MinFeePerKb < 0 { - return fmt.Errorf("MinFeePerKb cannot be negative") - } - - // Validation of what makes sense... - if c.ResponderFundingAmount < c.ResponderReserveAmount { - return fmt.Errorf("Reserve must be below Funding Amount") - } - - // Make sure there's not more than 127 inputs - if len(c.Inputs) > 127 { - return fmt.Errorf("Too many inputs") - } - - // Delivery PkScript is either P2SH or P2PKH - err = ValidatePkScript(c.DeliveryPkScript) - if err != nil { - return err - } - - // Change PkScript is either P2SH or P2PKH - err = ValidatePkScript(c.ChangePkScript) - if err != nil { - return err + // The delivery pkScript must be amongst the supported script + // templates. + if !isValidPkScript(c.DeliveryPkScript) { + return fmt.Errorf("Valid delivery public key scripts MUST be: " + + "P2PKH, P2WKH, P2SH, or P2WSH.") } // We're good! return nil } -func (c *FundingResponse) String() string { +// String returns the string representation of the SingleFundingResponse. +// +// This is part of the lnwire.Message interface. +func (c *SingleFundingResponse) String() string { var serializedPubkey []byte - if &c.Pubkey != nil && c.Pubkey.X != nil { - serializedPubkey = c.Pubkey.SerializeCompressed() + if &c.ChannelDerivationPoint != nil && c.ChannelDerivationPoint.X != nil { + serializedPubkey = c.ChannelDerivationPoint.SerializeCompressed() } - return fmt.Sprintf("\n--- Begin FundingResponse ---\n") + + 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("Pubkey\t\t\t\t%x\n", serializedPubkey) + - fmt.Sprintf("CommitSig\t\t\t%x\n", c.CommitSig.Serialize()) + + fmt.Sprintf("ChannelDerivationPoint\t\t\t\t%x\n", serializedPubkey) + fmt.Sprintf("DeliveryPkScript\t\t%x\n", c.DeliveryPkScript) + - fmt.Sprintf("ChangePkScript\t\t%x\n", c.ChangePkScript) + - fmt.Sprintf("--- End FundingResponse ---\n") + fmt.Sprintf("--- End SingleFundingResponse ---\n") }