Merge pull request #9770 from bitromortac/2504-mc-migration-fix
routing+migration: fix mission control migration with nil failure msg
This commit is contained in:
7 files changed
+234
-210
No files matched your search
@@ -118,6 +118,32 @@ var (
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resultOld3 = paymentResultOld{
|
||||||
|
id: 3,
|
||||||
|
timeFwd: time.Unix(0, 4),
|
||||||
|
timeReply: time.Unix(0, 7),
|
||||||
|
success: false,
|
||||||
|
failure: nil,
|
||||||
|
failureSourceIdx: &failureIndex,
|
||||||
|
route: &Route{
|
||||||
|
TotalTimeLock: 101,
|
||||||
|
TotalAmount: 401,
|
||||||
|
SourcePubKey: testPub2,
|
||||||
|
Hops: []*Hop{
|
||||||
|
{
|
||||||
|
PubKeyBytes: testPub,
|
||||||
|
ChannelID: 800,
|
||||||
|
OutgoingTimeLock: 4,
|
||||||
|
AmtToForward: 4,
|
||||||
|
BlindingPoint: pubkey,
|
||||||
|
EncryptedData: []byte{1, 2, 3},
|
||||||
|
CustomRecords: customRecord,
|
||||||
|
TotalAmtMsat: 600,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:ll
|
//nolint:ll
|
||||||
resultNew1Hop1 = &mcHop{
|
resultNew1Hop1 = &mcHop{
|
||||||
channelID: tlv.NewPrimitiveRecord[tlv.TlvType0, uint64](100),
|
channelID: tlv.NewPrimitiveRecord[tlv.TlvType0, uint64](100),
|
||||||
@@ -182,7 +208,7 @@ var (
|
|||||||
),
|
),
|
||||||
failure: tlv.SomeRecordT(
|
failure: tlv.SomeRecordT(
|
||||||
tlv.NewRecordT[tlv.TlvType3](
|
tlv.NewRecordT[tlv.TlvType3](
|
||||||
*newPaymentFailure(
|
newPaymentFailure(
|
||||||
&failureIndex,
|
&failureIndex,
|
||||||
&lnwire.FailFeeInsufficient{},
|
&lnwire.FailFeeInsufficient{},
|
||||||
),
|
),
|
||||||
@@ -217,6 +243,31 @@ var (
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:ll
|
||||||
|
resultNew3 = paymentResultNew{
|
||||||
|
id: 3,
|
||||||
|
timeFwd: tlv.NewPrimitiveRecord[tlv.TlvType0, uint64](
|
||||||
|
uint64(time.Unix(0, 4).UnixNano()),
|
||||||
|
),
|
||||||
|
timeReply: tlv.NewPrimitiveRecord[tlv.TlvType1, uint64](
|
||||||
|
uint64(time.Unix(0, 7).UnixNano()),
|
||||||
|
),
|
||||||
|
failure: tlv.SomeRecordT(
|
||||||
|
tlv.NewRecordT[tlv.TlvType3](
|
||||||
|
newPaymentFailure(
|
||||||
|
&failureIndex, nil,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
route: tlv.NewRecordT[tlv.TlvType2](mcRoute{
|
||||||
|
sourcePubKey: tlv.NewRecordT[tlv.TlvType0](testPub2),
|
||||||
|
totalAmount: tlv.NewRecordT[tlv.TlvType1, lnwire.MilliSatoshi](401),
|
||||||
|
hops: tlv.NewRecordT[tlv.TlvType2](mcHops{
|
||||||
|
resultNew2Hop1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestMigrateMCRouteSerialisation tests that the MigrateMCRouteSerialisation
|
// TestMigrateMCRouteSerialisation tests that the MigrateMCRouteSerialisation
|
||||||
@@ -225,10 +276,10 @@ var (
|
|||||||
func TestMigrateMCRouteSerialisation(t *testing.T) {
|
func TestMigrateMCRouteSerialisation(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
resultsOld = []*paymentResultOld{
|
resultsOld = []*paymentResultOld{
|
||||||
&resultOld1, &resultOld2,
|
&resultOld1, &resultOld2, &resultOld3,
|
||||||
}
|
}
|
||||||
expectedResultsNew = []*paymentResultNew{
|
expectedResultsNew = []*paymentResultNew{
|
||||||
&resultNew1, &resultNew2,
|
&resultNew1, &resultNew2, &resultNew3,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -93,9 +93,12 @@ func deserializeOldResult(k, v []byte) (*paymentResultOld, error) {
|
|||||||
|
|
||||||
// convertPaymentResult converts a paymentResultOld to a paymentResultNew.
|
// convertPaymentResult converts a paymentResultOld to a paymentResultNew.
|
||||||
func convertPaymentResult(old *paymentResultOld) *paymentResultNew {
|
func convertPaymentResult(old *paymentResultOld) *paymentResultNew {
|
||||||
var failure *paymentFailure
|
var failure fn.Option[paymentFailure]
|
||||||
if !old.success {
|
if !old.success {
|
||||||
failure = newPaymentFailure(old.failureSourceIdx, old.failure)
|
failure = fn.Some(newPaymentFailure(
|
||||||
|
old.failureSourceIdx,
|
||||||
|
old.failure,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
return newPaymentResult(
|
return newPaymentResult(
|
||||||
@@ -106,7 +109,7 @@ func convertPaymentResult(old *paymentResultOld) *paymentResultNew {
|
|||||||
|
|
||||||
// newPaymentResult constructs a new paymentResult.
|
// newPaymentResult constructs a new paymentResult.
|
||||||
func newPaymentResult(id uint64, rt *mcRoute, timeFwd, timeReply time.Time,
|
func newPaymentResult(id uint64, rt *mcRoute, timeFwd, timeReply time.Time,
|
||||||
failure *paymentFailure) *paymentResultNew {
|
failure fn.Option[paymentFailure]) *paymentResultNew {
|
||||||
|
|
||||||
result := &paymentResultNew{
|
result := &paymentResultNew{
|
||||||
id: id,
|
id: id,
|
||||||
@@ -119,11 +122,11 @@ func newPaymentResult(id uint64, rt *mcRoute, timeFwd, timeReply time.Time,
|
|||||||
route: tlv.NewRecordT[tlv.TlvType2](*rt),
|
route: tlv.NewRecordT[tlv.TlvType2](*rt),
|
||||||
}
|
}
|
||||||
|
|
||||||
if failure != nil {
|
failure.WhenSome(func(f paymentFailure) {
|
||||||
result.failure = tlv.SomeRecordT(
|
result.failure = tlv.SomeRecordT(
|
||||||
tlv.NewRecordT[tlv.TlvType3](*failure),
|
tlv.NewRecordT[tlv.TlvType3](f),
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -142,33 +145,49 @@ type paymentResultNew struct {
|
|||||||
failure tlv.OptionalRecordT[tlv.TlvType3, paymentFailure]
|
failure tlv.OptionalRecordT[tlv.TlvType3, paymentFailure]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// newPaymentFailure constructs a new paymentFailure struct. If the source
|
||||||
// index is nil, then an empty paymentFailure is returned. This represents a
|
// index is nil, then an empty paymentFailure is returned. This represents a
|
||||||
// failure with unknown details. Otherwise, the index and failure message are
|
// failure with unknown details. Otherwise, the index and failure message are
|
||||||
// used to populate the info field of the paymentFailure.
|
// used to populate the info field of the paymentFailure.
|
||||||
func newPaymentFailure(sourceIdx *int,
|
func newPaymentFailure(sourceIdx *int,
|
||||||
failureMsg lnwire.FailureMessage) *paymentFailure {
|
failureMsg lnwire.FailureMessage) paymentFailure {
|
||||||
|
|
||||||
|
// If we can't identify a failure source, we also won't have a decrypted
|
||||||
|
// failure message. In this case we return an empty payment failure.
|
||||||
if sourceIdx == nil {
|
if sourceIdx == nil {
|
||||||
return &paymentFailure{}
|
return paymentFailure{}
|
||||||
}
|
}
|
||||||
|
|
||||||
info := paymentFailureInfo{
|
info := paymentFailure{
|
||||||
sourceIdx: tlv.NewPrimitiveRecord[tlv.TlvType0](
|
sourceIdx: tlv.SomeRecordT(
|
||||||
uint8(*sourceIdx),
|
tlv.NewPrimitiveRecord[tlv.TlvType0](
|
||||||
|
uint8(*sourceIdx),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
msg: tlv.NewRecordT[tlv.TlvType1](failureMessage{failureMsg}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &paymentFailure{
|
if failureMsg != nil {
|
||||||
info: tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType0](info)),
|
info.msg = tlv.SomeRecordT(
|
||||||
|
tlv.NewRecordT[tlv.TlvType1](
|
||||||
|
failureMessage{failureMsg},
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
// paymentFailure holds additional information about a payment failure.
|
||||||
|
type paymentFailure struct {
|
||||||
|
// sourceIdx is the hop the error was reported from. In order to be able
|
||||||
|
// to decrypt the error message, we need to know the source, which is
|
||||||
|
// why an error message can only be present if the source is known.
|
||||||
|
sourceIdx tlv.OptionalRecordT[tlv.TlvType0, uint8]
|
||||||
|
|
||||||
|
// msg is the error why a payment failed. If we identify the failure of
|
||||||
|
// a certain hop at the above index, but aren't able to decode the
|
||||||
|
// failure message we indicate this by not setting this field.
|
||||||
|
msg tlv.OptionalRecordT[tlv.TlvType1, failureMessage]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record returns a TLV record that can be used to encode/decode a
|
// Record returns a TLV record that can be used to encode/decode a
|
||||||
@@ -194,14 +213,27 @@ func (r *paymentFailure) Record() tlv.Record {
|
|||||||
func encodePaymentFailure(w io.Writer, val interface{}, _ *[8]byte) error {
|
func encodePaymentFailure(w io.Writer, val interface{}, _ *[8]byte) error {
|
||||||
if v, ok := val.(*paymentFailure); ok {
|
if v, ok := val.(*paymentFailure); ok {
|
||||||
var recordProducers []tlv.RecordProducer
|
var recordProducers []tlv.RecordProducer
|
||||||
v.info.WhenSome(
|
|
||||||
func(r tlv.RecordT[tlv.TlvType0, paymentFailureInfo]) {
|
v.sourceIdx.WhenSome(
|
||||||
recordProducers = append(recordProducers, &r)
|
func(r tlv.RecordT[tlv.TlvType0, uint8]) {
|
||||||
|
recordProducers = append(
|
||||||
|
recordProducers, &r,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
v.msg.WhenSome(
|
||||||
|
func(r tlv.RecordT[tlv.TlvType1, failureMessage]) {
|
||||||
|
recordProducers = append(
|
||||||
|
recordProducers, &r,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return lnwire.EncodeRecordsTo(
|
return lnwire.EncodeRecordsTo(
|
||||||
w, lnwire.ProduceRecordsSorted(recordProducers...),
|
w, lnwire.ProduceRecordsSorted(
|
||||||
|
recordProducers...,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,90 +242,36 @@ func encodePaymentFailure(w io.Writer, val interface{}, _ *[8]byte) error {
|
|||||||
|
|
||||||
func decodePaymentFailure(r io.Reader, val interface{}, _ *[8]byte,
|
func decodePaymentFailure(r io.Reader, val interface{}, _ *[8]byte,
|
||||||
l uint64) error {
|
l uint64) error {
|
||||||
|
|
||||||
if v, ok := val.(*paymentFailure); ok {
|
if v, ok := val.(*paymentFailure); ok {
|
||||||
var h paymentFailure
|
var h paymentFailure
|
||||||
|
|
||||||
info := tlv.ZeroRecordT[tlv.TlvType0, paymentFailureInfo]()
|
sourceIdx := tlv.ZeroRecordT[tlv.TlvType0, uint8]()
|
||||||
|
msg := tlv.ZeroRecordT[tlv.TlvType1, failureMessage]()
|
||||||
|
|
||||||
typeMap, err := lnwire.DecodeRecords(
|
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,
|
r,
|
||||||
lnwire.ProduceRecordsSorted(&h.sourceIdx, &h.msg)...,
|
lnwire.ProduceRecordsSorted(&sourceIdx, &msg)...,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := typeMap[h.sourceIdx.TlvType()]; ok {
|
||||||
|
h.sourceIdx = tlv.SomeRecordT(sourceIdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := typeMap[h.msg.TlvType()]; ok {
|
||||||
|
h.msg = tlv.SomeRecordT(msg)
|
||||||
|
}
|
||||||
|
|
||||||
*v = h
|
*v = h
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return tlv.NewTypeForDecodingErr(
|
return tlv.NewTypeForDecodingErr(
|
||||||
val, "routing.paymentFailureInfo", l, l,
|
val, "routing.paymentFailure", l, l,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -484,10 +484,12 @@ the on going rate we'll permit.
|
|||||||
|
|
||||||
## Database
|
## Database
|
||||||
|
|
||||||
* [Migrate the mission control
|
* [Migrate the mission control
|
||||||
store](https://github.com/lightningnetwork/lnd/pull/8911) to use a more
|
store](https://github.com/lightningnetwork/lnd/pull/8911) to use a more
|
||||||
minimal encoding for payment attempt routes as well as use [pure TLV
|
minimal encoding for payment attempt routes as well as use [pure TLV
|
||||||
encoding](https://github.com/lightningnetwork/lnd/pull/9167).
|
encoding](https://github.com/lightningnetwork/lnd/pull/9167). [A
|
||||||
|
fix](https://github.com/lightningnetwork/lnd/pull/9770) was added to handle
|
||||||
|
nil routing failure messages and the serialization was optimized.
|
||||||
|
|
||||||
* [Migrate the mission control
|
* [Migrate the mission control
|
||||||
store](https://github.com/lightningnetwork/lnd/pull/9001) so that results are
|
store](https://github.com/lightningnetwork/lnd/pull/9001) so that results are
|
||||||
|
|||||||
+74
-94
@@ -276,7 +276,7 @@ type paymentResult struct {
|
|||||||
|
|
||||||
// newPaymentResult constructs a new paymentResult.
|
// newPaymentResult constructs a new paymentResult.
|
||||||
func newPaymentResult(id uint64, rt *mcRoute, timeFwd, timeReply time.Time,
|
func newPaymentResult(id uint64, rt *mcRoute, timeFwd, timeReply time.Time,
|
||||||
failure *paymentFailure) *paymentResult {
|
failure fn.Option[paymentFailure]) *paymentResult {
|
||||||
|
|
||||||
result := &paymentResult{
|
result := &paymentResult{
|
||||||
id: id,
|
id: id,
|
||||||
@@ -289,11 +289,13 @@ func newPaymentResult(id uint64, rt *mcRoute, timeFwd, timeReply time.Time,
|
|||||||
route: tlv.NewRecordT[tlv.TlvType2](*rt),
|
route: tlv.NewRecordT[tlv.TlvType2](*rt),
|
||||||
}
|
}
|
||||||
|
|
||||||
if failure != nil {
|
failure.WhenSome(
|
||||||
result.failure = tlv.SomeRecordT(
|
func(f paymentFailure) {
|
||||||
tlv.NewRecordT[tlv.TlvType3](*failure),
|
result.failure = tlv.SomeRecordT(
|
||||||
)
|
tlv.NewRecordT[tlv.TlvType3](f),
|
||||||
}
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -621,7 +623,7 @@ func (m *MissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route,
|
|||||||
|
|
||||||
result := newPaymentResult(
|
result := newPaymentResult(
|
||||||
paymentID, extractMCRoute(rt), timestamp, timestamp,
|
paymentID, extractMCRoute(rt), timestamp, timestamp,
|
||||||
newPaymentFailure(failureSourceIdx, failure),
|
fn.Some(newPaymentFailure(failureSourceIdx, failure)),
|
||||||
)
|
)
|
||||||
|
|
||||||
return m.processPaymentResult(result)
|
return m.processPaymentResult(result)
|
||||||
@@ -635,7 +637,8 @@ func (m *MissionControl) ReportPaymentSuccess(paymentID uint64,
|
|||||||
timestamp := m.cfg.clock.Now()
|
timestamp := m.cfg.clock.Now()
|
||||||
|
|
||||||
result := newPaymentResult(
|
result := newPaymentResult(
|
||||||
paymentID, extractMCRoute(rt), timestamp, timestamp, nil,
|
paymentID, extractMCRoute(rt), timestamp, timestamp,
|
||||||
|
fn.None[paymentFailure](),
|
||||||
)
|
)
|
||||||
|
|
||||||
_, err := m.processPaymentResult(result)
|
_, err := m.processPaymentResult(result)
|
||||||
@@ -827,33 +830,49 @@ func (n *namespacedDB) purge() error {
|
|||||||
}, func() {})
|
}, 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
|
// newPaymentFailure constructs a new paymentFailure struct. If the source
|
||||||
// index is nil, then an empty paymentFailure is returned. This represents a
|
// index is nil, then an empty paymentFailure is returned. This represents a
|
||||||
// failure with unknown details. Otherwise, the index and failure message are
|
// failure with unknown details. Otherwise, the index and failure message are
|
||||||
// used to populate the info field of the paymentFailure.
|
// used to populate the info field of the paymentFailure.
|
||||||
func newPaymentFailure(sourceIdx *int,
|
func newPaymentFailure(sourceIdx *int,
|
||||||
failureMsg lnwire.FailureMessage) *paymentFailure {
|
failureMsg lnwire.FailureMessage) paymentFailure {
|
||||||
|
|
||||||
|
// If we can't identify a failure source, we also won't have a decrypted
|
||||||
|
// failure message. In this case we return an empty payment failure.
|
||||||
if sourceIdx == nil {
|
if sourceIdx == nil {
|
||||||
return &paymentFailure{}
|
return paymentFailure{}
|
||||||
}
|
}
|
||||||
|
|
||||||
info := paymentFailureInfo{
|
info := paymentFailure{
|
||||||
sourceIdx: tlv.NewPrimitiveRecord[tlv.TlvType0](
|
sourceIdx: tlv.SomeRecordT(
|
||||||
uint8(*sourceIdx),
|
tlv.NewPrimitiveRecord[tlv.TlvType0](
|
||||||
|
uint8(*sourceIdx),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
msg: tlv.NewRecordT[tlv.TlvType1](failureMessage{failureMsg}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &paymentFailure{
|
if failureMsg != nil {
|
||||||
info: tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType0](info)),
|
info.msg = tlv.SomeRecordT(
|
||||||
|
tlv.NewRecordT[tlv.TlvType1](
|
||||||
|
failureMessage{failureMsg},
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
// paymentFailure holds additional information about a payment failure.
|
||||||
|
type paymentFailure struct {
|
||||||
|
// sourceIdx is the hop the error was reported from. In order to be able
|
||||||
|
// to decrypt the error message, we need to know the source, which is
|
||||||
|
// why an error message can only be present if the source is known.
|
||||||
|
sourceIdx tlv.OptionalRecordT[tlv.TlvType0, uint8]
|
||||||
|
|
||||||
|
// msg is the error why a payment failed. If we identify the failure of
|
||||||
|
// a certain hop at the above index, but aren't able to decode the
|
||||||
|
// failure message we indicate this by not setting this field.
|
||||||
|
msg tlv.OptionalRecordT[tlv.TlvType1, failureMessage]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record returns a TLV record that can be used to encode/decode a
|
// Record returns a TLV record that can be used to encode/decode a
|
||||||
@@ -872,21 +891,35 @@ func (r *paymentFailure) Record() tlv.Record {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return tlv.MakeDynamicRecord(
|
return tlv.MakeDynamicRecord(
|
||||||
0, r, recordSize, encodePaymentFailure, decodePaymentFailure,
|
0, r, recordSize, encodePaymentFailure,
|
||||||
|
decodePaymentFailure,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodePaymentFailure(w io.Writer, val interface{}, _ *[8]byte) error {
|
func encodePaymentFailure(w io.Writer, val interface{}, _ *[8]byte) error {
|
||||||
if v, ok := val.(*paymentFailure); ok {
|
if v, ok := val.(*paymentFailure); ok {
|
||||||
var recordProducers []tlv.RecordProducer
|
var recordProducers []tlv.RecordProducer
|
||||||
v.info.WhenSome(
|
|
||||||
func(r tlv.RecordT[tlv.TlvType0, paymentFailureInfo]) {
|
v.sourceIdx.WhenSome(
|
||||||
recordProducers = append(recordProducers, &r)
|
func(r tlv.RecordT[tlv.TlvType0, uint8]) {
|
||||||
|
recordProducers = append(
|
||||||
|
recordProducers, &r,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
v.msg.WhenSome(
|
||||||
|
func(r tlv.RecordT[tlv.TlvType1, failureMessage]) {
|
||||||
|
recordProducers = append(
|
||||||
|
recordProducers, &r,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return lnwire.EncodeRecordsTo(
|
return lnwire.EncodeRecordsTo(
|
||||||
w, lnwire.ProduceRecordsSorted(recordProducers...),
|
w, lnwire.ProduceRecordsSorted(
|
||||||
|
recordProducers...,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -899,85 +932,32 @@ func decodePaymentFailure(r io.Reader, val interface{}, _ *[8]byte,
|
|||||||
if v, ok := val.(*paymentFailure); ok {
|
if v, ok := val.(*paymentFailure); ok {
|
||||||
var h paymentFailure
|
var h paymentFailure
|
||||||
|
|
||||||
info := tlv.ZeroRecordT[tlv.TlvType0, paymentFailureInfo]()
|
sourceIdx := tlv.ZeroRecordT[tlv.TlvType0, uint8]()
|
||||||
|
msg := tlv.ZeroRecordT[tlv.TlvType1, failureMessage]()
|
||||||
|
|
||||||
typeMap, err := lnwire.DecodeRecords(
|
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,
|
r,
|
||||||
lnwire.ProduceRecordsSorted(&h.sourceIdx, &h.msg)...,
|
lnwire.ProduceRecordsSorted(&sourceIdx, &msg)...,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := typeMap[h.sourceIdx.TlvType()]; ok {
|
||||||
|
h.sourceIdx = tlv.SomeRecordT(sourceIdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := typeMap[h.msg.TlvType()]; ok {
|
||||||
|
h.msg = tlv.SomeRecordT(msg)
|
||||||
|
}
|
||||||
|
|
||||||
*v = h
|
*v = h
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return tlv.NewTypeForDecodingErr(
|
return tlv.NewTypeForDecodingErr(
|
||||||
val, "routing.paymentFailureInfo", l, l,
|
val, "routing.paymentFailure", l, l,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcwallet/walletdb"
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
|
"github.com/lightningnetwork/lnd/fn/v2"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@@ -85,19 +86,19 @@ func TestMissionControlStore(t *testing.T) {
|
|||||||
|
|
||||||
result1 := newPaymentResult(
|
result1 := newPaymentResult(
|
||||||
99, mcStoreTestRoute, testTime, testTime,
|
99, mcStoreTestRoute, testTime, testTime,
|
||||||
newPaymentFailure(
|
fn.Some(newPaymentFailure(
|
||||||
&failureSourceIdx,
|
&failureSourceIdx,
|
||||||
lnwire.NewFailIncorrectDetails(100, 1000),
|
lnwire.NewFailIncorrectDetails(100, 1000),
|
||||||
),
|
)),
|
||||||
)
|
)
|
||||||
|
|
||||||
result2 := newPaymentResult(
|
result2 := newPaymentResult(
|
||||||
2, mcStoreTestRoute, testTime.Add(time.Hour),
|
2, mcStoreTestRoute, testTime.Add(time.Hour),
|
||||||
testTime.Add(time.Hour),
|
testTime.Add(time.Hour),
|
||||||
newPaymentFailure(
|
fn.Some(newPaymentFailure(
|
||||||
&failureSourceIdx,
|
&failureSourceIdx,
|
||||||
lnwire.NewFailIncorrectDetails(100, 1000),
|
lnwire.NewFailIncorrectDetails(100, 1000),
|
||||||
),
|
)),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Store result.
|
// Store result.
|
||||||
@@ -134,7 +135,7 @@ func TestMissionControlStore(t *testing.T) {
|
|||||||
)
|
)
|
||||||
result3.id = 3
|
result3.id = 3
|
||||||
result3.failure = tlv.SomeRecordT(
|
result3.failure = tlv.SomeRecordT(
|
||||||
tlv.NewRecordT[tlv.TlvType3](*newPaymentFailure(
|
tlv.NewRecordT[tlv.TlvType3](newPaymentFailure(
|
||||||
&failureSourceIdx, &lnwire.FailMPPTimeout{},
|
&failureSourceIdx, &lnwire.FailMPPTimeout{},
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
@@ -153,7 +154,7 @@ func TestMissionControlStore(t *testing.T) {
|
|||||||
// Also demonstrate the persistence of a success result.
|
// Also demonstrate the persistence of a success result.
|
||||||
result4 := newPaymentResult(
|
result4 := newPaymentResult(
|
||||||
5, mcStoreTestRoute, testTime.Add(3*time.Hour),
|
5, mcStoreTestRoute, testTime.Add(3*time.Hour),
|
||||||
testTime.Add(3*time.Hour), nil,
|
testTime.Add(3*time.Hour), fn.None[paymentFailure](),
|
||||||
)
|
)
|
||||||
store.AddResult(result4)
|
store.AddResult(result4)
|
||||||
require.NoError(t, store.storeResults())
|
require.NoError(t, store.storeResults())
|
||||||
@@ -186,7 +187,10 @@ func TestMissionControlStoreFlushing(t *testing.T) {
|
|||||||
return newPaymentResult(
|
return newPaymentResult(
|
||||||
lastID, mcStoreTestRoute, testTime.Add(-time.Hour),
|
lastID, mcStoreTestRoute, testTime.Add(-time.Hour),
|
||||||
testTime,
|
testTime,
|
||||||
newPaymentFailure(&failureSourceIdx, failureDetails),
|
fn.Some(newPaymentFailure(
|
||||||
|
&failureSourceIdx,
|
||||||
|
failureDetails,
|
||||||
|
)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,10 +291,10 @@ func BenchmarkMissionControlStoreFlushing(b *testing.B) {
|
|||||||
result := newPaymentResult(
|
result := newPaymentResult(
|
||||||
lastID, mcStoreTestRoute, testTimeFwd,
|
lastID, mcStoreTestRoute, testTimeFwd,
|
||||||
testTime,
|
testTime,
|
||||||
newPaymentFailure(
|
fn.Some(newPaymentFailure(
|
||||||
&failureSourceIdx,
|
&failureSourceIdx,
|
||||||
failureDetails,
|
failureDetails,
|
||||||
),
|
)),
|
||||||
)
|
)
|
||||||
store.AddResult(result)
|
store.AddResult(result)
|
||||||
}
|
}
|
||||||
@@ -305,10 +309,10 @@ func BenchmarkMissionControlStoreFlushing(b *testing.B) {
|
|||||||
results[i] = newPaymentResult(
|
results[i] = newPaymentResult(
|
||||||
0, mcStoreTestRoute, testTimeFwd,
|
0, mcStoreTestRoute, testTimeFwd,
|
||||||
testTime,
|
testTime,
|
||||||
newPaymentFailure(
|
fn.Some(newPaymentFailure(
|
||||||
&failureSourceIdx,
|
&failureSourceIdx,
|
||||||
failureDetails,
|
failureDetails,
|
||||||
),
|
)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,9 @@ func (i *interpretedResult) processSuccess(route *mcRoute) {
|
|||||||
|
|
||||||
// processFail processes a failed payment attempt.
|
// processFail processes a failed payment attempt.
|
||||||
func (i *interpretedResult) processFail(rt *mcRoute, failure paymentFailure) {
|
func (i *interpretedResult) processFail(rt *mcRoute, failure paymentFailure) {
|
||||||
if failure.info.IsNone() {
|
// Not having a source index means that we were unable to decrypt the
|
||||||
|
// error message.
|
||||||
|
if failure.sourceIdx.IsNone() {
|
||||||
i.processPaymentOutcomeUnknown(rt)
|
i.processPaymentOutcomeUnknown(rt)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -136,10 +138,17 @@ func (i *interpretedResult) processFail(rt *mcRoute, failure paymentFailure) {
|
|||||||
failMsg lnwire.FailureMessage
|
failMsg lnwire.FailureMessage
|
||||||
)
|
)
|
||||||
|
|
||||||
failure.info.WhenSome(
|
failure.sourceIdx.WhenSome(
|
||||||
func(r tlv.RecordT[tlv.TlvType0, paymentFailureInfo]) {
|
func(r tlv.RecordT[tlv.TlvType0, uint8]) {
|
||||||
idx = int(r.Val.sourceIdx.Val)
|
idx = int(r.Val)
|
||||||
failMsg = r.Val.msg.Val.FailureMessage
|
|
||||||
|
failure.msg.WhenSome(
|
||||||
|
func(r tlv.RecordT[tlv.TlvType1,
|
||||||
|
failureMessage]) {
|
||||||
|
|
||||||
|
failMsg = r.Val.FailureMessage
|
||||||
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -732,7 +732,7 @@ func TestResultInterpretation(t *testing.T) {
|
|||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
var failure fn.Option[paymentFailure]
|
var failure fn.Option[paymentFailure]
|
||||||
if !testCase.success {
|
if !testCase.success {
|
||||||
failure = fn.Some(*newPaymentFailure(
|
failure = fn.Some(newPaymentFailure(
|
||||||
&testCase.failureSrcIdx,
|
&testCase.failureSrcIdx,
|
||||||
testCase.failure,
|
testCase.failure,
|
||||||
))
|
))
|
||||||
|
|||||||
Reference in new issue
Block a user