Skip to content

Commit 8314bac

Browse files
committed
graph/db: extract InboundFee at deserialisation time
Here we add an explicit InboundFee field to the ChannelEdgePolicy struct. Then, in the graph KVStore, at deserialisation time, we extract the InboundFee from the ExtraOpaqueData. Currently we do this at higher levels but we are going to move it to the DB layer so that when we add the SQL implementation of the graph store, we can have explicit columns for inbound fees. We need to account for the fact that we may have invalid TLV already persisted though and we dont want to fail if we deserialise those necessarily. So we return ErrParsingExtraTLVBytes now if we fail to parse the extra bytes as TLV and then we let the callers handle it similarly to how ErrParsingExtraTLVBytes is handled in that we dont necessarily fail if we receive one of these errors. As of this commit, we can now expect the InboundFee field of a ChannelEdgePolicy to be set (if inbound fees are set on the policy) for any update that we read from disk.
1 parent 239acb4 commit 8314bac

File tree

2 files changed

+58
-11
lines changed

2 files changed

+58
-11
lines changed

graph/db/kv_store.go

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/btcsuite/btcwallet/walletdb"
2323
"github.com/lightningnetwork/lnd/aliasmgr"
2424
"github.com/lightningnetwork/lnd/batch"
25+
"github.com/lightningnetwork/lnd/fn/v2"
2526
"github.com/lightningnetwork/lnd/graph/db/models"
2627
"github.com/lightningnetwork/lnd/input"
2728
"github.com/lightningnetwork/lnd/kvdb"
@@ -284,6 +285,13 @@ func (c *KVStore) getChannelMap(edges kvdb.RBucket) (
284285
case errors.Is(err, ErrEdgePolicyOptionalFieldNotFound):
285286
return nil
286287

288+
// We don't want a single policy with bad TLV data to stop us
289+
// from loading the rest of the data, so we just skip this
290+
// policy. This is for backwards compatibility since we did not
291+
// use to validate TLV data in the past before persisting it.
292+
case errors.Is(err, ErrParsingExtraTLVBytes):
293+
return nil
294+
287295
case err != nil:
288296
return err
289297
}
@@ -2342,7 +2350,7 @@ func (c *KVStore) FilterChannelRange(startHeight,
23422350
edge, err := deserializeChanEdgePolicyRaw(r)
23432351
if err != nil && !errors.Is(
23442352
err, ErrEdgePolicyOptionalFieldNotFound,
2345-
) {
2353+
) && !errors.Is(err, ErrParsingExtraTLVBytes) {
23462354

23472355
return err
23482356
}
@@ -2357,7 +2365,7 @@ func (c *KVStore) FilterChannelRange(startHeight,
23572365
edge, err := deserializeChanEdgePolicyRaw(r)
23582366
if err != nil && !errors.Is(
23592367
err, ErrEdgePolicyOptionalFieldNotFound,
2360-
) {
2368+
) && !errors.Is(err, ErrParsingExtraTLVBytes) {
23612369

23622370
return err
23632371
}
@@ -4334,15 +4342,20 @@ func putChanEdgePolicy(edges kvdb.RwBucket, edge *models.ChannelEdgePolicy,
43344342
// need to deserialize the existing policy within the database
43354343
// (now outdated by the new one), and delete its corresponding
43364344
// entry within the update index. We'll ignore any
4337-
// ErrEdgePolicyOptionalFieldNotFound error, as we only need
4338-
// the channel ID and update time to delete the entry.
4345+
// ErrEdgePolicyOptionalFieldNotFound or ErrParsingExtraTLVBytes
4346+
// errors, as we only need the channel ID and update time to
4347+
// delete the entry.
4348+
//
43394349
// TODO(halseth): get rid of these invalid policies in a
43404350
// migration.
4351+
// TODO(elle): complete the above TODO in migration from kvdb
4352+
// to SQL.
43414353
oldEdgePolicy, err := deserializeChanEdgePolicy(
43424354
bytes.NewReader(edgeBytes),
43434355
)
43444356
if err != nil &&
4345-
!errors.Is(err, ErrEdgePolicyOptionalFieldNotFound) {
4357+
!errors.Is(err, ErrEdgePolicyOptionalFieldNotFound) &&
4358+
!errors.Is(err, ErrParsingExtraTLVBytes) {
43464359

43474360
return err
43484361
}
@@ -4449,6 +4462,11 @@ func fetchChanEdgePolicy(edges kvdb.RBucket, chanID []byte,
44494462
case errors.Is(err, ErrEdgePolicyOptionalFieldNotFound):
44504463
return nil, nil
44514464

4465+
// If the policy contains invalid TLV bytes, we return nil as if
4466+
// the policy was unknown.
4467+
case errors.Is(err, ErrParsingExtraTLVBytes):
4468+
return nil, nil
4469+
44524470
case err != nil:
44534471
return nil, err
44544472
}
@@ -4568,15 +4586,18 @@ func serializeChanEdgePolicy(w io.Writer, edge *models.ChannelEdgePolicy,
45684586

45694587
func deserializeChanEdgePolicy(r io.Reader) (*models.ChannelEdgePolicy, error) {
45704588
// Deserialize the policy. Note that in case an optional field is not
4571-
// found, both an error and a populated policy object are returned.
4572-
edge, deserializeErr := deserializeChanEdgePolicyRaw(r)
4573-
if deserializeErr != nil &&
4574-
!errors.Is(deserializeErr, ErrEdgePolicyOptionalFieldNotFound) {
4589+
// found or if the edge has invalid TLV data, then both an error and a
4590+
// populated policy object are returned so that the caller can decide
4591+
// if it still wants to use the edge or not.
4592+
edge, err := deserializeChanEdgePolicyRaw(r)
4593+
if err != nil &&
4594+
!errors.Is(err, ErrEdgePolicyOptionalFieldNotFound) &&
4595+
!errors.Is(err, ErrParsingExtraTLVBytes) {
45754596

4576-
return nil, deserializeErr
4597+
return nil, err
45774598
}
45784599

4579-
return edge, deserializeErr
4600+
return edge, err
45804601
}
45814602

45824603
func deserializeChanEdgePolicyRaw(r io.Reader) (*models.ChannelEdgePolicy,
@@ -4663,6 +4684,22 @@ func deserializeChanEdgePolicyRaw(r io.Reader) (*models.ChannelEdgePolicy,
46634684
edge.ExtraOpaqueData = opq[8:]
46644685
}
46654686

4687+
// Attempt to extract the inbound fee from the opaque data. If we fail
4688+
// to parse the TLV here, we return an error we also return the edge
4689+
// so that the caller can still use it. This is for backwards
4690+
// compatibility in case we have already persisted some policies that
4691+
// have invalid TLV data.
4692+
var inboundFee lnwire.Fee
4693+
typeMap, err := edge.ExtraOpaqueData.ExtractRecords(&inboundFee)
4694+
if err != nil {
4695+
return edge, fmt.Errorf("%w: %w", ErrParsingExtraTLVBytes, err)
4696+
}
4697+
4698+
val, ok := typeMap[lnwire.FeeRecordType]
4699+
if ok && val == nil {
4700+
edge.InboundFee = fn.Some(inboundFee)
4701+
}
4702+
46664703
return edge, nil
46674704
}
46684705

graph/db/models/channel_edge_policy.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"time"
66

77
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
8+
"github.com/lightningnetwork/lnd/fn/v2"
89
"github.com/lightningnetwork/lnd/lnwire"
910
)
1011

@@ -65,6 +66,15 @@ type ChannelEdgePolicy struct {
6566
// to. Using this pub key, the channel graph can further be traversed.
6667
ToNode [33]byte
6768

69+
// InboundFee is the fee that must be paid for incoming HTLCs.
70+
//
71+
// NOTE: for our kvdb implementation of the graph store, inbound fees
72+
// are still only persisted as part of extra opaque data and so this
73+
// field is not explicitly stored but is rather populated from the
74+
// ExtraOpaqueData field on deserialization. For our SQL implementation,
75+
// this field will be explicitly persisted in the database.
76+
InboundFee fn.Option[lnwire.Fee]
77+
6878
// ExtraOpaqueData is the set of data that was appended to this
6979
// message, some of which we may not actually know how to iterate or
7080
// parse. By holding onto this data, we ensure that we're able to

0 commit comments

Comments
 (0)