package lnwire import ( "fmt" "io" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcutil" ) type FundingRequest struct { ReservationID uint64 ChannelType uint8 RequesterFundingAmount btcutil.Amount RequesterReserveAmount btcutil.Amount MinFeePerKb btcutil.Amount // The funding requester can request payment // This wallet only allows positive values, // which is a payment to the responder // (This can be used to fund the Reserve) // If the responder disagrees, then the funding request fails // THIS VALUE GOES INTO THE RESPONDER'S FUNDING AMOUNT // total requester input value = RequesterFundingAmount + PaymentAmount + "Total Change" + Fees(?) // RequesterFundingAmount = "Available Balance" + RequesterReserveAmount // Payment SHOULD NOT be acknowledged until the minimum confirmation has elapsed // (Due to double-spend risks the recipient will not want to acknolwedge confirmation until later) // This is to make a payment as part of opening the channel PaymentAmount btcutil.Amount // Minimum number of confirmations to validate transaction MinDepth uint32 // Should double-check the total funding later MinTotalFundingAmount btcutil.Amount // 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 DeliveryPkScript PkScript // *MUST* be either P2PKH or P2SH ChangePkScript PkScript // *MUST* be either P2PKH or P2SH Inputs []*wire.TxIn } func (c *FundingRequest) Decode(r io.Reader, pver uint32) error { // Reservation ID (8) // Channel Type (1) // Funding Amount (8) // Channel Minimum Capacity (8) // Revocation Hash (20) // Commitment Pubkey (32) // Reserve Amount (8) // Minimum Transaction Fee Per Kb (8) // PaymentAmount (8) // MinDepth (4) // LockTime (4) // FeePayer (1) // 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 // For each input, it's 32bytes txin & 4bytes index err := readElements(r, &c.ReservationID, &c.ChannelType, &c.RequesterFundingAmount, &c.MinTotalFundingAmount, &c.RevocationHash, &c.Pubkey, &c.RequesterReserveAmount, &c.MinFeePerKb, &c.PaymentAmount, &c.MinDepth, &c.LockTime, &c.FeePayer, &c.DeliveryPkScript, &c.ChangePkScript, &c.Inputs) if err != nil { return err } return nil } // Creates a new FundingRequest func NewFundingRequest() *FundingRequest { return &FundingRequest{} } // Serializes the item from the FundingRequest struct // Writes the data to w func (c *FundingRequest) Encode(w io.Writer, pver uint32) error { // Channel Type // Funding Amont // Channel Minimum Capacity // Revocation Hash // Commitment Pubkey // Reserve Amount // Minimum Transaction Fee Per KB // LockTime // FeePayer // DeliveryPkScript // ChangePkScript // Inputs: Append the actual Txins err := writeElements(w, c.ReservationID, c.ChannelType, c.RequesterFundingAmount, c.MinTotalFundingAmount, c.RevocationHash, c.Pubkey, c.RequesterReserveAmount, c.MinFeePerKb, c.PaymentAmount, c.MinDepth, c.LockTime, c.FeePayer, c.DeliveryPkScript, c.ChangePkScript, c.Inputs) if err != nil { return err } return nil } func (c *FundingRequest) Command() uint32 { return CmdFundingRequest } func (c *FundingRequest) MaxPayloadLength(uint32) uint32 { // 110 (base size) + 26 (pkscript) + 26 (pkscript) + 1 (numTxes) + 127*36(127 inputs * sha256+idx) return 4735 } // Makes sure the struct data is valid (e.g. no negatives or invalid pkscripts) func (c *FundingRequest) Validate() error { // No negative values if c.RequesterFundingAmount < 0 { return fmt.Errorf("RequesterFundingAmount cannot be negative") } if c.RequesterReserveAmount < 0 { return fmt.Errorf("RequesterReserveAmount cannot be negative") } if c.MinFeePerKb < 0 { return fmt.Errorf("MinFeePerKb cannot be negative") } if c.MinTotalFundingAmount < 0 { return fmt.Errorf("MinTotalFundingAmount cannot be negative") } // Validation of what makes sense... if c.MinTotalFundingAmount < c.RequesterFundingAmount { return fmt.Errorf("Requester's minimum too low.") } if c.RequesterFundingAmount < c.RequesterReserveAmount { return fmt.Errorf("Reserve must be below Funding Amount") } // This wallet only allows payment from the requester to responder if c.PaymentAmount < 0 { return fmt.Errorf("This wallet requieres payment to be greater than zero.") } // Make sure there's not more than 127 inputs if len(c.Inputs) > 127 { return fmt.Errorf("Too many inputs") } // DeliveryPkScript is either P2SH or P2PKH if !isValidPkScript(c.DeliveryPkScript) { // TODO(roasbeef): move into actual error return fmt.Errorf("Valid delivery public key scripts MUST be: " + "P2PKH, P2WKH, P2SH, or P2WSH.") } // ChangePkScript is either P2SH or P2PKH if !isValidPkScript(c.ChangePkScript) { return fmt.Errorf("Valid change public key script MUST be: " + "P2PKH, P2WKH, P2SH, or P2WSH.") } // We're good! return nil } func (c *FundingRequest) String() string { var inputs string for i, in := range c.Inputs { inputs += fmt.Sprintf("\n Slice\t%d\n", i) if &in != nil { inputs += fmt.Sprintf("\tHash\t%s\n", in.PreviousOutPoint.Hash) inputs += fmt.Sprintf("\tIndex\t%d\n", in.PreviousOutPoint.Index) } } var serializedPubkey []byte if &c.Pubkey != nil && c.Pubkey.X != nil { serializedPubkey = c.Pubkey.SerializeCompressed() } return fmt.Sprintf("\n--- Begin FundingRequest ---\n") + fmt.Sprintf("ReservationID:\t\t\t%d\n", c.ReservationID) + fmt.Sprintf("ChannelType:\t\t\t%x\n", c.ChannelType) + fmt.Sprintf("RequesterFundingAmount:\t\t%s\n", c.RequesterFundingAmount.String()) + fmt.Sprintf("RequesterReserveAmount:\t\t%s\n", c.RequesterReserveAmount.String()) + fmt.Sprintf("MinFeePerKb:\t\t\t%s\n", c.MinFeePerKb.String()) + fmt.Sprintf("PaymentAmount:\t\t\t%s\n", c.PaymentAmount.String()) + fmt.Sprintf("MinDepth:\t\t\t%d\n", c.MinDepth) + fmt.Sprintf("MinTotalFundingAmount\t\t%s\n", c.MinTotalFundingAmount.String()) + fmt.Sprintf("LockTime\t\t\t%d\n", c.LockTime) + fmt.Sprintf("FeePayer\t\t\t%x\n", c.FeePayer) + fmt.Sprintf("RevocationHash\t\t\t%x\n", c.RevocationHash) + fmt.Sprintf("Pubkey\t\t\t\t%x\n", serializedPubkey) + fmt.Sprintf("DeliveryPkScript\t\t%x\n", c.DeliveryPkScript) + fmt.Sprintf("Inputs:") + inputs + fmt.Sprintf("--- End FundingRequest ---\n") }