mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
Messages for funding flow.
This is the most different due to segwit (the rest of the messages are simple). I still need to simplify/refactor the tests, they're "messy".
This commit is contained in:
parent
a5f0d3e56e
commit
a93b6dcee4
@ -30,7 +30,7 @@ timeout, the sender will be refunded. Sender and receiver can agree to
|
||||
authorize payment in most cases where there is cooperation, escrow is only
|
||||
contacted if there is non-cooperation.
|
||||
|
||||
Supported in the wire protocol for the unit8 (two 4-bit N-of-M):
|
||||
Supported in the wire protocol for the uint8 (two 4-bit N-of-M):
|
||||
17 (00010001): 1-of-1
|
||||
34 (00100010): 2-of-2
|
||||
35 (00100011): 2-of-3 [with Recipient being 1 of the two N parties]
|
||||
|
41
lnwire/FLOW.md
Normal file
41
lnwire/FLOW.md
Normal file
@ -0,0 +1,41 @@
|
||||
Funding (segwit+CSV)
|
||||
====================
|
||||
|
||||
This is two-party funder for a single Funding Transaction (more efficient and
|
||||
makes the channel creation atomic, but doesn't work for
|
||||
CSV-no-malleability-fix).
|
||||
|
||||
|
||||
Funding Request
|
||||
---------------
|
||||
Someone wants to open a channel. The requester provides any inputs and relevant
|
||||
information on how much they want to fund and the parameters, these paramters
|
||||
are a proposal.
|
||||
|
||||
|
||||
Funding Response
|
||||
----------------
|
||||
If the responder accepts the request, they also provide any inputs, and returns
|
||||
with parameters as well. These parameters are now considered "Committed" and the
|
||||
negotation has finished. If the requester doesn't agree with the new conditions,
|
||||
they stop. The response also contains the first Commitment pubkey provided by the
|
||||
responder, which refunds the initial balance back to both parties.
|
||||
|
||||
|
||||
Funding SignAccept
|
||||
------------
|
||||
The requester now has sufficient information to get a refund if the transaction
|
||||
is ever broadcast. The requester signs the Funding Transaction and this message
|
||||
gives the signature to the responder. The requester also provides the signature
|
||||
for the initial Commitment Transaction.
|
||||
|
||||
|
||||
Funding SignComplete
|
||||
---------------
|
||||
The responder has sufficient information to broadcast the Funding Transaction
|
||||
(with the ability to receive a refund), the responder broadcasts on the
|
||||
blockchain and returns the txid to the requester, with the signature of the
|
||||
Funding Transaction. This is provided as a courtesy, it cannot be relied upon
|
||||
with non-cooperative channel counterparties and the Funding Transaction can be
|
||||
braodcast without this message being received by the requester. After the
|
||||
necessary number of confirmations, Lightning Network transactions can proceed.
|
@ -1,7 +1,6 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
@ -19,7 +18,7 @@ type FundingRequest struct {
|
||||
//Should double-check the total funding later
|
||||
MinTotalFundingAmount btcutil.Amount
|
||||
|
||||
//CLTV lock-time to use
|
||||
//CLTV/CSV lock-time to use
|
||||
LockTime uint32
|
||||
|
||||
//Who pays the fees
|
||||
@ -31,14 +30,13 @@ type FundingRequest struct {
|
||||
RevocationHash [20]byte
|
||||
Pubkey *btcec.PublicKey
|
||||
DeliveryPkScript PkScript //*MUST* be either P2PKH or P2SH
|
||||
//FIXME: Need a ChangePkScript PkScript for dual-funder CLTV?
|
||||
ChangePkScript PkScript //*MUST* be either P2PKH or P2SH
|
||||
|
||||
Inputs []*wire.TxIn
|
||||
}
|
||||
|
||||
func (c *FundingRequest) Decode(r io.Reader, pver uint32) error {
|
||||
//Channel Type (0/1)
|
||||
// default to 0 for CLTV-only
|
||||
//Funding Amount (1/8)
|
||||
//Channel Minimum Capacity (9/8)
|
||||
//Revocation Hash (17/20)
|
||||
@ -47,7 +45,9 @@ func (c *FundingRequest) Decode(r io.Reader, pver uint32) error {
|
||||
//Minimum Transaction Fee Per Kb (77/8)
|
||||
//LockTime (85/4)
|
||||
//FeePayer (89/1)
|
||||
//DeliveryPkScript
|
||||
//DeliveryPkScript (final delivery)
|
||||
// First byte length then pkscript
|
||||
//ChangePkScript (change for extra from inputs)
|
||||
// First byte length then pkscript
|
||||
//Inputs: Create the TxIns
|
||||
// First byte is number of inputs
|
||||
@ -63,12 +63,13 @@ func (c *FundingRequest) Decode(r io.Reader, pver uint32) error {
|
||||
&c.LockTime,
|
||||
&c.FeePayer,
|
||||
&c.DeliveryPkScript,
|
||||
&c.ChangePkScript,
|
||||
&c.Inputs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Validate()
|
||||
return nil
|
||||
}
|
||||
|
||||
//Creates a new FundingRequest
|
||||
@ -89,6 +90,7 @@ func (c *FundingRequest) Encode(w io.Writer, pver uint32) error {
|
||||
//LockTime
|
||||
//FeePayer
|
||||
//DeliveryPkScript
|
||||
//ChangePkScript
|
||||
//Inputs: Append the actual Txins
|
||||
err := writeElements(w, false,
|
||||
c.ChannelType,
|
||||
@ -101,6 +103,7 @@ func (c *FundingRequest) Encode(w io.Writer, pver uint32) error {
|
||||
c.LockTime,
|
||||
c.FeePayer,
|
||||
c.DeliveryPkScript,
|
||||
c.ChangePkScript,
|
||||
c.Inputs)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -114,13 +117,14 @@ func (c *FundingRequest) Command() uint32 {
|
||||
}
|
||||
|
||||
func (c *FundingRequest) MaxPayloadLength(uint32) uint32 {
|
||||
//90 (base size) + 26 (pkscript) + 1 (numTxes) + 127*36(127 inputs * sha256+idx)
|
||||
//4690
|
||||
return 4689
|
||||
//90 (base size) + 26 (pkscript) + 26 (pkscript) + 1 (numTxes) + 127*36(127 inputs * sha256+idx)
|
||||
return 4715
|
||||
}
|
||||
|
||||
//Makes sure the struct data is valid (e.g. no negatives or invalid pkscripts)
|
||||
func (c *FundingRequest) Validate() error {
|
||||
var err error
|
||||
|
||||
//No negative values
|
||||
if c.FundingAmount < 0 {
|
||||
return fmt.Errorf("FundingAmount cannot be negative")
|
||||
@ -137,28 +141,16 @@ func (c *FundingRequest) Validate() error {
|
||||
return fmt.Errorf("MinTotalFundingAmount cannot be negative")
|
||||
}
|
||||
|
||||
//PkScript is either P2SH or P2PKH
|
||||
if len(c.DeliveryPkScript) == 25 {
|
||||
//P2PKH
|
||||
//Begins with OP_DUP OP_HASH160 PUSHDATA(20)
|
||||
if !bytes.Equal(c.DeliveryPkScript[0:3], []byte{118, 169, 20}) &&
|
||||
//Ends with OP_EQUALVERIFY OP_CHECKSIG
|
||||
!bytes.Equal(c.DeliveryPkScript[23:25], []byte{136, 172}) {
|
||||
//If it's not correct, return error
|
||||
return fmt.Errorf("PkScript only allows P2SH or P2PKH")
|
||||
}
|
||||
} else if len(c.DeliveryPkScript) == 23 {
|
||||
//P2SH
|
||||
//Begins with OP_HASH160 PUSHDATA(20)
|
||||
if !bytes.Equal(c.DeliveryPkScript[0:2], []byte{169, 20}) &&
|
||||
//Ends with OP_EQUAL
|
||||
!bytes.Equal(c.DeliveryPkScript[22:23], []byte{135}) {
|
||||
//If it's not correct, return error
|
||||
return fmt.Errorf("PkScript only allows P2SH or P2PKH")
|
||||
}
|
||||
} else {
|
||||
//Length not 23 or 25
|
||||
return fmt.Errorf("PkScript only allows P2SH or P2PKH")
|
||||
//DeliveryPkScript is either P2SH or P2PKH
|
||||
err = ValidatePkScript(c.DeliveryPkScript)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//ChangePkScript is either P2SH or P2PKH
|
||||
err = ValidatePkScript(c.ChangePkScript)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//We're good!
|
||||
|
@ -33,6 +33,11 @@ var (
|
||||
//PKhash: n2fkWVphUzw3zSigzPsv9GuDyg9mohzKpz
|
||||
deliveryPkScript, _ = hex.DecodeString("76a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac")
|
||||
|
||||
// Change PkScript
|
||||
//Privkey: 5b18f5049efd9d3aff1fb9a06506c0b809fb71562b6ecd02f6c5b3ab298f3b0f
|
||||
//PKhash: miky84cHvLuk6jcT6GsSbgHR8d7eZCu9Qc
|
||||
changePkScript, _ = hex.DecodeString("76a914238ee44bb5c8c1314dd03974a17ec6c406fdcb8388ac")
|
||||
|
||||
//echo -n | openssl sha256
|
||||
//This stuff gets reversed!!!
|
||||
shaHash1Bytes, _ = hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
|
||||
@ -58,10 +63,11 @@ var (
|
||||
RevocationHash: revocationHash,
|
||||
Pubkey: pubKey,
|
||||
DeliveryPkScript: deliveryPkScript,
|
||||
ChangePkScript: changePkScript,
|
||||
Inputs: inputs,
|
||||
}
|
||||
serializedString = "000000000005f5e1000000000008f0d1804132b6b48371f7b022a16eacb9b2b0ebee134d4102f977808cb9577897582d7524b562691e180953dd0008eb44e09594c539d6daee00000000000200000000000000004e20000010e0001976a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac02e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8550000000001ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b00000001"
|
||||
serializedMessage = "0709110b00000014000000be000000000005f5e1000000000008f0d1804132b6b48371f7b022a16eacb9b2b0ebee134d4102f977808cb9577897582d7524b562691e180953dd0008eb44e09594c539d6daee00000000000200000000000000004e20000010e0001976a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac02e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8550000000001ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b00000001"
|
||||
serializedString = "000000000005f5e1000000000008f0d1804132b6b48371f7b022a16eacb9b2b0ebee134d4102f977808cb9577897582d7524b562691e180953dd0008eb44e09594c539d6daee00000000000200000000000000004e20000010e0001976a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac1976a914238ee44bb5c8c1314dd03974a17ec6c406fdcb8388ac02e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8550000000001ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b00000001"
|
||||
serializedMessage = "0709110b000000c8000000d8000000000005f5e1000000000008f0d1804132b6b48371f7b022a16eacb9b2b0ebee134d4102f977808cb9577897582d7524b562691e180953dd0008eb44e09594c539d6daee00000000000200000000000000004e20000010e0001976a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac1976a914238ee44bb5c8c1314dd03974a17ec6c406fdcb8388ac02e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8550000000001ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b00000001"
|
||||
)
|
||||
|
||||
func TestFundingRequestEncodeDecode(t *testing.T) {
|
||||
@ -71,19 +77,20 @@ func TestFundingRequestEncodeDecode(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error("Serialization error")
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Logf("Encoded Funding Request: %x\n", b.Bytes())
|
||||
//Check if we serialized correctly
|
||||
if serializedString != hex.EncodeToString(b.Bytes()) {
|
||||
t.Error("Serialization does not match expected")
|
||||
}
|
||||
} else {
|
||||
t.Logf("Encoded Funding Request: %x\n", b.Bytes())
|
||||
//Check if we serialized correctly
|
||||
if serializedString != hex.EncodeToString(b.Bytes()) {
|
||||
t.Error("Serialization does not match expected")
|
||||
}
|
||||
|
||||
//So I can do: hexdump -C /dev/shm/fundingRequest.raw
|
||||
if WRITE_FILE {
|
||||
err = ioutil.WriteFile(FILENAME, b.Bytes(), 0644)
|
||||
if err != nil {
|
||||
t.Error("File write error")
|
||||
t.Error(err.Error())
|
||||
//So I can do: hexdump -C /dev/shm/fundingRequest.raw
|
||||
if WRITE_FILE {
|
||||
err = ioutil.WriteFile(FILENAME, b.Bytes(), 0644)
|
||||
if err != nil {
|
||||
t.Error("File write error")
|
||||
t.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,14 +104,14 @@ func TestFundingRequestEncodeDecode(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error("Decoding Error")
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
if !reflect.DeepEqual(newFunding, fundingRequest) {
|
||||
t.Error("Decoding does not match!")
|
||||
}
|
||||
//Show the struct
|
||||
t.Log(newFunding.String())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(newFunding, fundingRequest) {
|
||||
t.Error("Decoding does not match!")
|
||||
}
|
||||
//Show the struct
|
||||
t.Log(newFunding.String())
|
||||
|
||||
//Test message using Message interface
|
||||
//Serialize/Encode
|
||||
b = new(bytes.Buffer)
|
||||
@ -119,9 +126,10 @@ func TestFundingRequestEncodeDecode(t *testing.T) {
|
||||
_, msg, _, err := ReadMessage(c, uint32(1), wire.TestNet3)
|
||||
if err != nil {
|
||||
t.Errorf(err.Error())
|
||||
} else {
|
||||
if !reflect.DeepEqual(msg, fundingRequest) {
|
||||
t.Error("Message decoding does not match!")
|
||||
}
|
||||
t.Logf(msg.String())
|
||||
}
|
||||
if !reflect.DeepEqual(msg, fundingRequest) {
|
||||
t.Error("Message decoding does not match!")
|
||||
}
|
||||
t.Logf(msg.String())
|
||||
}
|
||||
|
185
lnwire/funding_response.go
Normal file
185
lnwire/funding_response.go
Normal file
@ -0,0 +1,185 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"io"
|
||||
)
|
||||
|
||||
type FundingResponse struct {
|
||||
ChannelType uint8
|
||||
|
||||
ReservationID uint64
|
||||
|
||||
FundingAmount btcutil.Amount
|
||||
ReserveAmount btcutil.Amount
|
||||
MinFeePerKb btcutil.Amount //Lock-in min fee
|
||||
|
||||
//CLTV/CSV lock-time to use
|
||||
LockTime uint32
|
||||
|
||||
//Who pays the fees
|
||||
//0: (default) channel initiator
|
||||
//1: split
|
||||
//2: channel responder
|
||||
FeePayer uint8
|
||||
|
||||
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
|
||||
|
||||
Inputs []*wire.TxIn
|
||||
}
|
||||
|
||||
func (c *FundingResponse) Decode(r io.Reader, pver uint32) error {
|
||||
//ReservationID (0/8)
|
||||
//Channel Type (8/1)
|
||||
//Funding Amount (9/8)
|
||||
//Revocation Hash (29/20)
|
||||
//Commitment Pubkey (61/32)
|
||||
//Reserve Amount (69/8)
|
||||
//Minimum Transaction Fee Per Kb (77/8)
|
||||
//LockTime (81/4)
|
||||
//FeePayer (82/1)
|
||||
//DeliveryPkScript (final delivery)
|
||||
// First byte length then pkscript
|
||||
//ChangePkScript (change for extra from inputs)
|
||||
// First byte length then pkscript
|
||||
//CommitSig
|
||||
// First byte length then sig
|
||||
//Inputs: Create the TxIns
|
||||
// First byte is number of inputs
|
||||
// For each input, it's 32bytes txin & 4bytes index
|
||||
err := readElements(r, false,
|
||||
&c.ReservationID,
|
||||
&c.ChannelType,
|
||||
&c.FundingAmount,
|
||||
&c.RevocationHash,
|
||||
&c.Pubkey,
|
||||
&c.ReserveAmount,
|
||||
&c.MinFeePerKb,
|
||||
&c.LockTime,
|
||||
&c.FeePayer,
|
||||
&c.DeliveryPkScript,
|
||||
&c.ChangePkScript,
|
||||
&c.CommitSig,
|
||||
&c.Inputs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Creates a new FundingResponse
|
||||
func NewFundingResponse() *FundingResponse {
|
||||
return &FundingResponse{}
|
||||
}
|
||||
|
||||
//Serializes the item from the FundingResponse struct
|
||||
//Writes the data to w
|
||||
func (c *FundingResponse) Encode(w io.Writer, pver uint32) error {
|
||||
//ReservationID (8)
|
||||
//Channel Type (1)
|
||||
//Funding Amount (8)
|
||||
//Revocation Hash (20)
|
||||
//Commitment Pubkey (32)
|
||||
//Reserve Amount (8)
|
||||
//Minimum Transaction Fee Per Kb (8)
|
||||
//LockTime (4)
|
||||
//FeePayer (1)
|
||||
//DeliveryPkScript (final delivery)
|
||||
//ChangePkScript (change for extra from inputs)
|
||||
//CommitSig
|
||||
//Inputs
|
||||
err := writeElements(w, false,
|
||||
c.ReservationID,
|
||||
c.ChannelType,
|
||||
c.FundingAmount,
|
||||
c.RevocationHash,
|
||||
c.Pubkey,
|
||||
c.ReserveAmount,
|
||||
c.MinFeePerKb,
|
||||
c.LockTime,
|
||||
c.FeePayer,
|
||||
c.DeliveryPkScript,
|
||||
c.ChangePkScript,
|
||||
c.CommitSig,
|
||||
c.Inputs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FundingResponse) Command() uint32 {
|
||||
return CmdFundingResponse
|
||||
}
|
||||
|
||||
func (c *FundingResponse) MaxPayloadLength(uint32) uint32 {
|
||||
//82 (base size) + 26 (pkscript) + 26 (pkscript) + 74sig + 1 (numTxes) + 127*36(127 inputs * sha256+idx)
|
||||
return 4781
|
||||
}
|
||||
|
||||
//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.FundingAmount < 0 {
|
||||
return fmt.Errorf("FundingAmount cannot be negative")
|
||||
}
|
||||
|
||||
if c.ReserveAmount < 0 {
|
||||
return fmt.Errorf("ReserveAmount cannot be negative")
|
||||
}
|
||||
|
||||
if c.MinFeePerKb < 0 {
|
||||
return fmt.Errorf("MinFeePerKb cannot be negative")
|
||||
}
|
||||
|
||||
//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
|
||||
}
|
||||
|
||||
//We're good!
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FundingResponse) String() string {
|
||||
var inputs string
|
||||
for i, in := range c.Inputs {
|
||||
inputs += fmt.Sprintf("\n Slice\t%d\n", i)
|
||||
inputs += fmt.Sprintf("\tHash\t%s\n", in.PreviousOutPoint.Hash)
|
||||
inputs += fmt.Sprintf("\tIndex\t%d\n", in.PreviousOutPoint.Index)
|
||||
}
|
||||
return fmt.Sprintf("\n--- Begin FundingResponse ---\n") +
|
||||
fmt.Sprintf("ChannelType:\t\t%x\n", c.ChannelType) +
|
||||
fmt.Sprintf("ReservationID:\t\t%d\n", c.ReservationID) +
|
||||
fmt.Sprintf("FundingAmount:\t\t%s\n", c.FundingAmount.String()) +
|
||||
fmt.Sprintf("ReserveAmount:\t\t%s\n", c.ReserveAmount.String()) +
|
||||
fmt.Sprintf("MinFeePerKb:\t\t%s\n", c.MinFeePerKb.String()) +
|
||||
fmt.Sprintf("LockTime\t\t%d\n", c.LockTime) +
|
||||
fmt.Sprintf("FeePayer\t\t%x\n", c.FeePayer) +
|
||||
fmt.Sprintf("RevocationHash\t\t%x\n", c.RevocationHash) +
|
||||
fmt.Sprintf("Pubkey\t\t\t%x\n", c.Pubkey.SerializeCompressed()) +
|
||||
fmt.Sprintf("CommitSig\t\t%x\n", c.CommitSig.Serialize()) +
|
||||
fmt.Sprintf("DeliveryPkScript\t%x\n", c.DeliveryPkScript) +
|
||||
fmt.Sprintf("ChangePkScript\t%x\n", c.ChangePkScript) +
|
||||
fmt.Sprintf("Inputs:") +
|
||||
inputs +
|
||||
fmt.Sprintf("--- End FundingResponse ---\n")
|
||||
}
|
143
lnwire/funding_response_test.go
Normal file
143
lnwire/funding_response_test.go
Normal file
@ -0,0 +1,143 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
// "io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFundingResponseEncodeDecode(t *testing.T) {
|
||||
var (
|
||||
//For debugging, writes to /dev/shm/
|
||||
//Maybe in the future do it if you do "go test -v"
|
||||
WRITE_FILE = false
|
||||
FILENAME = "/dev/shm/fundingResponse.raw"
|
||||
|
||||
//preimage: 9a2cbd088763db88dd8ba79e5726daa6aba4aa7e
|
||||
//echo -n | openssl sha256 | openssl ripemd160 | openssl sha256 | openssl ripemd160
|
||||
revocationHashBytes, _ = hex.DecodeString("4132b6b48371f7b022a16eacb9b2b0ebee134d41")
|
||||
revocationHash [20]byte
|
||||
_ = copy(revocationHash[:], revocationHashBytes)
|
||||
|
||||
privKeyBytes, _ = hex.DecodeString("9fa1d55217f57019a3c37f49465896b15836f54cb8ef6963870a52926420a2dd")
|
||||
privKey, pubKey = btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes)
|
||||
//pubKeyBytes, _ = hex.DecodeString("02f977808cb9577897582d7524b562691e180953dd0008eb44e09594c539d6daee")
|
||||
//pubKey, _ = btcec.ParsePubKey(pubKeyBytes, btcec.S256())
|
||||
|
||||
// Delivery PkScript
|
||||
//Privkey: f2c00ead9cbcfec63098dc0a5f152c0165aff40a2ab92feb4e24869a284c32a7
|
||||
//PKhash: n2fkWVphUzw3zSigzPsv9GuDyg9mohzKpz
|
||||
deliveryPkScript, _ = hex.DecodeString("76a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac")
|
||||
|
||||
// Change PkScript
|
||||
//Privkey: 5b18f5049efd9d3aff1fb9a06506c0b809fb71562b6ecd02f6c5b3ab298f3b0f
|
||||
//PKhash: miky84cHvLuk6jcT6GsSbgHR8d7eZCu9Qc
|
||||
changePkScript, _ = hex.DecodeString("76a914238ee44bb5c8c1314dd03974a17ec6c406fdcb8388ac")
|
||||
|
||||
//echo -n | openssl sha256
|
||||
//This stuff gets reversed!!!
|
||||
shaHash1Bytes, _ = hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
|
||||
shaHash1, _ = wire.NewShaHash(shaHash1Bytes)
|
||||
outpoint1 = wire.NewOutPoint(shaHash1, 0)
|
||||
//echo | openssl sha256
|
||||
//This stuff gets reversed!!!
|
||||
shaHash2Bytes, _ = hex.DecodeString("01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b")
|
||||
shaHash2, _ = wire.NewShaHash(shaHash2Bytes)
|
||||
outpoint2 = wire.NewOutPoint(shaHash2, 1)
|
||||
//create inputs from outpoint1 and outpoint2
|
||||
inputs = []*wire.TxIn{wire.NewTxIn(outpoint1, nil), wire.NewTxIn(outpoint2, nil)}
|
||||
|
||||
//Commitment Signature
|
||||
tx = wire.NewMsgTx()
|
||||
emptybytes = new([]byte)
|
||||
sigStr, _ = txscript.RawTxInSignature(tx, 0, *emptybytes, txscript.SigHashAll, privKey)
|
||||
commitSig, _ = btcec.ParseSignature(sigStr, btcec.S256())
|
||||
|
||||
//funding response
|
||||
fundingResponse = &FundingResponse{
|
||||
ChannelType: uint8(1),
|
||||
ReservationID: uint64(12345678),
|
||||
FundingAmount: btcutil.Amount(100000000),
|
||||
ReserveAmount: btcutil.Amount(131072),
|
||||
MinFeePerKb: btcutil.Amount(20000),
|
||||
LockTime: uint32(4320), //30 block-days
|
||||
FeePayer: uint8(1),
|
||||
RevocationHash: revocationHash,
|
||||
Pubkey: pubKey,
|
||||
CommitSig: commitSig,
|
||||
DeliveryPkScript: deliveryPkScript,
|
||||
ChangePkScript: changePkScript,
|
||||
Inputs: inputs,
|
||||
}
|
||||
serializedString = "0000000000bc614e010000000005f5e1004132b6b48371f7b022a16eacb9b2b0ebee134d4102f977808cb9577897582d7524b562691e180953dd0008eb44e09594c539d6daee00000000000200000000000000004e20000010e0011976a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac1976a914238ee44bb5c8c1314dd03974a17ec6c406fdcb8388ac4630440220333835e58e958f5e92b4ff4e6fa2470dac88094c97506b4d6d1f4e23e52cb481022057483ac18d6b9c9c14f0c626694c9ccf8b27b3dbbedfdf6b6c9a9fa9f427a1df02e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8550000000001ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b00000001"
|
||||
serializedMessage = "0709110b000000d20000011f0000000000bc614e010000000005f5e1004132b6b48371f7b022a16eacb9b2b0ebee134d4102f977808cb9577897582d7524b562691e180953dd0008eb44e09594c539d6daee00000000000200000000000000004e20000010e0011976a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac1976a914238ee44bb5c8c1314dd03974a17ec6c406fdcb8388ac4630440220333835e58e958f5e92b4ff4e6fa2470dac88094c97506b4d6d1f4e23e52cb481022057483ac18d6b9c9c14f0c626694c9ccf8b27b3dbbedfdf6b6c9a9fa9f427a1df02e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8550000000001ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b00000001"
|
||||
)
|
||||
//Test serialization
|
||||
b := new(bytes.Buffer)
|
||||
err := fundingResponse.Encode(b, 0)
|
||||
if err != nil {
|
||||
t.Error("Serialization error")
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
t.Logf("Encoded Funding Response: %x\n", b.Bytes())
|
||||
//Check if we serialized correctly
|
||||
if serializedString != hex.EncodeToString(b.Bytes()) {
|
||||
t.Error("Serialization does not match expected")
|
||||
}
|
||||
|
||||
//So I can do: hexdump -C /dev/shm/fundingResponse.raw
|
||||
if WRITE_FILE {
|
||||
err = ioutil.WriteFile(FILENAME, b.Bytes(), 0644)
|
||||
if err != nil {
|
||||
t.Error("File write error")
|
||||
t.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Test deserialization
|
||||
//Make a new buffer just to be clean
|
||||
c := new(bytes.Buffer)
|
||||
c.Write(b.Bytes())
|
||||
|
||||
newFunding := NewFundingResponse()
|
||||
err = newFunding.Decode(c, 0)
|
||||
if err != nil {
|
||||
t.Error("Decoding Error")
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
if !reflect.DeepEqual(newFunding, fundingResponse) {
|
||||
t.Error("Decoding does not match!")
|
||||
}
|
||||
//Show the struct
|
||||
t.Log(newFunding.String())
|
||||
}
|
||||
|
||||
//Test message using Message interface
|
||||
//Serialize/Encode
|
||||
b = new(bytes.Buffer)
|
||||
_, err = WriteMessage(b, fundingResponse, uint32(1), wire.TestNet3)
|
||||
t.Logf("%x\n", b.Bytes())
|
||||
if hex.EncodeToString(b.Bytes()) != serializedMessage {
|
||||
t.Error("Message encoding error")
|
||||
}
|
||||
//Deserialize/Decode
|
||||
c = new(bytes.Buffer)
|
||||
c.Write(b.Bytes())
|
||||
_, msg, _, err := ReadMessage(c, uint32(1), wire.TestNet3)
|
||||
if err != nil {
|
||||
t.Errorf(err.Error())
|
||||
} else {
|
||||
if !reflect.DeepEqual(msg, fundingResponse) {
|
||||
t.Error("Message decoding does not match!")
|
||||
}
|
||||
t.Logf(msg.String())
|
||||
}
|
||||
}
|
84
lnwire/funding_signaccept.go
Normal file
84
lnwire/funding_signaccept.go
Normal file
@ -0,0 +1,84 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"io"
|
||||
)
|
||||
|
||||
type FundingSignAccept struct {
|
||||
ReservationID uint64
|
||||
|
||||
CommitSig *btcec.Signature //Requester's Commitment
|
||||
FundingTXSigs *[]btcec.Signature
|
||||
}
|
||||
|
||||
func (c *FundingSignAccept) Decode(r io.Reader, pver uint32) error {
|
||||
//ReservationID (0/8)
|
||||
//CommitSig
|
||||
// First byte length then sig
|
||||
//FundingTXSigs
|
||||
// First byte is number of FundingTxSigs
|
||||
// Sorted list of the requester's input signatures
|
||||
// (originally provided in the Funding Request)
|
||||
err := readElements(r, false,
|
||||
&c.ReservationID,
|
||||
&c.CommitSig,
|
||||
&c.FundingTXSigs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Creates a new FundingSignAccept
|
||||
func NewFundingSignAccept() *FundingSignAccept {
|
||||
return &FundingSignAccept{}
|
||||
}
|
||||
|
||||
//Serializes the item from the FundingSignAccept struct
|
||||
//Writes the data to w
|
||||
func (c *FundingSignAccept) Encode(w io.Writer, pver uint32) error {
|
||||
//ReservationID (8)
|
||||
//CommitSig
|
||||
//FundingTxSigs
|
||||
err := writeElements(w, false,
|
||||
c.ReservationID,
|
||||
c.CommitSig,
|
||||
c.FundingTXSigs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FundingSignAccept) Command() uint32 {
|
||||
return CmdFundingSignAccept
|
||||
}
|
||||
|
||||
func (c *FundingSignAccept) MaxPayloadLength(uint32) uint32 {
|
||||
//8 (base size) + 73 + (73maxSigSize*127maxInputs)
|
||||
return 9352
|
||||
}
|
||||
|
||||
//Makes sure the struct data is valid (e.g. no negatives or invalid pkscripts)
|
||||
func (c *FundingSignAccept) Validate() error {
|
||||
//We're good!
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FundingSignAccept) String() string {
|
||||
var sigs string
|
||||
for i, in := range *c.FundingTXSigs {
|
||||
sigs += fmt.Sprintf("\n Slice\t%d\n", i)
|
||||
sigs += fmt.Sprintf("\tSig\t%x\n", in.Serialize())
|
||||
}
|
||||
return fmt.Sprintf("\n--- Begin FundingSignAccept ---\n") +
|
||||
fmt.Sprintf("ReservationID:\t\t%d\n", c.ReservationID) +
|
||||
fmt.Sprintf("CommitSig\t\t%x\n", c.CommitSig.Serialize()) +
|
||||
fmt.Sprintf("FundingTxSigs:") +
|
||||
sigs +
|
||||
fmt.Sprintf("--- End FundingSignAccept ---\n")
|
||||
}
|
115
lnwire/funding_signaccept_test.go
Normal file
115
lnwire/funding_signaccept_test.go
Normal file
@ -0,0 +1,115 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
// "io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFundingSignAcceptEncodeDecode(t *testing.T) {
|
||||
var (
|
||||
//For debugging, writes to /dev/shm/
|
||||
//Maybe in the future do it if you do "go test -v"
|
||||
WRITE_FILE = false
|
||||
FILENAME = "/dev/shm/fundingSignAccept.raw"
|
||||
|
||||
privKeyBytes, _ = hex.DecodeString("9fa1d55217f57019a3c37f49465896b15836f54cb8ef6963870a52926420a2dd")
|
||||
privKey, _ = btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes)
|
||||
//pubKeyBytes, _ = hex.DecodeString("02f977808cb9577897582d7524b562691e180953dd0008eb44e09594c539d6daee")
|
||||
//pubKey, _ = btcec.ParsePubKey(pubKeyBytes, btcec.S256())
|
||||
|
||||
//Commitment Signature
|
||||
tx = wire.NewMsgTx()
|
||||
emptybytes = new([]byte)
|
||||
sigStr, _ = txscript.RawTxInSignature(tx, 0, *emptybytes, txscript.SigHashAll, privKey)
|
||||
commitSig, _ = btcec.ParseSignature(sigStr, btcec.S256())
|
||||
|
||||
//Funding TX Sig 1
|
||||
sig1privKeyBytes, _ = hex.DecodeString("927f5827d75dd2addeb532c0fa5ac9277565f981dd6d0d037b422be5f60bdbef")
|
||||
sig1privKey, _ = btcec.PrivKeyFromBytes(btcec.S256(), sig1privKeyBytes)
|
||||
sigStr1, _ = txscript.RawTxInSignature(tx, 0, *emptybytes, txscript.SigHashAll, sig1privKey)
|
||||
commitSig1, _ = btcec.ParseSignature(sigStr1, btcec.S256())
|
||||
//Funding TX Sig 2
|
||||
sig2privKeyBytes, _ = hex.DecodeString("8a4ad188f6f4000495b765cfb6ffa591133a73019c45428ddd28f53bab551847")
|
||||
sig2privKey, _ = btcec.PrivKeyFromBytes(btcec.S256(), sig2privKeyBytes)
|
||||
sigStr2, _ = txscript.RawTxInSignature(tx, 0, *emptybytes, txscript.SigHashAll, sig2privKey)
|
||||
commitSig2, _ = btcec.ParseSignature(sigStr2, btcec.S256())
|
||||
fundingTXSigs = append(*new([]btcec.Signature), *commitSig1, *commitSig2)
|
||||
|
||||
//funding response
|
||||
fundingSignAccept = &FundingSignAccept{
|
||||
ReservationID: uint64(12345678),
|
||||
CommitSig: commitSig,
|
||||
FundingTXSigs: &fundingTXSigs,
|
||||
}
|
||||
serializedString = "0000000000bc614e4630440220333835e58e958f5e92b4ff4e6fa2470dac88094c97506b4d6d1f4e23e52cb481022057483ac18d6b9c9c14f0c626694c9ccf8b27b3dbbedfdf6b6c9a9fa9f427a1df02473045022100e7946d057c0b4cc4d3ea525ba156b429796858ebc543d75a6c6c2cbca732db6902202fea377c1f9fb98cd103cf5a4fba276a074b378d4227d15f5fa6439f1a6685bb4630440220235ee55fed634080089953048c3e3f7dc3a154fd7ad18f31dc08e05b7864608a02203bdd7d4e4d9a8162d4b511faf161f0bb16c45181187125017cd0c620c53876ca"
|
||||
serializedMessage = "0709110b000000dc000000df0000000000bc614e4630440220333835e58e958f5e92b4ff4e6fa2470dac88094c97506b4d6d1f4e23e52cb481022057483ac18d6b9c9c14f0c626694c9ccf8b27b3dbbedfdf6b6c9a9fa9f427a1df02473045022100e7946d057c0b4cc4d3ea525ba156b429796858ebc543d75a6c6c2cbca732db6902202fea377c1f9fb98cd103cf5a4fba276a074b378d4227d15f5fa6439f1a6685bb4630440220235ee55fed634080089953048c3e3f7dc3a154fd7ad18f31dc08e05b7864608a02203bdd7d4e4d9a8162d4b511faf161f0bb16c45181187125017cd0c620c53876ca"
|
||||
)
|
||||
//Test serialization
|
||||
b := new(bytes.Buffer)
|
||||
err := fundingSignAccept.Encode(b, 0)
|
||||
if err != nil {
|
||||
t.Error("Serialization error")
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
t.Logf("Encoded Funding SignAccept: %x\n", b.Bytes())
|
||||
//Check if we serialized correctly
|
||||
if serializedString != hex.EncodeToString(b.Bytes()) {
|
||||
t.Error("Serialization does not match expected")
|
||||
}
|
||||
|
||||
//So I can do: hexdump -C /dev/shm/fundingSignAccept.raw
|
||||
if WRITE_FILE {
|
||||
err = ioutil.WriteFile(FILENAME, b.Bytes(), 0644)
|
||||
if err != nil {
|
||||
t.Error("File write error")
|
||||
t.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Test deserialization
|
||||
//Make a new buffer just to be clean
|
||||
c := new(bytes.Buffer)
|
||||
c.Write(b.Bytes())
|
||||
|
||||
newFunding := NewFundingSignAccept()
|
||||
err = newFunding.Decode(c, 0)
|
||||
if err != nil {
|
||||
t.Error("Decoding Error")
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
if !reflect.DeepEqual(newFunding, fundingSignAccept) {
|
||||
t.Error("Decoding does not match!")
|
||||
}
|
||||
//Show the struct
|
||||
t.Log(newFunding.String())
|
||||
}
|
||||
|
||||
//Test message using Message interface
|
||||
//Serialize/Encode
|
||||
b = new(bytes.Buffer)
|
||||
_, err = WriteMessage(b, fundingSignAccept, uint32(1), wire.TestNet3)
|
||||
t.Logf("%x\n", b.Bytes())
|
||||
if hex.EncodeToString(b.Bytes()) != serializedMessage {
|
||||
t.Error("Message encoding error")
|
||||
}
|
||||
//Deserialize/Decode
|
||||
c = new(bytes.Buffer)
|
||||
c.Write(b.Bytes())
|
||||
_, msg, _, err := ReadMessage(c, uint32(1), wire.TestNet3)
|
||||
if err != nil {
|
||||
t.Errorf(err.Error())
|
||||
} else {
|
||||
if !reflect.DeepEqual(msg, fundingSignAccept) {
|
||||
t.Error("Message decoding does not match!")
|
||||
}
|
||||
t.Logf(msg.String())
|
||||
}
|
||||
}
|
84
lnwire/funding_signcomplete.go
Normal file
84
lnwire/funding_signcomplete.go
Normal file
@ -0,0 +1,84 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"io"
|
||||
)
|
||||
|
||||
type FundingSignComplete struct {
|
||||
ReservationID uint64
|
||||
|
||||
TxID *wire.ShaHash
|
||||
FundingTXSigs *[]btcec.Signature
|
||||
}
|
||||
|
||||
func (c *FundingSignComplete) Decode(r io.Reader, pver uint32) error {
|
||||
//ReservationID (0/8)
|
||||
//TxID
|
||||
//FundingTXSigs
|
||||
// First byte is number of FundingTxSigs
|
||||
// Sorted list of the requester's input signatures
|
||||
// (originally provided in the Funding Request)
|
||||
err := readElements(r, false,
|
||||
&c.ReservationID,
|
||||
&c.TxID,
|
||||
&c.FundingTXSigs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Creates a new FundingSignComplete
|
||||
func NewFundingSignComplete() *FundingSignComplete {
|
||||
return &FundingSignComplete{}
|
||||
}
|
||||
|
||||
//Serializes the item from the FundingSignComplete struct
|
||||
//Writes the data to w
|
||||
func (c *FundingSignComplete) Encode(w io.Writer, pver uint32) error {
|
||||
//ReservationID (8)
|
||||
//CommitSig
|
||||
//FundingTxSigs
|
||||
err := writeElements(w, false,
|
||||
c.ReservationID,
|
||||
c.TxID,
|
||||
c.FundingTXSigs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FundingSignComplete) Command() uint32 {
|
||||
return CmdFundingSignComplete
|
||||
}
|
||||
|
||||
func (c *FundingSignComplete) MaxPayloadLength(uint32) uint32 {
|
||||
//8 (base size) + 32 + (73maxSigSize*127maxInputs)
|
||||
return 9311
|
||||
}
|
||||
|
||||
//Makes sure the struct data is valid (e.g. no negatives or invalid pkscripts)
|
||||
func (c *FundingSignComplete) Validate() error {
|
||||
//We're good!
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FundingSignComplete) String() string {
|
||||
var sigs string
|
||||
for i, in := range *c.FundingTXSigs {
|
||||
sigs += fmt.Sprintf("\n Slice\t%d\n", i)
|
||||
sigs += fmt.Sprintf("\tSig\t%x\n", in.Serialize())
|
||||
}
|
||||
return fmt.Sprintf("\n--- Begin FundingSignComplete ---\n") +
|
||||
fmt.Sprintf("ReservationID:\t\t%d\n", c.ReservationID) +
|
||||
fmt.Sprintf("TxID\t\t%s\n", c.TxID.String()) +
|
||||
fmt.Sprintf("FundingTxSigs:") +
|
||||
sigs +
|
||||
fmt.Sprintf("--- End FundingSignComplete ---\n")
|
||||
}
|
112
lnwire/funding_signcomplete_test.go
Normal file
112
lnwire/funding_signcomplete_test.go
Normal file
@ -0,0 +1,112 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
// "io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFundingSignCompleteEncodeDecode(t *testing.T) {
|
||||
var (
|
||||
//For debugging, writes to /dev/shm/
|
||||
//Maybe in the future do it if you do "go test -v"
|
||||
WRITE_FILE = false
|
||||
FILENAME = "/dev/shm/fundingSignComplete.raw"
|
||||
|
||||
//TxID
|
||||
txid = new(wire.ShaHash)
|
||||
//Reversed when displayed
|
||||
txidBytes, _ = hex.DecodeString("fd95c6e5c9d5bcf9cfc7231b6a438e46c518c724d0b04b75cc8fddf84a254e3a")
|
||||
_ = copy(txid[:], txidBytes)
|
||||
|
||||
//Funding TX Sig 1
|
||||
tx = wire.NewMsgTx()
|
||||
emptybytes = new([]byte)
|
||||
sig1privKeyBytes, _ = hex.DecodeString("927f5827d75dd2addeb532c0fa5ac9277565f981dd6d0d037b422be5f60bdbef")
|
||||
sig1privKey, _ = btcec.PrivKeyFromBytes(btcec.S256(), sig1privKeyBytes)
|
||||
sigStr1, _ = txscript.RawTxInSignature(tx, 0, *emptybytes, txscript.SigHashAll, sig1privKey)
|
||||
commitSig1, _ = btcec.ParseSignature(sigStr1, btcec.S256())
|
||||
//Funding TX Sig 2
|
||||
sig2privKeyBytes, _ = hex.DecodeString("8a4ad188f6f4000495b765cfb6ffa591133a73019c45428ddd28f53bab551847")
|
||||
sig2privKey, _ = btcec.PrivKeyFromBytes(btcec.S256(), sig2privKeyBytes)
|
||||
sigStr2, _ = txscript.RawTxInSignature(tx, 0, *emptybytes, txscript.SigHashAll, sig2privKey)
|
||||
commitSig2, _ = btcec.ParseSignature(sigStr2, btcec.S256())
|
||||
fundingTXSigs = append(*new([]btcec.Signature), *commitSig1, *commitSig2)
|
||||
|
||||
//funding response
|
||||
fundingSignComplete = &FundingSignComplete{
|
||||
ReservationID: uint64(12345678),
|
||||
TxID: txid,
|
||||
FundingTXSigs: &fundingTXSigs,
|
||||
}
|
||||
serializedString = "0000000000bc614efd95c6e5c9d5bcf9cfc7231b6a438e46c518c724d0b04b75cc8fddf84a254e3a02473045022100e7946d057c0b4cc4d3ea525ba156b429796858ebc543d75a6c6c2cbca732db6902202fea377c1f9fb98cd103cf5a4fba276a074b378d4227d15f5fa6439f1a6685bb4630440220235ee55fed634080089953048c3e3f7dc3a154fd7ad18f31dc08e05b7864608a02203bdd7d4e4d9a8162d4b511faf161f0bb16c45181187125017cd0c620c53876ca"
|
||||
serializedMessage = "0709110b000000e6000000b80000000000bc614efd95c6e5c9d5bcf9cfc7231b6a438e46c518c724d0b04b75cc8fddf84a254e3a02473045022100e7946d057c0b4cc4d3ea525ba156b429796858ebc543d75a6c6c2cbca732db6902202fea377c1f9fb98cd103cf5a4fba276a074b378d4227d15f5fa6439f1a6685bb4630440220235ee55fed634080089953048c3e3f7dc3a154fd7ad18f31dc08e05b7864608a02203bdd7d4e4d9a8162d4b511faf161f0bb16c45181187125017cd0c620c53876ca"
|
||||
)
|
||||
//Test serialization
|
||||
b := new(bytes.Buffer)
|
||||
err := fundingSignComplete.Encode(b, 0)
|
||||
if err != nil {
|
||||
t.Error("Serialization error")
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
t.Logf("Encoded FundingSignComplete: %x\n", b.Bytes())
|
||||
//Check if we serialized correctly
|
||||
if serializedString != hex.EncodeToString(b.Bytes()) {
|
||||
t.Error("Serialization does not match expected")
|
||||
}
|
||||
|
||||
//So I can do: hexdump -C /dev/shm/fundingSignComplete.raw
|
||||
if WRITE_FILE {
|
||||
err = ioutil.WriteFile(FILENAME, b.Bytes(), 0644)
|
||||
if err != nil {
|
||||
t.Error("File write error")
|
||||
t.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Test deserialization
|
||||
//Make a new buffer just to be clean
|
||||
c := new(bytes.Buffer)
|
||||
c.Write(b.Bytes())
|
||||
|
||||
newFunding := NewFundingSignComplete()
|
||||
err = newFunding.Decode(c, 0)
|
||||
if err != nil {
|
||||
t.Error("Decoding Error")
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
if !reflect.DeepEqual(newFunding, fundingSignComplete) {
|
||||
t.Error("Decoding does not match!")
|
||||
}
|
||||
//Show the struct
|
||||
t.Log(newFunding.String())
|
||||
}
|
||||
|
||||
//Test message using Message interface
|
||||
//Serialize/Encode
|
||||
b = new(bytes.Buffer)
|
||||
_, err = WriteMessage(b, fundingSignComplete, uint32(1), wire.TestNet3)
|
||||
t.Logf("%x\n", b.Bytes())
|
||||
if hex.EncodeToString(b.Bytes()) != serializedMessage {
|
||||
t.Error("Message encoding error")
|
||||
}
|
||||
//Deserialize/Decode
|
||||
c = new(bytes.Buffer)
|
||||
c.Write(b.Bytes())
|
||||
_, msg, _, err := ReadMessage(c, uint32(1), wire.TestNet3)
|
||||
if err != nil {
|
||||
t.Errorf(err.Error())
|
||||
} else {
|
||||
if !reflect.DeepEqual(msg, fundingSignComplete) {
|
||||
t.Error("Message decoding does not match!")
|
||||
}
|
||||
t.Logf(msg.String())
|
||||
}
|
||||
}
|
166
lnwire/lnwire.go
166
lnwire/lnwire.go
@ -1,6 +1,7 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
@ -39,6 +40,14 @@ func writeElement(w io.Writer, includeSig bool, element interface{}) error {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case uint64:
|
||||
var b [8]byte
|
||||
binary.BigEndian.PutUint64(b[:], uint64(e))
|
||||
_, err = w.Write(b[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case btcutil.Amount:
|
||||
err = binary.Write(w, binary.BigEndian, int64(e))
|
||||
if err != nil {
|
||||
@ -57,6 +66,47 @@ func writeElement(w io.Writer, includeSig bool, element interface{}) error {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case *[]btcec.Signature:
|
||||
numSigs := len(*e)
|
||||
if numSigs > 127 {
|
||||
return fmt.Errorf("Too many signatures!")
|
||||
}
|
||||
//Write the size
|
||||
err = writeElement(w, false, uint8(numSigs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//Write the data
|
||||
for i := 0; i < numSigs; i++ {
|
||||
err = writeElement(w, false, &(*e)[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case *btcec.Signature:
|
||||
sig := e.Serialize()
|
||||
sigLength := len(sig)
|
||||
if sigLength > 73 {
|
||||
return fmt.Errorf("Signature too long!")
|
||||
}
|
||||
//Write the size
|
||||
err = writeElement(w, false, uint8(sigLength))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//Write the data
|
||||
_, err = w.Write(sig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case *wire.ShaHash:
|
||||
_, err = w.Write(e[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case [20]byte:
|
||||
_, err = w.Write(e[:])
|
||||
if err != nil {
|
||||
@ -78,7 +128,7 @@ func writeElement(w io.Writer, includeSig bool, element interface{}) error {
|
||||
return fmt.Errorf("PkScript too long!")
|
||||
}
|
||||
//Write the size (1-byte)
|
||||
err = binary.Write(w, binary.BigEndian, uint8(scriptLength))
|
||||
err = writeElement(w, false, uint8(scriptLength))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -94,7 +144,7 @@ func writeElement(w io.Writer, includeSig bool, element interface{}) error {
|
||||
if len(e) > 127 {
|
||||
return fmt.Errorf("Too many txins")
|
||||
}
|
||||
err = binary.Write(w, binary.BigEndian, uint8(len(e)))
|
||||
err = writeElement(w, false, uint8(len(e)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -163,6 +213,14 @@ func readElement(r io.Reader, includeSig bool, element interface{}) error {
|
||||
}
|
||||
*e = binary.BigEndian.Uint32(b[:])
|
||||
return nil
|
||||
case *uint64:
|
||||
var b [8]byte
|
||||
_, err = io.ReadFull(r, b[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = binary.BigEndian.Uint64(b[:])
|
||||
return nil
|
||||
case *btcutil.Amount:
|
||||
var b [8]byte
|
||||
_, err = io.ReadFull(r, b[:])
|
||||
@ -171,6 +229,14 @@ func readElement(r io.Reader, includeSig bool, element interface{}) error {
|
||||
}
|
||||
*e = btcutil.Amount(int64(binary.BigEndian.Uint64(b[:])))
|
||||
return nil
|
||||
case **wire.ShaHash:
|
||||
var b wire.ShaHash
|
||||
_, err = io.ReadFull(r, b[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = &b
|
||||
return nil
|
||||
case **btcec.PublicKey:
|
||||
var b [33]byte
|
||||
_, err = io.ReadFull(r, b[:])
|
||||
@ -178,10 +244,55 @@ func readElement(r io.Reader, includeSig bool, element interface{}) error {
|
||||
return err
|
||||
}
|
||||
x, err := btcec.ParsePubKey(b[:], btcec.S256())
|
||||
*e = &*x
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = &*x
|
||||
return nil
|
||||
case **[]btcec.Signature:
|
||||
var numSigs uint8
|
||||
err = readElement(r, false, &numSigs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if numSigs > 127 {
|
||||
return fmt.Errorf("Too many signatures!")
|
||||
}
|
||||
|
||||
//Read that number of signatures
|
||||
var sigs []btcec.Signature
|
||||
for i := uint8(0); i < numSigs; i++ {
|
||||
sig := new(btcec.Signature)
|
||||
readElement(r, false, &sig)
|
||||
sigs = append(sigs, *sig)
|
||||
}
|
||||
*e = &sigs
|
||||
return nil
|
||||
case **btcec.Signature:
|
||||
var sigLength uint8
|
||||
err = readElement(r, false, &sigLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sigLength > 73 {
|
||||
return fmt.Errorf("Signature too long!")
|
||||
}
|
||||
|
||||
//Read the sig length
|
||||
l := io.LimitReader(r, int64(sigLength))
|
||||
sig, err := ioutil.ReadAll(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(sig) != int(sigLength) {
|
||||
return fmt.Errorf("EOF: Signature length mismatch.")
|
||||
}
|
||||
btcecSig, err := btcec.ParseSignature(sig, btcec.S256())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = &*btcecSig
|
||||
return nil
|
||||
case *[20]byte:
|
||||
_, err = io.ReadFull(r, e[:])
|
||||
@ -199,37 +310,40 @@ func readElement(r io.Reader, includeSig bool, element interface{}) error {
|
||||
return nil
|
||||
case *PkScript:
|
||||
//Get the script length first
|
||||
var scriptLength [1]uint8
|
||||
_, err = r.Read(scriptLength[:])
|
||||
var scriptLength uint8
|
||||
err = readElement(r, false, &scriptLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if scriptLength[0] > 25 {
|
||||
if scriptLength > 25 {
|
||||
return fmt.Errorf("PkScript too long!")
|
||||
}
|
||||
|
||||
//Read the script length
|
||||
l := io.LimitReader(r, int64(scriptLength[0]))
|
||||
*e, _ = ioutil.ReadAll(l)
|
||||
l := io.LimitReader(r, int64(scriptLength))
|
||||
*e, err = ioutil.ReadAll(l)
|
||||
if len(*e) != int(scriptLength) {
|
||||
return fmt.Errorf("EOF: Signature length mismatch.")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case *[]*wire.TxIn:
|
||||
//Read the size (1-byte number of txins)
|
||||
var numScripts [1]uint8
|
||||
_, err = r.Read(numScripts[:])
|
||||
var numScripts uint8
|
||||
err = readElement(r, false, &numScripts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if numScripts[0] > 127 {
|
||||
if numScripts > 127 {
|
||||
return fmt.Errorf("Too many txins")
|
||||
}
|
||||
|
||||
//Append the actual TxIns
|
||||
var txins []*wire.TxIn
|
||||
for i := uint8(0); i < numScripts[0]; i++ {
|
||||
for i := uint8(0); i < numScripts; i++ {
|
||||
//Hash
|
||||
var h [32]byte
|
||||
_, err = io.ReadFull(r, h[:])
|
||||
@ -281,3 +395,31 @@ func readElements(r io.Reader, includeSig bool, elements ...interface{}) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Validates whether a PkScript byte array is P2SH or P2PKH
|
||||
func ValidatePkScript(pkScript PkScript) error {
|
||||
if len(pkScript) == 25 {
|
||||
//P2PKH
|
||||
//Begins with OP_DUP OP_HASH160 PUSHDATA(20)
|
||||
if !bytes.Equal(pkScript[0:3], []byte{118, 169, 20}) ||
|
||||
//Ends with OP_EQUALVERIFY OP_CHECKSIG
|
||||
!bytes.Equal(pkScript[23:25], []byte{136, 172}) {
|
||||
//If it's not correct, return error
|
||||
return fmt.Errorf("PkScript only allows P2SH or P2PKH")
|
||||
}
|
||||
} else if len(pkScript) == 23 {
|
||||
//P2SH
|
||||
//Begins with OP_HASH160 PUSHDATA(20)
|
||||
if !bytes.Equal(pkScript[0:2], []byte{169, 20}) ||
|
||||
//Ends with OP_EQUAL
|
||||
!bytes.Equal(pkScript[22:23], []byte{135}) {
|
||||
//If it's not correct, return error
|
||||
return fmt.Errorf("PkScript only allows P2SH or P2PKH")
|
||||
}
|
||||
} else {
|
||||
//Length not 23 or 25
|
||||
return fmt.Errorf("PkScript only allows P2SH or P2PKH")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -14,7 +14,10 @@ const MessageHeaderSize = 12
|
||||
const MaxMessagePayload = 1024 * 1024 * 32 // 32MB
|
||||
|
||||
const (
|
||||
CmdFundingRequest = uint32(20)
|
||||
CmdFundingRequest = uint32(200)
|
||||
CmdFundingResponse = uint32(210)
|
||||
CmdFundingSignAccept = uint32(220)
|
||||
CmdFundingSignComplete = uint32(230)
|
||||
)
|
||||
|
||||
//Every message has these functions:
|
||||
@ -33,8 +36,14 @@ func makeEmptyMessage(command uint32) (Message, error) {
|
||||
switch command {
|
||||
case CmdFundingRequest:
|
||||
msg = &FundingRequest{}
|
||||
case CmdFundingResponse:
|
||||
msg = &FundingResponse{}
|
||||
case CmdFundingSignAccept:
|
||||
msg = &FundingSignAccept{}
|
||||
case CmdFundingSignComplete:
|
||||
msg = &FundingSignComplete{}
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled command [%x]", command)
|
||||
return nil, fmt.Errorf("unhandled command [%d]", command)
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
@ -194,7 +203,7 @@ func ReadMessage(r io.Reader, pver uint32, btcnet wire.BitcoinNet) (int, Message
|
||||
}
|
||||
|
||||
//Validate the data
|
||||
msg.Validate()
|
||||
err = msg.Validate()
|
||||
if err != nil {
|
||||
return totalBytes, nil, nil, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user