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
|
authorize payment in most cases where there is cooperation, escrow is only
|
||||||
contacted if there is non-cooperation.
|
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
|
17 (00010001): 1-of-1
|
||||||
34 (00100010): 2-of-2
|
34 (00100010): 2-of-2
|
||||||
35 (00100011): 2-of-3 [with Recipient being 1 of the two N parties]
|
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
|
package lnwire
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
@ -19,7 +18,7 @@ type FundingRequest struct {
|
|||||||
//Should double-check the total funding later
|
//Should double-check the total funding later
|
||||||
MinTotalFundingAmount btcutil.Amount
|
MinTotalFundingAmount btcutil.Amount
|
||||||
|
|
||||||
//CLTV lock-time to use
|
//CLTV/CSV lock-time to use
|
||||||
LockTime uint32
|
LockTime uint32
|
||||||
|
|
||||||
//Who pays the fees
|
//Who pays the fees
|
||||||
@ -31,14 +30,13 @@ type FundingRequest struct {
|
|||||||
RevocationHash [20]byte
|
RevocationHash [20]byte
|
||||||
Pubkey *btcec.PublicKey
|
Pubkey *btcec.PublicKey
|
||||||
DeliveryPkScript PkScript //*MUST* be either P2PKH or P2SH
|
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
|
Inputs []*wire.TxIn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FundingRequest) Decode(r io.Reader, pver uint32) error {
|
func (c *FundingRequest) Decode(r io.Reader, pver uint32) error {
|
||||||
//Channel Type (0/1)
|
//Channel Type (0/1)
|
||||||
// default to 0 for CLTV-only
|
|
||||||
//Funding Amount (1/8)
|
//Funding Amount (1/8)
|
||||||
//Channel Minimum Capacity (9/8)
|
//Channel Minimum Capacity (9/8)
|
||||||
//Revocation Hash (17/20)
|
//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)
|
//Minimum Transaction Fee Per Kb (77/8)
|
||||||
//LockTime (85/4)
|
//LockTime (85/4)
|
||||||
//FeePayer (89/1)
|
//FeePayer (89/1)
|
||||||
//DeliveryPkScript
|
//DeliveryPkScript (final delivery)
|
||||||
|
// First byte length then pkscript
|
||||||
|
//ChangePkScript (change for extra from inputs)
|
||||||
// First byte length then pkscript
|
// First byte length then pkscript
|
||||||
//Inputs: Create the TxIns
|
//Inputs: Create the TxIns
|
||||||
// First byte is number of inputs
|
// First byte is number of inputs
|
||||||
@ -63,12 +63,13 @@ func (c *FundingRequest) Decode(r io.Reader, pver uint32) error {
|
|||||||
&c.LockTime,
|
&c.LockTime,
|
||||||
&c.FeePayer,
|
&c.FeePayer,
|
||||||
&c.DeliveryPkScript,
|
&c.DeliveryPkScript,
|
||||||
|
&c.ChangePkScript,
|
||||||
&c.Inputs)
|
&c.Inputs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Validate()
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Creates a new FundingRequest
|
//Creates a new FundingRequest
|
||||||
@ -89,6 +90,7 @@ func (c *FundingRequest) Encode(w io.Writer, pver uint32) error {
|
|||||||
//LockTime
|
//LockTime
|
||||||
//FeePayer
|
//FeePayer
|
||||||
//DeliveryPkScript
|
//DeliveryPkScript
|
||||||
|
//ChangePkScript
|
||||||
//Inputs: Append the actual Txins
|
//Inputs: Append the actual Txins
|
||||||
err := writeElements(w, false,
|
err := writeElements(w, false,
|
||||||
c.ChannelType,
|
c.ChannelType,
|
||||||
@ -101,6 +103,7 @@ func (c *FundingRequest) Encode(w io.Writer, pver uint32) error {
|
|||||||
c.LockTime,
|
c.LockTime,
|
||||||
c.FeePayer,
|
c.FeePayer,
|
||||||
c.DeliveryPkScript,
|
c.DeliveryPkScript,
|
||||||
|
c.ChangePkScript,
|
||||||
c.Inputs)
|
c.Inputs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -114,13 +117,14 @@ func (c *FundingRequest) Command() uint32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *FundingRequest) MaxPayloadLength(uint32) uint32 {
|
func (c *FundingRequest) MaxPayloadLength(uint32) uint32 {
|
||||||
//90 (base size) + 26 (pkscript) + 1 (numTxes) + 127*36(127 inputs * sha256+idx)
|
//90 (base size) + 26 (pkscript) + 26 (pkscript) + 1 (numTxes) + 127*36(127 inputs * sha256+idx)
|
||||||
//4690
|
return 4715
|
||||||
return 4689
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Makes sure the struct data is valid (e.g. no negatives or invalid pkscripts)
|
//Makes sure the struct data is valid (e.g. no negatives or invalid pkscripts)
|
||||||
func (c *FundingRequest) Validate() error {
|
func (c *FundingRequest) Validate() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
//No negative values
|
//No negative values
|
||||||
if c.FundingAmount < 0 {
|
if c.FundingAmount < 0 {
|
||||||
return fmt.Errorf("FundingAmount cannot be negative")
|
return fmt.Errorf("FundingAmount cannot be negative")
|
||||||
@ -137,28 +141,16 @@ func (c *FundingRequest) Validate() error {
|
|||||||
return fmt.Errorf("MinTotalFundingAmount cannot be negative")
|
return fmt.Errorf("MinTotalFundingAmount cannot be negative")
|
||||||
}
|
}
|
||||||
|
|
||||||
//PkScript is either P2SH or P2PKH
|
//DeliveryPkScript is either P2SH or P2PKH
|
||||||
if len(c.DeliveryPkScript) == 25 {
|
err = ValidatePkScript(c.DeliveryPkScript)
|
||||||
//P2PKH
|
if err != nil {
|
||||||
//Begins with OP_DUP OP_HASH160 PUSHDATA(20)
|
return err
|
||||||
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}) {
|
//ChangePkScript is either P2SH or P2PKH
|
||||||
//If it's not correct, return error
|
err = ValidatePkScript(c.ChangePkScript)
|
||||||
return fmt.Errorf("PkScript only allows P2SH or P2PKH")
|
if err != nil {
|
||||||
}
|
return err
|
||||||
} 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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//We're good!
|
//We're good!
|
||||||
|
@ -33,6 +33,11 @@ var (
|
|||||||
//PKhash: n2fkWVphUzw3zSigzPsv9GuDyg9mohzKpz
|
//PKhash: n2fkWVphUzw3zSigzPsv9GuDyg9mohzKpz
|
||||||
deliveryPkScript, _ = hex.DecodeString("76a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac")
|
deliveryPkScript, _ = hex.DecodeString("76a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac")
|
||||||
|
|
||||||
|
// Change PkScript
|
||||||
|
//Privkey: 5b18f5049efd9d3aff1fb9a06506c0b809fb71562b6ecd02f6c5b3ab298f3b0f
|
||||||
|
//PKhash: miky84cHvLuk6jcT6GsSbgHR8d7eZCu9Qc
|
||||||
|
changePkScript, _ = hex.DecodeString("76a914238ee44bb5c8c1314dd03974a17ec6c406fdcb8388ac")
|
||||||
|
|
||||||
//echo -n | openssl sha256
|
//echo -n | openssl sha256
|
||||||
//This stuff gets reversed!!!
|
//This stuff gets reversed!!!
|
||||||
shaHash1Bytes, _ = hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
|
shaHash1Bytes, _ = hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
|
||||||
@ -58,10 +63,11 @@ var (
|
|||||||
RevocationHash: revocationHash,
|
RevocationHash: revocationHash,
|
||||||
Pubkey: pubKey,
|
Pubkey: pubKey,
|
||||||
DeliveryPkScript: deliveryPkScript,
|
DeliveryPkScript: deliveryPkScript,
|
||||||
|
ChangePkScript: changePkScript,
|
||||||
Inputs: inputs,
|
Inputs: inputs,
|
||||||
}
|
}
|
||||||
serializedString = "000000000005f5e1000000000008f0d1804132b6b48371f7b022a16eacb9b2b0ebee134d4102f977808cb9577897582d7524b562691e180953dd0008eb44e09594c539d6daee00000000000200000000000000004e20000010e0001976a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac02e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8550000000001ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b00000001"
|
serializedString = "000000000005f5e1000000000008f0d1804132b6b48371f7b022a16eacb9b2b0ebee134d4102f977808cb9577897582d7524b562691e180953dd0008eb44e09594c539d6daee00000000000200000000000000004e20000010e0001976a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac1976a914238ee44bb5c8c1314dd03974a17ec6c406fdcb8388ac02e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8550000000001ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b00000001"
|
||||||
serializedMessage = "0709110b00000014000000be000000000005f5e1000000000008f0d1804132b6b48371f7b022a16eacb9b2b0ebee134d4102f977808cb9577897582d7524b562691e180953dd0008eb44e09594c539d6daee00000000000200000000000000004e20000010e0001976a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac02e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8550000000001ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b00000001"
|
serializedMessage = "0709110b000000c8000000d8000000000005f5e1000000000008f0d1804132b6b48371f7b022a16eacb9b2b0ebee134d4102f977808cb9577897582d7524b562691e180953dd0008eb44e09594c539d6daee00000000000200000000000000004e20000010e0001976a914e8048c0fb75bdecc91ebfb99c174f4ece29ffbd488ac1976a914238ee44bb5c8c1314dd03974a17ec6c406fdcb8388ac02e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8550000000001ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b00000001"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFundingRequestEncodeDecode(t *testing.T) {
|
func TestFundingRequestEncodeDecode(t *testing.T) {
|
||||||
@ -71,19 +77,20 @@ func TestFundingRequestEncodeDecode(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("Serialization error")
|
t.Error("Serialization error")
|
||||||
t.Error(err.Error())
|
t.Error(err.Error())
|
||||||
}
|
} else {
|
||||||
t.Logf("Encoded Funding Request: %x\n", b.Bytes())
|
t.Logf("Encoded Funding Request: %x\n", b.Bytes())
|
||||||
//Check if we serialized correctly
|
//Check if we serialized correctly
|
||||||
if serializedString != hex.EncodeToString(b.Bytes()) {
|
if serializedString != hex.EncodeToString(b.Bytes()) {
|
||||||
t.Error("Serialization does not match expected")
|
t.Error("Serialization does not match expected")
|
||||||
}
|
}
|
||||||
|
|
||||||
//So I can do: hexdump -C /dev/shm/fundingRequest.raw
|
//So I can do: hexdump -C /dev/shm/fundingRequest.raw
|
||||||
if WRITE_FILE {
|
if WRITE_FILE {
|
||||||
err = ioutil.WriteFile(FILENAME, b.Bytes(), 0644)
|
err = ioutil.WriteFile(FILENAME, b.Bytes(), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("File write error")
|
t.Error("File write error")
|
||||||
t.Error(err.Error())
|
t.Error(err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,14 +104,14 @@ func TestFundingRequestEncodeDecode(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("Decoding Error")
|
t.Error("Decoding Error")
|
||||||
t.Error(err.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
|
//Test message using Message interface
|
||||||
//Serialize/Encode
|
//Serialize/Encode
|
||||||
b = new(bytes.Buffer)
|
b = new(bytes.Buffer)
|
||||||
@ -119,9 +126,10 @@ func TestFundingRequestEncodeDecode(t *testing.T) {
|
|||||||
_, msg, _, err := ReadMessage(c, uint32(1), wire.TestNet3)
|
_, msg, _, err := ReadMessage(c, uint32(1), wire.TestNet3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf(err.Error())
|
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
|
package lnwire
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
@ -39,6 +40,14 @@ func writeElement(w io.Writer, includeSig bool, element interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
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:
|
case btcutil.Amount:
|
||||||
err = binary.Write(w, binary.BigEndian, int64(e))
|
err = binary.Write(w, binary.BigEndian, int64(e))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -57,6 +66,47 @@ func writeElement(w io.Writer, includeSig bool, element interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
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:
|
case [20]byte:
|
||||||
_, err = w.Write(e[:])
|
_, err = w.Write(e[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -78,7 +128,7 @@ func writeElement(w io.Writer, includeSig bool, element interface{}) error {
|
|||||||
return fmt.Errorf("PkScript too long!")
|
return fmt.Errorf("PkScript too long!")
|
||||||
}
|
}
|
||||||
//Write the size (1-byte)
|
//Write the size (1-byte)
|
||||||
err = binary.Write(w, binary.BigEndian, uint8(scriptLength))
|
err = writeElement(w, false, uint8(scriptLength))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -94,7 +144,7 @@ func writeElement(w io.Writer, includeSig bool, element interface{}) error {
|
|||||||
if len(e) > 127 {
|
if len(e) > 127 {
|
||||||
return fmt.Errorf("Too many txins")
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -163,6 +213,14 @@ func readElement(r io.Reader, includeSig bool, element interface{}) error {
|
|||||||
}
|
}
|
||||||
*e = binary.BigEndian.Uint32(b[:])
|
*e = binary.BigEndian.Uint32(b[:])
|
||||||
return nil
|
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:
|
case *btcutil.Amount:
|
||||||
var b [8]byte
|
var b [8]byte
|
||||||
_, err = io.ReadFull(r, b[:])
|
_, 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[:])))
|
*e = btcutil.Amount(int64(binary.BigEndian.Uint64(b[:])))
|
||||||
return nil
|
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:
|
case **btcec.PublicKey:
|
||||||
var b [33]byte
|
var b [33]byte
|
||||||
_, err = io.ReadFull(r, b[:])
|
_, err = io.ReadFull(r, b[:])
|
||||||
@ -178,10 +244,55 @@ func readElement(r io.Reader, includeSig bool, element interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
x, err := btcec.ParsePubKey(b[:], btcec.S256())
|
x, err := btcec.ParsePubKey(b[:], btcec.S256())
|
||||||
*e = &*x
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
return nil
|
||||||
case *[20]byte:
|
case *[20]byte:
|
||||||
_, err = io.ReadFull(r, e[:])
|
_, err = io.ReadFull(r, e[:])
|
||||||
@ -199,37 +310,40 @@ func readElement(r io.Reader, includeSig bool, element interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
case *PkScript:
|
case *PkScript:
|
||||||
//Get the script length first
|
//Get the script length first
|
||||||
var scriptLength [1]uint8
|
var scriptLength uint8
|
||||||
_, err = r.Read(scriptLength[:])
|
err = readElement(r, false, &scriptLength)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if scriptLength[0] > 25 {
|
if scriptLength > 25 {
|
||||||
return fmt.Errorf("PkScript too long!")
|
return fmt.Errorf("PkScript too long!")
|
||||||
}
|
}
|
||||||
|
|
||||||
//Read the script length
|
//Read the script length
|
||||||
l := io.LimitReader(r, int64(scriptLength[0]))
|
l := io.LimitReader(r, int64(scriptLength))
|
||||||
*e, _ = ioutil.ReadAll(l)
|
*e, err = ioutil.ReadAll(l)
|
||||||
|
if len(*e) != int(scriptLength) {
|
||||||
|
return fmt.Errorf("EOF: Signature length mismatch.")
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case *[]*wire.TxIn:
|
case *[]*wire.TxIn:
|
||||||
//Read the size (1-byte number of txins)
|
//Read the size (1-byte number of txins)
|
||||||
var numScripts [1]uint8
|
var numScripts uint8
|
||||||
_, err = r.Read(numScripts[:])
|
err = readElement(r, false, &numScripts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if numScripts[0] > 127 {
|
if numScripts > 127 {
|
||||||
return fmt.Errorf("Too many txins")
|
return fmt.Errorf("Too many txins")
|
||||||
}
|
}
|
||||||
|
|
||||||
//Append the actual TxIns
|
//Append the actual TxIns
|
||||||
var txins []*wire.TxIn
|
var txins []*wire.TxIn
|
||||||
for i := uint8(0); i < numScripts[0]; i++ {
|
for i := uint8(0); i < numScripts; i++ {
|
||||||
//Hash
|
//Hash
|
||||||
var h [32]byte
|
var h [32]byte
|
||||||
_, err = io.ReadFull(r, h[:])
|
_, err = io.ReadFull(r, h[:])
|
||||||
@ -281,3 +395,31 @@ func readElements(r io.Reader, includeSig bool, elements ...interface{}) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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 MaxMessagePayload = 1024 * 1024 * 32 // 32MB
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CmdFundingRequest = uint32(20)
|
CmdFundingRequest = uint32(200)
|
||||||
|
CmdFundingResponse = uint32(210)
|
||||||
|
CmdFundingSignAccept = uint32(220)
|
||||||
|
CmdFundingSignComplete = uint32(230)
|
||||||
)
|
)
|
||||||
|
|
||||||
//Every message has these functions:
|
//Every message has these functions:
|
||||||
@ -33,8 +36,14 @@ func makeEmptyMessage(command uint32) (Message, error) {
|
|||||||
switch command {
|
switch command {
|
||||||
case CmdFundingRequest:
|
case CmdFundingRequest:
|
||||||
msg = &FundingRequest{}
|
msg = &FundingRequest{}
|
||||||
|
case CmdFundingResponse:
|
||||||
|
msg = &FundingResponse{}
|
||||||
|
case CmdFundingSignAccept:
|
||||||
|
msg = &FundingSignAccept{}
|
||||||
|
case CmdFundingSignComplete:
|
||||||
|
msg = &FundingSignComplete{}
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unhandled command [%x]", command)
|
return nil, fmt.Errorf("unhandled command [%d]", command)
|
||||||
}
|
}
|
||||||
|
|
||||||
return msg, nil
|
return msg, nil
|
||||||
@ -194,7 +203,7 @@ func ReadMessage(r io.Reader, pver uint32, btcnet wire.BitcoinNet) (int, Message
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Validate the data
|
//Validate the data
|
||||||
msg.Validate()
|
err = msg.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return totalBytes, nil, nil, err
|
return totalBytes, nil, nil, err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user