channeldb: final htlc resolution storage opt-in

This commit is contained in:
Joost Jager committed 2023-01-26 07:02:17 +01:00
1 parent b50d59ec2e
commit 7a785c74c4
10 files changed
+85 -27

No files matched your search

+24 -15
View File
@@ -1879,12 +1879,18 @@ func (c *OpenChannel) UpdateCommitment(newCommitment *ChannelCommitment,
return err return err
} }
// Get the bucket where settled htlcs are recorded. // Get the bucket where settled htlcs are recorded if the user
finalHtlcsBucket, err := fetchFinalHtlcsBucketRw( // opted in to storing this information.
tx, c.ShortChannelID, var finalHtlcsBucket kvdb.RwBucket
) if c.Db.parent.storeFinalHtlcResolutions {
if err != nil { bucket, err := fetchFinalHtlcsBucketRw(
return err tx, c.ShortChannelID,
)
if err != nil {
return err
}
finalHtlcsBucket = bucket
} }
var unsignedUpdates []LogUpdate var unsignedUpdates []LogUpdate
@@ -1957,15 +1963,18 @@ func processFinalHtlc(finalHtlcsBucket walletdb.ReadWriteBucket, upd LogUpdate,
return nil return nil
} }
err := putFinalHtlc( // Store the final resolution in the database if a bucket is provided.
finalHtlcsBucket, id, if finalHtlcsBucket != nil {
FinalHtlcInfo{ err := putFinalHtlc(
Settled: settled, finalHtlcsBucket, id,
Offchain: true, FinalHtlcInfo{
}, Settled: settled,
) Offchain: true,
if err != nil { },
return err )
if err != nil {
return err
}
} }
finalHtlcs[id] = settled finalHtlcs[id] = settled
+1 -1
View File
@@ -1463,7 +1463,7 @@ func TestKeyLocatorEncoding(t *testing.T) {
func TestFinalHtlcs(t *testing.T) { func TestFinalHtlcs(t *testing.T) {
t.Parallel() t.Parallel()
fullDB, err := MakeTestDB(t) fullDB, err := MakeTestDB(t, OptionStoreFinalHtlcResolutions(true))
require.NoError(t, err, "unable to make test database") require.NoError(t, err, "unable to make test database")
cdb := fullDB.ChannelStateDB() cdb := fullDB.ChannelStateDB()
+7
View File
@@ -307,6 +307,7 @@ type DB struct {
clock clock.Clock clock clock.Clock
dryRun bool dryRun bool
keepFailedPaymentAttempts bool keepFailedPaymentAttempts bool
storeFinalHtlcResolutions bool
} }
// Open opens or creates channeldb. Any necessary schemas migrations due // Open opens or creates channeldb. Any necessary schemas migrations due
@@ -364,6 +365,7 @@ func CreateWithBackend(backend kvdb.Backend,
clock: opts.clock, clock: opts.clock,
dryRun: opts.dryRun, dryRun: opts.dryRun,
keepFailedPaymentAttempts: opts.keepFailedPaymentAttempts, keepFailedPaymentAttempts: opts.keepFailedPaymentAttempts,
storeFinalHtlcResolutions: opts.storeFinalHtlcResolutions,
} }
// Set the parent pointer (only used in tests). // Set the parent pointer (only used in tests).
@@ -1741,6 +1743,11 @@ func (c *ChannelStateDB) LookupFinalHtlc(chanID lnwire.ShortChannelID,
func (c *ChannelStateDB) PutOnchainFinalHtlcOutcome( func (c *ChannelStateDB) PutOnchainFinalHtlcOutcome(
chanID lnwire.ShortChannelID, htlcID uint64, settled bool) error { chanID lnwire.ShortChannelID, htlcID uint64, settled bool) error {
// Skip if the user did not opt in to storing final resolutions.
if !c.parent.storeFinalHtlcResolutions {
return nil
}
return kvdb.Update(c.backend, func(tx kvdb.RwTx) error { return kvdb.Update(c.backend, func(tx kvdb.RwTx) error {
finalHtlcsBucket, err := fetchFinalHtlcsBucketRw(tx, chanID) finalHtlcsBucket, err := fetchFinalHtlcsBucketRw(tx, chanID)
if err != nil { if err != nil {
+14
View File
@@ -74,6 +74,10 @@ type Options struct {
// keepFailedPaymentAttempts determines whether failed htlc attempts // keepFailedPaymentAttempts determines whether failed htlc attempts
// are kept on disk or removed to save space. // are kept on disk or removed to save space.
keepFailedPaymentAttempts bool keepFailedPaymentAttempts bool
// storeFinalHtlcResolutions determines whether to persistently store
// the final resolution of incoming htlcs.
storeFinalHtlcResolutions bool
} }
// DefaultOptions returns an Options populated with default values. // DefaultOptions returns an Options populated with default values.
@@ -187,6 +191,16 @@ func OptionKeepFailedPaymentAttempts(keepFailedPaymentAttempts bool) OptionModif
} }
} }
// OptionStoreFinalHtlcResolutions controls whether to persistently store the
// final resolution of incoming htlcs.
func OptionStoreFinalHtlcResolutions(
storeFinalHtlcResolutions bool) OptionModifier {
return func(o *Options) {
o.storeFinalHtlcResolutions = storeFinalHtlcResolutions
}
}
// OptionPruneRevocationLog specifies whether the migration for pruning // OptionPruneRevocationLog specifies whether the migration for pruning
// revocation logs needs to be applied or not. // revocation logs needs to be applied or not.
func OptionPruneRevocationLog(prune bool) OptionModifier { func OptionPruneRevocationLog(prune bool) OptionModifier {
+2
View File
@@ -372,6 +372,8 @@ type Config struct {
KeepFailedPaymentAttempts bool `long:"keep-failed-payment-attempts" description:"Keeps persistent record of all failed payment attempts for successfully settled payments."` KeepFailedPaymentAttempts bool `long:"keep-failed-payment-attempts" description:"Keeps persistent record of all failed payment attempts for successfully settled payments."`
StoreFinalHtlcResolutions bool `long:"store-final-htlc-resolutions" description:"Persistently store the final resolution of incoming htlcs."`
DefaultRemoteMaxHtlcs uint16 `long:"default-remote-max-htlcs" description:"The default max_htlc applied when opening or accepting channels. This value limits the number of concurrent HTLCs that the remote party can add to the commitment. The maximum possible value is 483."` DefaultRemoteMaxHtlcs uint16 `long:"default-remote-max-htlcs" description:"The default max_htlc applied when opening or accepting channels. This value limits the number of concurrent HTLCs that the remote party can add to the commitment. The maximum possible value is 483."`
NumGraphSyncPeers int `long:"numgraphsyncpeers" description:"The number of peers that we should receive new graph updates from. This option can be tuned to save bandwidth for light clients or routing nodes."` NumGraphSyncPeers int `long:"numgraphsyncpeers" description:"The number of peers that we should receive new graph updates from. This option can be tuned to save bandwidth for light clients or routing nodes."`
+3
View File
@@ -872,6 +872,9 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
channeldb.OptionDryRunMigration(cfg.DryRunMigration), channeldb.OptionDryRunMigration(cfg.DryRunMigration),
channeldb.OptionSetUseGraphCache(!cfg.DB.NoGraphCache), channeldb.OptionSetUseGraphCache(!cfg.DB.NoGraphCache),
channeldb.OptionKeepFailedPaymentAttempts(cfg.KeepFailedPaymentAttempts), channeldb.OptionKeepFailedPaymentAttempts(cfg.KeepFailedPaymentAttempts),
channeldb.OptionStoreFinalHtlcResolutions(
cfg.StoreFinalHtlcResolutions,
),
channeldb.OptionPruneRevocationLog(cfg.DB.PruneRevocation), channeldb.OptionPruneRevocationLog(cfg.DB.PruneRevocation),
} }
@@ -73,6 +73,9 @@ current gossip sync query status.
Final resolution data will only be available for htlcs that are resolved Final resolution data will only be available for htlcs that are resolved
after upgrading lnd. after upgrading lnd.
This feature is [opt-in](https://github.com/lightningnetwork/lnd/pull/7341)
via a config flag.
* Zero-amount private invoices [now provide hop * Zero-amount private invoices [now provide hop
hints](https://github.com/lightningnetwork/lnd/pull/7082), up to `maxHopHints` hints](https://github.com/lightningnetwork/lnd/pull/7082), up to `maxHopHints`
(20 currently). (20 currently).
+23 -7
View File
@@ -59,7 +59,9 @@ func assertOutputExistsByValue(t *testing.T, commitTx *wire.MsgTx,
// testAddSettleWorkflow tests a simple channel scenario where Alice and Bob // testAddSettleWorkflow tests a simple channel scenario where Alice and Bob
// add, the settle an HTLC between themselves. // add, the settle an HTLC between themselves.
func testAddSettleWorkflow(t *testing.T, tweakless bool) { func testAddSettleWorkflow(t *testing.T, tweakless,
storeFinalHtlcResolutions bool) {
// Create a test channel which will be used for the duration of this // Create a test channel which will be used for the duration of this
// unittest. The channel will be funded evenly with Alice having 5 BTC, // unittest. The channel will be funded evenly with Alice having 5 BTC,
// and Bob having 5 BTC. // and Bob having 5 BTC.
@@ -68,7 +70,12 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool) {
chanType = channeldb.SingleFunderBit chanType = channeldb.SingleFunderBit
} }
aliceChannel, bobChannel, err := CreateTestChannels(t, chanType) aliceChannel, bobChannel, err := CreateTestChannels(
t, chanType,
channeldb.OptionStoreFinalHtlcResolutions(
storeFinalHtlcResolutions,
),
)
require.NoError(t, err, "unable to create test channels") require.NoError(t, err, "unable to create test channels")
paymentPreimage := bytes.Repeat([]byte{1}, 32) paymentPreimage := bytes.Repeat([]byte{1}, 32)
@@ -248,13 +255,18 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool) {
for htlcID, settled := range finalHtlcs { for htlcID, settled := range finalHtlcs {
require.True(t, settled, "final settle expected") require.True(t, settled, "final settle expected")
// Assert that final resolution was stored in Bob's database. // Assert that final resolution was stored in Bob's database if
// storage is enabled.
finalInfo, err := bobChannel.channelState.Db.LookupFinalHtlc( finalInfo, err := bobChannel.channelState.Db.LookupFinalHtlc(
bobChannel.ShortChanID(), htlcID, bobChannel.ShortChanID(), htlcID,
) )
require.NoError(t, err) if storeFinalHtlcResolutions {
require.True(t, finalInfo.Offchain) require.NoError(t, err)
require.True(t, finalInfo.Settled) require.True(t, finalInfo.Offchain)
require.True(t, finalInfo.Settled)
} else {
require.ErrorIs(t, err, channeldb.ErrHtlcUnknown)
}
} }
fwdPkg, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation2) fwdPkg, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation2)
@@ -339,9 +351,13 @@ func TestSimpleAddSettleWorkflow(t *testing.T) {
for _, tweakless := range []bool{true, false} { for _, tweakless := range []bool{true, false} {
tweakless := tweakless tweakless := tweakless
t.Run(fmt.Sprintf("tweakless=%v", tweakless), func(t *testing.T) { t.Run(fmt.Sprintf("tweakless=%v", tweakless), func(t *testing.T) {
testAddSettleWorkflow(t, tweakless) testAddSettleWorkflow(t, tweakless, false)
}) })
} }
t.Run("storeFinalHtlcResolutions=true", func(t *testing.T) {
testAddSettleWorkflow(t, false, true)
})
} }
// TestChannelZeroAddLocalHeight tests that we properly set the addCommitHeightLocal // TestChannelZeroAddLocalHeight tests that we properly set the addCommitHeightLocal
+5 -4
View File
@@ -106,8 +106,9 @@ var (
// allocated to each side. Within the channel, Alice is the initiator. If // allocated to each side. Within the channel, Alice is the initiator. If
// tweaklessCommits is true, then the commits within the channels will use the // tweaklessCommits is true, then the commits within the channels will use the
// new format, otherwise the legacy format. // new format, otherwise the legacy format.
func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType) ( func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType,
*LightningChannel, *LightningChannel, error) { dbModifiers ...channeldb.OptionModifier) (*LightningChannel,
*LightningChannel, error) {
channelCapacity, err := btcutil.NewAmount(testChannelCapacity) channelCapacity, err := btcutil.NewAmount(testChannelCapacity)
if err != nil { if err != nil {
@@ -228,7 +229,7 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType) (
return nil, nil, err return nil, nil, err
} }
dbAlice, err := channeldb.Open(t.TempDir()) dbAlice, err := channeldb.Open(t.TempDir(), dbModifiers...)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -236,7 +237,7 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType) (
require.NoError(t, dbAlice.Close()) require.NoError(t, dbAlice.Close())
}) })
dbBob, err := channeldb.Open(t.TempDir()) dbBob, err := channeldb.Open(t.TempDir(), dbModifiers...)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
+3
View File
@@ -315,6 +315,9 @@
; settled payments. ; settled payments.
; keep-failed-payment-attempts=false ; keep-failed-payment-attempts=false
; Persistently store the final resolution of incoming htlcs.
; store-final-htlc-resolutions=false
; The default max_htlc applied when opening or accepting channels. This value ; The default max_htlc applied when opening or accepting channels. This value
; limits the number of concurrent HTLCs that the remote party can add to the ; limits the number of concurrent HTLCs that the remote party can add to the
; commitment. The maximum possible value is 483. ; commitment. The maximum possible value is 483.