diff --git a/lnwire/ESCROWED_HTLC_NOTES b/lnwire/ESCROWED_HTLC_NOTES index e3e6aafc..e7978446 100644 --- a/lnwire/ESCROWED_HTLC_NOTES +++ b/lnwire/ESCROWED_HTLC_NOTES @@ -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] diff --git a/lnwire/FLOW.md b/lnwire/FLOW.md new file mode 100644 index 00000000..6c6071b8 --- /dev/null +++ b/lnwire/FLOW.md @@ -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. diff --git a/lnwire/funding_request.go b/lnwire/funding_request.go index 4396258b..6e540e84 100644 --- a/lnwire/funding_request.go +++ b/lnwire/funding_request.go @@ -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! diff --git a/lnwire/funding_request_test.go b/lnwire/funding_request_test.go index 6292bafa..defb9a53 100644 --- a/lnwire/funding_request_test.go +++ b/lnwire/funding_request_test.go @@ -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()) } diff --git a/lnwire/funding_response.go b/lnwire/funding_response.go new file mode 100644 index 00000000..aee38580 --- /dev/null +++ b/lnwire/funding_response.go @@ -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") +} diff --git a/lnwire/funding_response_test.go b/lnwire/funding_response_test.go new file mode 100644 index 00000000..b754c68e --- /dev/null +++ b/lnwire/funding_response_test.go @@ -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()) + } +} diff --git a/lnwire/funding_signaccept.go b/lnwire/funding_signaccept.go new file mode 100644 index 00000000..32c03520 --- /dev/null +++ b/lnwire/funding_signaccept.go @@ -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") +} diff --git a/lnwire/funding_signaccept_test.go b/lnwire/funding_signaccept_test.go new file mode 100644 index 00000000..0f68cbd9 --- /dev/null +++ b/lnwire/funding_signaccept_test.go @@ -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()) + } +} diff --git a/lnwire/funding_signcomplete.go b/lnwire/funding_signcomplete.go new file mode 100644 index 00000000..b45c96fa --- /dev/null +++ b/lnwire/funding_signcomplete.go @@ -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") +} diff --git a/lnwire/funding_signcomplete_test.go b/lnwire/funding_signcomplete_test.go new file mode 100644 index 00000000..07ab97d0 --- /dev/null +++ b/lnwire/funding_signcomplete_test.go @@ -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()) + } +} diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index 5f9273e4..a8470636 100644 --- a/lnwire/lnwire.go +++ b/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 +} diff --git a/lnwire/message.go b/lnwire/message.go index e88d901d..5082e8d8 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -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 }