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 }