routing+migration32: update MC encoding to use pure TLV

In this commit, we update an existing migration which at the time of
writing has not been included in a release. We update it so that it
converts the format used for MissionControl result encoding to use pure
TLV instead. The 3 structs that have been updated are: `mcHop`,
`mcRoute` and `paymentResult`.
This commit is contained in:
Elle Mouton
2024-10-08 13:43:30 +02:00
parent d9a073ad7e
commit 5370c90906
7 changed files with 1612 additions and 690 deletions

View File

@@ -1,8 +1,10 @@
package routing
import (
"bytes"
"errors"
"fmt"
"io"
"sync"
"time"
@@ -16,6 +18,7 @@ import (
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/tlv"
)
const (
@@ -261,12 +264,39 @@ type MissionControlPairSnapshot struct {
// paymentResult is the information that becomes available when a payment
// attempt completes.
type paymentResult struct {
id uint64
timeFwd, timeReply time.Time
route *mcRoute
success bool
failureSourceIdx *int
failure lnwire.FailureMessage
id uint64
timeFwd tlv.RecordT[tlv.TlvType0, uint64]
timeReply tlv.RecordT[tlv.TlvType1, uint64]
route tlv.RecordT[tlv.TlvType2, mcRoute]
// failure holds information related to the failure of a payment. The
// presence of this record indicates a payment failure. The absence of
// this record indicates a successful payment.
failure tlv.OptionalRecordT[tlv.TlvType3, paymentFailure]
}
// newPaymentResult constructs a new paymentResult.
func newPaymentResult(id uint64, rt *mcRoute, timeFwd, timeReply time.Time,
failure *paymentFailure) *paymentResult {
result := &paymentResult{
id: id,
timeFwd: tlv.NewPrimitiveRecord[tlv.TlvType0](
uint64(timeFwd.UnixNano()),
),
timeReply: tlv.NewPrimitiveRecord[tlv.TlvType1](
uint64(timeReply.UnixNano()),
),
route: tlv.NewRecordT[tlv.TlvType2](*rt),
}
if failure != nil {
result.failure = tlv.SomeRecordT(
tlv.NewRecordT[tlv.TlvType3](*failure),
)
}
return result
}
// NewMissionController returns a new instance of MissionController.
@@ -590,15 +620,10 @@ func (m *MissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route,
timestamp := m.cfg.clock.Now()
result := &paymentResult{
success: false,
timeFwd: timestamp,
timeReply: timestamp,
id: paymentID,
failureSourceIdx: failureSourceIdx,
failure: failure,
route: extractMCRoute(rt),
}
result := newPaymentResult(
paymentID, extractMCRoute(rt), timestamp, timestamp,
newPaymentFailure(failureSourceIdx, failure),
)
return m.processPaymentResult(result)
}
@@ -610,15 +635,12 @@ func (m *MissionControl) ReportPaymentSuccess(paymentID uint64,
timestamp := m.cfg.clock.Now()
result := &paymentResult{
timeFwd: timestamp,
timeReply: timestamp,
id: paymentID,
success: true,
route: extractMCRoute(rt),
}
result := newPaymentResult(
paymentID, extractMCRoute(rt), timestamp, timestamp, nil,
)
_, err := m.processPaymentResult(result)
return err
}
@@ -646,14 +668,11 @@ func (m *MissionControl) applyPaymentResult(
result *paymentResult) *channeldb.FailureReason {
// Interpret result.
i := interpretResult(
result.route, result.success, result.failureSourceIdx,
result.failure,
)
i := interpretResult(&result.route.Val, result.failure.ValOpt())
if i.policyFailure != nil {
if m.state.requestSecondChance(
result.timeReply,
time.Unix(0, int64(result.timeReply.Val)),
i.policyFailure.From, i.policyFailure.To,
) {
return nil
@@ -681,7 +700,10 @@ func (m *MissionControl) applyPaymentResult(
m.log.Debugf("Reporting node failure to Mission Control: "+
"node=%v", *i.nodeFailure)
m.state.setAllFail(*i.nodeFailure, result.timeReply)
m.state.setAllFail(
*i.nodeFailure,
time.Unix(0, int64(result.timeReply.Val)),
)
}
for pair, pairResult := range i.pairResults {
@@ -698,7 +720,9 @@ func (m *MissionControl) applyPaymentResult(
}
m.state.setLastPairResult(
pair.From, pair.To, result.timeReply, &pairResult, false,
pair.From, pair.To,
time.Unix(0, int64(result.timeReply.Val)), &pairResult,
false,
)
}
@@ -803,3 +827,158 @@ func (n *namespacedDB) purge() error {
return err
}, func() {})
}
// paymentFailure represents the presence of a payment failure. It may or may
// not include additional information about said failure.
type paymentFailure struct {
info tlv.OptionalRecordT[tlv.TlvType0, paymentFailureInfo]
}
// newPaymentFailure constructs a new paymentFailure struct. If the source
// index is nil, then an empty paymentFailure is returned. This represents a
// failure with unknown details. Otherwise, the index and failure message are
// used to populate the info field of the paymentFailure.
func newPaymentFailure(sourceIdx *int,
failureMsg lnwire.FailureMessage) *paymentFailure {
if sourceIdx == nil {
return &paymentFailure{}
}
info := paymentFailureInfo{
sourceIdx: tlv.NewPrimitiveRecord[tlv.TlvType0](
uint8(*sourceIdx),
),
msg: tlv.NewRecordT[tlv.TlvType1](failureMessage{failureMsg}),
}
return &paymentFailure{
info: tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType0](info)),
}
}
// Record returns a TLV record that can be used to encode/decode a
// paymentFailure to/from a TLV stream.
func (r *paymentFailure) Record() tlv.Record {
recordSize := func() uint64 {
var (
b bytes.Buffer
buf [8]byte
)
if err := encodePaymentFailure(&b, r, &buf); err != nil {
panic(err)
}
return uint64(len(b.Bytes()))
}
return tlv.MakeDynamicRecord(
0, r, recordSize, encodePaymentFailure, decodePaymentFailure,
)
}
func encodePaymentFailure(w io.Writer, val interface{}, _ *[8]byte) error {
if v, ok := val.(*paymentFailure); ok {
var recordProducers []tlv.RecordProducer
v.info.WhenSome(
func(r tlv.RecordT[tlv.TlvType0, paymentFailureInfo]) {
recordProducers = append(recordProducers, &r)
},
)
return lnwire.EncodeRecordsTo(
w, lnwire.ProduceRecordsSorted(recordProducers...),
)
}
return tlv.NewTypeForEncodingErr(val, "routing.paymentFailure")
}
func decodePaymentFailure(r io.Reader, val interface{}, _ *[8]byte,
l uint64) error {
if v, ok := val.(*paymentFailure); ok {
var h paymentFailure
info := tlv.ZeroRecordT[tlv.TlvType0, paymentFailureInfo]()
typeMap, err := lnwire.DecodeRecords(
r, lnwire.ProduceRecordsSorted(&info)...,
)
if err != nil {
return err
}
if _, ok := typeMap[h.info.TlvType()]; ok {
h.info = tlv.SomeRecordT(info)
}
*v = h
return nil
}
return tlv.NewTypeForDecodingErr(val, "routing.paymentFailure", l, l)
}
// paymentFailureInfo holds additional information about a payment failure.
type paymentFailureInfo struct {
sourceIdx tlv.RecordT[tlv.TlvType0, uint8]
msg tlv.RecordT[tlv.TlvType1, failureMessage]
}
// Record returns a TLV record that can be used to encode/decode a
// paymentFailureInfo to/from a TLV stream.
func (r *paymentFailureInfo) Record() tlv.Record {
recordSize := func() uint64 {
var (
b bytes.Buffer
buf [8]byte
)
if err := encodePaymentFailureInfo(&b, r, &buf); err != nil {
panic(err)
}
return uint64(len(b.Bytes()))
}
return tlv.MakeDynamicRecord(
0, r, recordSize, encodePaymentFailureInfo,
decodePaymentFailureInfo,
)
}
func encodePaymentFailureInfo(w io.Writer, val interface{}, _ *[8]byte) error {
if v, ok := val.(*paymentFailureInfo); ok {
return lnwire.EncodeRecordsTo(
w, lnwire.ProduceRecordsSorted(
&v.sourceIdx, &v.msg,
),
)
}
return tlv.NewTypeForEncodingErr(val, "routing.paymentFailureInfo")
}
func decodePaymentFailureInfo(r io.Reader, val interface{}, _ *[8]byte,
l uint64) error {
if v, ok := val.(*paymentFailureInfo); ok {
var h paymentFailureInfo
_, err := lnwire.DecodeRecords(
r,
lnwire.ProduceRecordsSorted(&h.sourceIdx, &h.msg)...,
)
if err != nil {
return err
}
*v = h
return nil
}
return tlv.NewTypeForDecodingErr(
val, "routing.paymentFailureInfo", l, l,
)
}