Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5/5]sweep: introduce budget-based deadline aware fee bumper #8424

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3f8da35
sweep: expand `InputSet` with more interface methods
yyforyongyu Jan 16, 2024
d00c104
sweep: change `markInputsPublishFailed` to take outpoints
yyforyongyu Jan 16, 2024
bd9256e
sweep: refactor `markInputsPendingPublish` to take `InputSet`
yyforyongyu Feb 21, 2024
0496d70
sweep: introduce `BudgetInputSet` to manage budget-based inputs
yyforyongyu Feb 27, 2024
9565c3b
sweep: introduce `BudgetAggregator` to cluster inputs by deadlines
yyforyongyu Feb 27, 2024
481216f
sweep: introduce `Bumper` interface to handle RBF
yyforyongyu Jan 17, 2024
818d1c2
sweeper: fix existing sweeper tests
yyforyongyu Feb 28, 2024
15872dc
sweep: remove RBF related tests
yyforyongyu Mar 15, 2024
7af5bba
sweep: remove `FeeRate()` from `InputSet` interface
yyforyongyu Jan 17, 2024
f354b65
sweep: add `FeeFunction` interface and a linear implementation
yyforyongyu Jan 24, 2024
447cf5c
lnwallet+sweep: add new method `CheckMempoolAcceptance`
yyforyongyu Feb 28, 2024
6e84fe6
lnwallet+sweep: calculate max allowed feerate on `BumpResult`
yyforyongyu Feb 29, 2024
4ca1de3
lnwallet+sweep: introduce `TxPublisher` to handle fee bump
yyforyongyu Feb 29, 2024
d5b5b76
sweep: add monitor loop to `TxPublisher`
yyforyongyu Feb 29, 2024
3365333
lnd: init publisher when creating new server
yyforyongyu Mar 17, 2024
0103345
sweep: increase delta fee rate precision in fee function
yyforyongyu Mar 18, 2024
83729e2
itest: fix existing itests
yyforyongyu Mar 17, 2024
1bd2589
docs: update release notes for fee bumper
yyforyongyu Mar 17, 2024
4bf98e7
sweep: make sure non-fee related errors are notified
yyforyongyu Mar 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions chainntnfs/mocks.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package chainntnfs

import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/fn"
"github.com/stretchr/testify/mock"
Expand Down Expand Up @@ -50,3 +51,73 @@ func (m *MockMempoolWatcher) LookupInputMempoolSpend(

return args.Get(0).(fn.Option[wire.MsgTx])
}

// MockNotifier is a mock implementation of the ChainNotifier interface.
type MockChainNotifier struct {
mock.Mock
}

// Compile-time check to ensure MockChainNotifier implements ChainNotifier.
var _ ChainNotifier = (*MockChainNotifier)(nil)

// RegisterConfirmationsNtfn registers an intent to be notified once txid
// reaches numConfs confirmations.
func (m *MockChainNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
pkScript []byte, numConfs, heightHint uint32,
opts ...NotifierOption) (*ConfirmationEvent, error) {

args := m.Called(txid, pkScript, numConfs, heightHint)
if args.Get(0) == nil {
return nil, args.Error(1)
}

return args.Get(0).(*ConfirmationEvent), args.Error(1)
}

// RegisterSpendNtfn registers an intent to be notified once the target
// outpoint is successfully spent within a transaction.
func (m *MockChainNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
pkScript []byte, heightHint uint32) (*SpendEvent, error) {

args := m.Called(outpoint, pkScript, heightHint)
if args.Get(0) == nil {
return nil, args.Error(1)
}

return args.Get(0).(*SpendEvent), args.Error(1)
}

// RegisterBlockEpochNtfn registers an intent to be notified of each new block
// connected to the tip of the main chain.
func (m *MockChainNotifier) RegisterBlockEpochNtfn(epoch *BlockEpoch) (
*BlockEpochEvent, error) {

args := m.Called(epoch)
if args.Get(0) == nil {
return nil, args.Error(1)
}

return args.Get(0).(*BlockEpochEvent), args.Error(1)
}

// Start the ChainNotifier. Once started, the implementation should be ready,
// and able to receive notification registrations from clients.
func (m *MockChainNotifier) Start() error {
args := m.Called()

return args.Error(0)
}

// Started returns true if this instance has been started, and false otherwise.
func (m *MockChainNotifier) Started() bool {
args := m.Called()

return args.Bool(0)
}

// Stops the concrete ChainNotifier.
func (m *MockChainNotifier) Stop() error {
args := m.Called()

return args.Error(0)
}
4 changes: 4 additions & 0 deletions contractcourt/utxonursery.go
Original file line number Diff line number Diff line change
Expand Up @@ -1406,6 +1406,10 @@ func (k *kidOutput) ConfHeight() uint32 {
return k.confHeight
}

func (k *kidOutput) RequiredLockTime() (uint32, bool) {
return k.absoluteMaturity, k.absoluteMaturity > 0
}

// Encode converts a KidOutput struct into a form suitable for on-disk database
// storage. Note that the signDescriptor struct field is included so that the
// output's witness can be generated by createSweepTx() when the output becomes
Expand Down
3 changes: 3 additions & 0 deletions docs/release-notes/release-notes-0.18.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ bitcoin peers' feefilter values into account](https:/lightningnetwor
* [Preparatory work](https:/lightningnetwork/lnd/pull/8159) for
forwarding of blinded routes was added.

* Introduced [fee bumper](https:/lightningnetwork/lnd/pull/8424) to
handle bumping the fees of sweeping transactions properly.

## RPC Additions

* [Deprecated](https:/lightningnetwork/lnd/pull/7175)
Expand Down
148 changes: 148 additions & 0 deletions input/mocks.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package input

import (
"crypto/sha256"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/keychain"
"github.com/stretchr/testify/mock"
)

Expand Down Expand Up @@ -123,3 +129,145 @@ func (m *MockInput) UnconfParent() *TxInfo {

return info.(*TxInfo)
}

// MockWitnessType implements the `WitnessType` interface and is used by other
// packages for mock testing.
type MockWitnessType struct {
mock.Mock
}

// Compile time assertion that MockWitnessType implements WitnessType.
var _ WitnessType = (*MockWitnessType)(nil)

// String returns a human readable version of the WitnessType.
func (m *MockWitnessType) String() string {
args := m.Called()

return args.String(0)
}

// WitnessGenerator will return a WitnessGenerator function that an output uses
// to generate the witness and optionally the sigScript for a sweep
// transaction.
func (m *MockWitnessType) WitnessGenerator(signer Signer,
descriptor *SignDescriptor) WitnessGenerator {

args := m.Called()

return args.Get(0).(WitnessGenerator)
}

// SizeUpperBound returns the maximum length of the witness of this WitnessType
// if it would be included in a tx. It also returns if the output itself is a
// nested p2sh output, if so then we need to take into account the extra
// sigScript data size.
func (m *MockWitnessType) SizeUpperBound() (int, bool, error) {
args := m.Called()

return args.Int(0), args.Bool(1), args.Error(2)
}

// AddWeightEstimation adds the estimated size of the witness in bytes to the
// given weight estimator.
func (m *MockWitnessType) AddWeightEstimation(e *TxWeightEstimator) error {
args := m.Called()

return args.Error(0)
}

// MockInputSigner is a mock implementation of the Signer interface.
type MockInputSigner struct {
mock.Mock
}

// Compile-time constraint to ensure MockInputSigner implements Signer.
var _ Signer = (*MockInputSigner)(nil)

// SignOutputRaw generates a signature for the passed transaction according to
// the data within the passed SignDescriptor.
func (m *MockInputSigner) SignOutputRaw(tx *wire.MsgTx,
signDesc *SignDescriptor) (Signature, error) {

args := m.Called(tx, signDesc)
if args.Get(0) == nil {
return nil, args.Error(1)
}

return args.Get(0).(Signature), args.Error(1)
}

// ComputeInputScript generates a complete InputIndex for the passed
// transaction with the signature as defined within the passed SignDescriptor.
func (m *MockInputSigner) ComputeInputScript(tx *wire.MsgTx,
signDesc *SignDescriptor) (*Script, error) {

args := m.Called(tx, signDesc)
if args.Get(0) == nil {
return nil, args.Error(1)
}

return args.Get(0).(*Script), args.Error(1)
}

// MuSig2CreateSession creates a new MuSig2 signing session using the local key
// identified by the key locator.
func (m *MockInputSigner) MuSig2CreateSession(version MuSig2Version,
locator keychain.KeyLocator, pubkey []*btcec.PublicKey,
tweak *MuSig2Tweaks, pubNonces [][musig2.PubNonceSize]byte,
nonces *musig2.Nonces) (*MuSig2SessionInfo, error) {

args := m.Called(version, locator, pubkey, tweak, pubNonces, nonces)
if args.Get(0) == nil {
return nil, args.Error(1)
}

return args.Get(0).(*MuSig2SessionInfo), args.Error(1)
}

// MuSig2RegisterNonces registers one or more public nonces of other signing
// participants for a session identified by its ID.
func (m *MockInputSigner) MuSig2RegisterNonces(versio MuSig2SessionID,
pubNonces [][musig2.PubNonceSize]byte) (bool, error) {

args := m.Called(versio, pubNonces)
if args.Get(0) == nil {
return false, args.Error(1)
}

return args.Bool(0), args.Error(1)
}

// MuSig2Sign creates a partial signature using the local signing key that was
// specified when the session was created.
func (m *MockInputSigner) MuSig2Sign(sessionID MuSig2SessionID,
msg [sha256.Size]byte, withSortedKeys bool) (
*musig2.PartialSignature, error) {

args := m.Called(sessionID, msg, withSortedKeys)
if args.Get(0) == nil {
return nil, args.Error(1)
}

return args.Get(0).(*musig2.PartialSignature), args.Error(1)
}

// MuSig2CombineSig combines the given partial signature(s) with the local one,
// if it already exists.
func (m *MockInputSigner) MuSig2CombineSig(sessionID MuSig2SessionID,
partialSig []*musig2.PartialSignature) (
*schnorr.Signature, bool, error) {

args := m.Called(sessionID, partialSig)
if args.Get(0) == nil {
return nil, false, args.Error(2)
}

return args.Get(0).(*schnorr.Signature), args.Bool(1), args.Error(2)
}

// MuSig2Cleanup removes a session from memory to free up resources.
func (m *MockInputSigner) MuSig2Cleanup(sessionID MuSig2SessionID) error {
args := m.Called(sessionID)

return args.Error(0)
}
3 changes: 2 additions & 1 deletion itest/lnd_channel_force_close_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,8 @@ func channelForceClosureTest(ht *lntest.HarnessTest,

// Allow some deviation because weight estimates during tx generation
// are estimates.
require.InEpsilon(ht, expectedFeeRate, feeRate, 0.005)
require.InEpsilonf(ht, expectedFeeRate, feeRate, 0.005, "fee rate not "+
"match: want %v, got %v", expectedFeeRate, feeRate)

// Find alice's commit sweep and anchor sweep (if present) in the
// mempool.
Expand Down
Loading