lntemp+itest: create interface WebFeeService
This commit adds a new interface, `WebFeeService`, so external projects can create their own mocked fee services.
This commit is contained in:
4 files changed
+97
-54
No files matched your search
+69
-38
@@ -11,46 +11,68 @@ import (
|
|||||||
|
|
||||||
"github.com/lightningnetwork/lnd/lntest"
|
"github.com/lightningnetwork/lnd/lntest"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// WebFeeService defines an interface that's used to provide fee estimation
|
||||||
|
// service used in the integration tests. It must provide an URL so that a lnd
|
||||||
|
// node can be started with the flag `--feeurl` and uses the customized fee
|
||||||
|
// estimator.
|
||||||
|
type WebFeeService interface {
|
||||||
|
// Start starts the service.
|
||||||
|
Start() error
|
||||||
|
|
||||||
|
// Stop stops the service.
|
||||||
|
Stop() error
|
||||||
|
|
||||||
|
// URL returns the service's endpoint.
|
||||||
|
URL() string
|
||||||
|
|
||||||
|
// SetFeeRate sets the estimated fee rate for a given confirmation
|
||||||
|
// target.
|
||||||
|
SetFeeRate(feeRate chainfee.SatPerKWeight, conf uint32)
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// feeServiceTarget is the confirmation target for which a fee estimate
|
// feeServiceTarget is the confirmation target for which a fee estimate
|
||||||
// is returned. Requests for higher confirmation targets will fall back
|
// is returned. Requests for higher confirmation targets will fall back
|
||||||
// to this.
|
// to this.
|
||||||
feeServiceTarget = 1
|
feeServiceTarget = 1
|
||||||
|
|
||||||
|
// DefaultFeeRateSatPerKw specifies the default fee rate used in the
|
||||||
|
// tests.
|
||||||
|
DefaultFeeRateSatPerKw = 12500
|
||||||
)
|
)
|
||||||
|
|
||||||
// feeService runs a web service that provides fee estimation information.
|
// FeeService runs a web service that provides fee estimation information.
|
||||||
type feeService struct {
|
type FeeService struct {
|
||||||
feeEstimates
|
*testing.T
|
||||||
|
|
||||||
t *testing.T
|
feeRateMap map[uint32]uint32
|
||||||
|
url string
|
||||||
srv *http.Server
|
|
||||||
wg sync.WaitGroup
|
|
||||||
|
|
||||||
url string
|
|
||||||
|
|
||||||
|
srv *http.Server
|
||||||
|
wg sync.WaitGroup
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// feeEstimates contains the current fee estimates.
|
// Compile-time check for the WebFeeService interface.
|
||||||
type feeEstimates struct {
|
var _ WebFeeService = (*FeeService)(nil)
|
||||||
Fees map[uint32]uint32 `json:"fee_by_block_target"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// startFeeService spins up a go-routine to serve fee estimates.
|
// Start spins up a go-routine to serve fee estimates.
|
||||||
func startFeeService(t *testing.T) *feeService {
|
func NewFeeService(t *testing.T) *FeeService {
|
||||||
port := lntest.NextAvailablePort()
|
port := lntest.NextAvailablePort()
|
||||||
f := feeService{
|
f := FeeService{
|
||||||
t: t,
|
T: t,
|
||||||
url: fmt.Sprintf(
|
url: fmt.Sprintf(
|
||||||
"http://localhost:%v/fee-estimates.json", port,
|
"http://localhost:%v/fee-estimates.json", port,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize default fee estimate.
|
// Initialize default fee estimate.
|
||||||
f.Fees = map[uint32]uint32{feeServiceTarget: 50000}
|
f.feeRateMap = map[uint32]uint32{
|
||||||
|
feeServiceTarget: DefaultFeeRateSatPerKw,
|
||||||
|
}
|
||||||
|
|
||||||
listenAddr := fmt.Sprintf(":%v", port)
|
listenAddr := fmt.Sprintf(":%v", port)
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
@@ -60,50 +82,59 @@ func startFeeService(t *testing.T) *feeService {
|
|||||||
Addr: listenAddr,
|
Addr: listenAddr,
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
}
|
}
|
||||||
|
return &f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts the web server.
|
||||||
|
func (f *FeeService) Start() error {
|
||||||
f.wg.Add(1)
|
f.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer f.wg.Done()
|
defer f.wg.Done()
|
||||||
|
|
||||||
if err := f.srv.ListenAndServe(); err != http.ErrServerClosed {
|
if err := f.srv.ListenAndServe(); err != http.ErrServerClosed {
|
||||||
f.t.Errorf("error: cannot start fee api: %v", err)
|
require.NoErrorf(f, err, "cannot start fee api")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return &f
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleRequest handles a client request for fee estimates.
|
// handleRequest handles a client request for fee estimates.
|
||||||
func (f *feeService) handleRequest(w http.ResponseWriter, r *http.Request) {
|
func (f *FeeService) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
f.lock.Lock()
|
f.lock.Lock()
|
||||||
defer f.lock.Unlock()
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
bytes, err := json.Marshal(f.feeEstimates)
|
bytes, err := json.Marshal(
|
||||||
if err != nil {
|
struct {
|
||||||
f.t.Errorf("error: cannot serialize estimates: %v", err)
|
Fees map[uint32]uint32 `json:"fee_by_block_target"`
|
||||||
|
}{
|
||||||
return
|
Fees: f.feeRateMap,
|
||||||
}
|
},
|
||||||
|
)
|
||||||
|
require.NoErrorf(f, err, "cannot serialize estimates")
|
||||||
|
|
||||||
_, err = io.WriteString(w, string(bytes))
|
_, err = io.WriteString(w, string(bytes))
|
||||||
if err != nil {
|
require.NoError(f, err, "cannot send estimates")
|
||||||
f.t.Errorf("error: cannot send estimates: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop stops the web server.
|
// Stop stops the web server.
|
||||||
func (f *feeService) stop() {
|
func (f *FeeService) Stop() error {
|
||||||
if err := f.srv.Shutdown(context.Background()); err != nil {
|
err := f.srv.Shutdown(context.Background())
|
||||||
f.t.Errorf("error: cannot stop fee api: %v", err)
|
require.NoError(f, err, "cannot stop fee api")
|
||||||
}
|
|
||||||
|
|
||||||
f.wg.Wait()
|
f.wg.Wait()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setFee changes the current fee estimate for the fixed confirmation target.
|
// SetFeeRate sets a fee for the given confirmation target.
|
||||||
func (f *feeService) setFee(fee chainfee.SatPerKWeight) {
|
func (f *FeeService) SetFeeRate(fee chainfee.SatPerKWeight, conf uint32) {
|
||||||
f.lock.Lock()
|
f.lock.Lock()
|
||||||
defer f.lock.Unlock()
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
f.Fees[feeServiceTarget] = uint32(fee.FeePerKVByte())
|
f.feeRateMap[conf] = uint32(fee.FeePerKVByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL returns the service endpoint.
|
||||||
|
func (f *FeeService) URL() string {
|
||||||
|
return f.url
|
||||||
}
|
}
|
||||||
+18
-10
@@ -56,7 +56,7 @@ type HarnessTest struct {
|
|||||||
|
|
||||||
// feeService is a web service that provides external fee estimates to
|
// feeService is a web service that provides external fee estimates to
|
||||||
// lnd.
|
// lnd.
|
||||||
feeService *feeService
|
feeService WebFeeService
|
||||||
|
|
||||||
// Channel for transmitting stderr output from failed lightning node
|
// Channel for transmitting stderr output from failed lightning node
|
||||||
// to main process.
|
// to main process.
|
||||||
@@ -79,7 +79,7 @@ type HarnessTest struct {
|
|||||||
|
|
||||||
// NewHarnessTest creates a new instance of a harnessTest from a regular
|
// NewHarnessTest creates a new instance of a harnessTest from a regular
|
||||||
// testing.T instance.
|
// testing.T instance.
|
||||||
func NewHarnessTest(t *testing.T, lndBinary string,
|
func NewHarnessTest(t *testing.T, lndBinary string, feeService WebFeeService,
|
||||||
dbBackend lntest.DatabaseBackend) *HarnessTest {
|
dbBackend lntest.DatabaseBackend) *HarnessTest {
|
||||||
|
|
||||||
// Create the run context.
|
// Create the run context.
|
||||||
@@ -87,10 +87,11 @@ func NewHarnessTest(t *testing.T, lndBinary string,
|
|||||||
|
|
||||||
manager := newNodeManager(lndBinary, dbBackend)
|
manager := newNodeManager(lndBinary, dbBackend)
|
||||||
return &HarnessTest{
|
return &HarnessTest{
|
||||||
T: t,
|
T: t,
|
||||||
manager: manager,
|
manager: manager,
|
||||||
runCtx: ctxt,
|
feeService: feeService,
|
||||||
cancel: cancel,
|
runCtx: ctxt,
|
||||||
|
cancel: cancel,
|
||||||
// We need to use buffered channel here as we don't want to
|
// We need to use buffered channel here as we don't want to
|
||||||
// block sending errors.
|
// block sending errors.
|
||||||
lndErrorChan: make(chan error, 10),
|
lndErrorChan: make(chan error, 10),
|
||||||
@@ -118,11 +119,12 @@ func (h *HarnessTest) Start(chain node.BackendConfig, miner *HarnessMiner) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// Start the fee service.
|
// Start the fee service.
|
||||||
h.feeService = startFeeService(h.T)
|
err := h.feeService.Start()
|
||||||
|
require.NoError(h, err, "failed to start fee service")
|
||||||
|
|
||||||
// Assemble the node manager with chainBackend and feeServiceURL.
|
// Assemble the node manager with chainBackend and feeServiceURL.
|
||||||
h.manager.chainBackend = chain
|
h.manager.chainBackend = chain
|
||||||
h.manager.feeServiceURL = h.feeService.url
|
h.manager.feeServiceURL = h.feeService.URL()
|
||||||
|
|
||||||
// Assemble the miner.
|
// Assemble the miner.
|
||||||
h.Miner = miner
|
h.Miner = miner
|
||||||
@@ -229,7 +231,8 @@ func (h *HarnessTest) Stop() {
|
|||||||
close(h.lndErrorChan)
|
close(h.lndErrorChan)
|
||||||
|
|
||||||
// Stop the fee service.
|
// Stop the fee service.
|
||||||
h.feeService.stop()
|
err := h.feeService.Stop()
|
||||||
|
require.NoError(h, err, "failed to stop fee service")
|
||||||
|
|
||||||
// Stop the chainBackend.
|
// Stop the chainBackend.
|
||||||
h.stopChainBackend()
|
h.stopChainBackend()
|
||||||
@@ -436,8 +439,13 @@ func (h *HarnessTest) RestartNodeWithExtraArgs(hn *node.HarnessNode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetFeeEstimate sets a fee rate to be returned from fee estimator.
|
// SetFeeEstimate sets a fee rate to be returned from fee estimator.
|
||||||
|
//
|
||||||
|
// NOTE: this method will set the fee rate for a conf target of 1, which is the
|
||||||
|
// fallback fee rate for a `WebAPIEstimator` if a higher conf target's fee rate
|
||||||
|
// is not set. This means if the fee rate for conf target 6 is set, the fee
|
||||||
|
// estimator will use that value instead.
|
||||||
func (h *HarnessTest) SetFeeEstimate(fee chainfee.SatPerKWeight) {
|
func (h *HarnessTest) SetFeeEstimate(fee chainfee.SatPerKWeight) {
|
||||||
h.feeService.setFee(fee)
|
h.feeService.SetFeeRate(fee, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateNodeState checks that the node doesn't have any uncleaned states
|
// validateNodeState checks that the node doesn't have any uncleaned states
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ import (
|
|||||||
// 3. start a chain backend(btcd, bitcoind, or neutrino).
|
// 3. start a chain backend(btcd, bitcoind, or neutrino).
|
||||||
// 4. connect the miner and the chain backend.
|
// 4. connect the miner and the chain backend.
|
||||||
// 5. start the HarnessTest.
|
// 5. start the HarnessTest.
|
||||||
func SetupHarness(t *testing.T, binaryPath, dbBackendName string) *HarnessTest {
|
func SetupHarness(t *testing.T, binaryPath, dbBackendName string,
|
||||||
|
feeService WebFeeService) *HarnessTest {
|
||||||
|
|
||||||
t.Log("Setting up HarnessTest...")
|
t.Log("Setting up HarnessTest...")
|
||||||
|
|
||||||
// Parse testing flags that influence our test execution.
|
// Parse testing flags that influence our test execution.
|
||||||
@@ -28,7 +30,7 @@ func SetupHarness(t *testing.T, binaryPath, dbBackendName string) *HarnessTest {
|
|||||||
dbBackend := prepareDbBackend(t, dbBackendName)
|
dbBackend := prepareDbBackend(t, dbBackendName)
|
||||||
|
|
||||||
// Create a new HarnessTest.
|
// Create a new HarnessTest.
|
||||||
ht := NewHarnessTest(t, binaryPath, dbBackend)
|
ht := NewHarnessTest(t, binaryPath, feeService, dbBackend)
|
||||||
|
|
||||||
// Init the miner.
|
// Init the miner.
|
||||||
t.Log("Prepare the miner and mine blocks to activate segwit...")
|
t.Log("Prepare the miner and mine blocks to activate segwit...")
|
||||||
|
|||||||
@@ -68,9 +68,14 @@ func TestLightningNetworkDaemonTemp(t *testing.T) {
|
|||||||
testCases, trancheIndex, trancheOffset := getTestCaseSplitTranche()
|
testCases, trancheIndex, trancheOffset := getTestCaseSplitTranche()
|
||||||
lntest.ApplyPortOffset(uint32(trancheIndex) * 1000)
|
lntest.ApplyPortOffset(uint32(trancheIndex) * 1000)
|
||||||
|
|
||||||
|
// Create a simple fee service.
|
||||||
|
feeService := lntemp.NewFeeService(t)
|
||||||
|
|
||||||
// Get the binary path and setup the harness test.
|
// Get the binary path and setup the harness test.
|
||||||
binary := getLndBinary(t)
|
binary := getLndBinary(t)
|
||||||
harnessTest := lntemp.SetupHarness(t, binary, *dbBackendFlag)
|
harnessTest := lntemp.SetupHarness(
|
||||||
|
t, binary, *dbBackendFlag, feeService,
|
||||||
|
)
|
||||||
defer harnessTest.Stop()
|
defer harnessTest.Stop()
|
||||||
|
|
||||||
// Setup standby nodes, Alice and Bob, which will be alive and shared
|
// Setup standby nodes, Alice and Bob, which will be alive and shared
|
||||||
@@ -105,9 +110,6 @@ func TestLightningNetworkDaemonTemp(t *testing.T) {
|
|||||||
ht.Alice.AddToLogf(logLine)
|
ht.Alice.AddToLogf(logLine)
|
||||||
ht.Bob.AddToLogf(logLine)
|
ht.Bob.AddToLogf(logLine)
|
||||||
|
|
||||||
// Start every test with the default static fee
|
|
||||||
// estimate.
|
|
||||||
ht.SetFeeEstimate(12500)
|
|
||||||
ht.EnsureConnected(ht.Alice, ht.Bob)
|
ht.EnsureConnected(ht.Alice, ht.Bob)
|
||||||
|
|
||||||
ht.RunTestCase(testCase)
|
ht.RunTestCase(testCase)
|
||||||
|
|||||||
Reference in new issue
Block a user