github.com/decred/dcrlnd@v0.7.6/record/mpp.go (about)

     1  package record
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  
     7  	"github.com/decred/dcrlnd/lnwire"
     8  	"github.com/decred/dcrlnd/tlv"
     9  )
    10  
    11  // MPPOnionType is the type used in the onion to reference the MPP fields:
    12  // total_amt and payment_addr.
    13  const MPPOnionType tlv.Type = 8
    14  
    15  // MPP is a record that encodes the fields necessary for multi-path payments.
    16  type MPP struct {
    17  	// paymentAddr is a random, receiver-generated value used to avoid
    18  	// collisions with concurrent payers.
    19  	paymentAddr [32]byte
    20  
    21  	// totalMAtoms is the total value of the payment, potentially spread
    22  	// across more than one HTLC.
    23  	totalMAtoms lnwire.MilliAtom
    24  }
    25  
    26  // NewMPP generates a new MPP record with the given total and payment address.
    27  func NewMPP(total lnwire.MilliAtom, addr [32]byte) *MPP {
    28  	return &MPP{
    29  		paymentAddr: addr,
    30  		totalMAtoms: total,
    31  	}
    32  }
    33  
    34  // PaymentAddr returns the payment address contained in the MPP record.
    35  func (r *MPP) PaymentAddr() [32]byte {
    36  	return r.paymentAddr
    37  }
    38  
    39  // TotalMAtoms returns the total value of an MPP payment in matoms.
    40  func (r *MPP) TotalMAtoms() lnwire.MilliAtom {
    41  	return r.totalMAtoms
    42  }
    43  
    44  // MPPEncoder writes the MPP record to the provided io.Writer.
    45  func MPPEncoder(w io.Writer, val interface{}, buf *[8]byte) error {
    46  	if v, ok := val.(*MPP); ok {
    47  		err := tlv.EBytes32(w, &v.paymentAddr, buf)
    48  		if err != nil {
    49  			return err
    50  		}
    51  
    52  		return tlv.ETUint64T(w, uint64(v.totalMAtoms), buf)
    53  	}
    54  	return tlv.NewTypeForEncodingErr(val, "MPP")
    55  }
    56  
    57  const (
    58  	// minMPPLength is the minimum length of a serialized MPP TLV record,
    59  	// which occurs when the truncated encoding of total_amt_msat takes 0
    60  	// bytes, leaving only the payment_addr.
    61  	minMPPLength = 32
    62  
    63  	// maxMPPLength is the maximum length of a serialized MPP TLV record,
    64  	// which occurs when the truncated encoding of total_amt_msat takes 8
    65  	// bytes.
    66  	maxMPPLength = 40
    67  )
    68  
    69  // MPPDecoder reads the MPP record to the provided io.Reader.
    70  func MPPDecoder(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
    71  	if v, ok := val.(*MPP); ok && minMPPLength <= l && l <= maxMPPLength {
    72  		if err := tlv.DBytes32(r, &v.paymentAddr, buf, 32); err != nil {
    73  			return err
    74  		}
    75  
    76  		var total uint64
    77  		if err := tlv.DTUint64(r, &total, buf, l-32); err != nil {
    78  			return err
    79  		}
    80  		v.totalMAtoms = lnwire.MilliAtom(total)
    81  
    82  		return nil
    83  
    84  	}
    85  	return tlv.NewTypeForDecodingErr(val, "MPP", l, maxMPPLength)
    86  }
    87  
    88  // Record returns a tlv.Record that can be used to encode or decode this record.
    89  func (r *MPP) Record() tlv.Record {
    90  	// Fixed-size, 32 byte payment address followed by truncated 64-bit
    91  	// total msat.
    92  	size := func() uint64 {
    93  		return 32 + tlv.SizeTUint64(uint64(r.totalMAtoms))
    94  	}
    95  
    96  	return tlv.MakeDynamicRecord(
    97  		MPPOnionType, r, size, MPPEncoder, MPPDecoder,
    98  	)
    99  }
   100  
   101  // PayloadSize returns the size this record takes up in encoded form.
   102  func (r *MPP) PayloadSize() uint64 {
   103  	return 32 + tlv.SizeTUint64(uint64(r.totalMAtoms))
   104  }
   105  
   106  // String returns a human-readable representation of the mpp payload field.
   107  func (r *MPP) String() string {
   108  	if r == nil {
   109  		return "<nil>"
   110  	}
   111  
   112  	return fmt.Sprintf("total=%v, addr=%x", r.totalMAtoms, r.paymentAddr)
   113  }