lntest: use web fee estimator in itests
This commit is contained in:
parent
fc3fd26a3d
commit
b6ebf3f27d
106
lntest/fee_service.go
Normal file
106
lntest/fee_service.go
Normal file
@ -0,0 +1,106 @@
|
||||
package lntest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
)
|
||||
|
||||
const (
|
||||
// feeServiceTarget is the confirmation target for which a fee estimate
|
||||
// is returned. Requests for higher confirmation targets will fall back
|
||||
// to this.
|
||||
feeServiceTarget = 2
|
||||
|
||||
// feeServicePort is the tcp port on which the service runs.
|
||||
feeServicePort = 16534
|
||||
)
|
||||
|
||||
// feeService runs a web service that provides fee estimation information.
|
||||
type feeService struct {
|
||||
feeEstimates
|
||||
|
||||
srv *http.Server
|
||||
wg sync.WaitGroup
|
||||
|
||||
url string
|
||||
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// feeEstimates contains the current fee estimates.
|
||||
type feeEstimates struct {
|
||||
Fees map[uint32]uint32 `json:"fee_by_block_target"`
|
||||
}
|
||||
|
||||
// startFeeService spins up a go-routine to serve fee estimates.
|
||||
func startFeeService() *feeService {
|
||||
f := feeService{
|
||||
url: fmt.Sprintf(
|
||||
"http://localhost:%v/fee-estimates.json", feeServicePort,
|
||||
),
|
||||
}
|
||||
|
||||
// Initialize default fee estimate.
|
||||
f.Fees = map[uint32]uint32{feeServiceTarget: 50000}
|
||||
|
||||
listenAddr := fmt.Sprintf(":%v", feeServicePort)
|
||||
f.srv = &http.Server{
|
||||
Addr: listenAddr,
|
||||
}
|
||||
|
||||
http.HandleFunc("/fee-estimates.json", f.handleRequest)
|
||||
|
||||
f.wg.Add(1)
|
||||
go func() {
|
||||
defer f.wg.Done()
|
||||
|
||||
if err := f.srv.ListenAndServe(); err != http.ErrServerClosed {
|
||||
fmt.Printf("error: cannot start fee api: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return &f
|
||||
}
|
||||
|
||||
// handleRequest handles a client request for fee estimates.
|
||||
func (f *feeService) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
|
||||
bytes, err := json.Marshal(f.feeEstimates)
|
||||
if err != nil {
|
||||
fmt.Printf("error: cannot serialize "+
|
||||
"estimates: %v", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
_, err = io.WriteString(w, string(bytes))
|
||||
if err != nil {
|
||||
fmt.Printf("error: cannot send estimates: %v",
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
// stop stops the web server.
|
||||
func (f *feeService) stop() {
|
||||
if err := f.srv.Shutdown(context.Background()); err != nil {
|
||||
fmt.Printf("error: cannot stop fee api: %v", err)
|
||||
}
|
||||
|
||||
f.wg.Wait()
|
||||
}
|
||||
|
||||
// setFee changes the current fee estimate for the fixed confirmation target.
|
||||
func (f *feeService) setFee(fee chainfee.SatPerKWeight) {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
|
||||
f.Fees[feeServiceTarget] = uint32(fee.FeePerKVByte())
|
||||
}
|
39
lntest/fee_service_test.go
Normal file
39
lntest/fee_service_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
package lntest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestFeeService tests the itest fee estimating web service.
|
||||
func TestFeeService(t *testing.T) {
|
||||
service := startFeeService()
|
||||
defer service.stop()
|
||||
|
||||
service.setFee(5000)
|
||||
|
||||
// Wait for service to start accepting connections.
|
||||
var resp *http.Response
|
||||
require.Eventually(
|
||||
t,
|
||||
func() bool {
|
||||
var err error
|
||||
resp, err = http.Get(service.url) // nolint:bodyclose
|
||||
return err == nil
|
||||
},
|
||||
10*time.Second, time.Second,
|
||||
)
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(
|
||||
t, "{\"fee_by_block_target\":{\"2\":20000}}", string(body),
|
||||
)
|
||||
}
|
@ -22,6 +22,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
)
|
||||
@ -63,6 +64,10 @@ type NetworkHarness struct {
|
||||
// to main process.
|
||||
lndErrorChan chan error
|
||||
|
||||
// feeService is a web service that provides external fee estimates to
|
||||
// lnd.
|
||||
feeService *feeService
|
||||
|
||||
quit chan struct{}
|
||||
|
||||
mtx sync.Mutex
|
||||
@ -75,6 +80,8 @@ type NetworkHarness struct {
|
||||
func NewNetworkHarness(r *rpctest.Harness, b BackendConfig, lndBinary string) (
|
||||
*NetworkHarness, error) {
|
||||
|
||||
feeService := startFeeService()
|
||||
|
||||
n := NetworkHarness{
|
||||
activeNodes: make(map[int]*HarnessNode),
|
||||
nodesByPub: make(map[string]*HarnessNode),
|
||||
@ -84,6 +91,7 @@ func NewNetworkHarness(r *rpctest.Harness, b BackendConfig, lndBinary string) (
|
||||
netParams: r.ActiveNet,
|
||||
Miner: r,
|
||||
BackendCfg: b,
|
||||
feeService: feeService,
|
||||
quit: make(chan struct{}),
|
||||
lndBinary: lndBinary,
|
||||
}
|
||||
@ -251,6 +259,8 @@ func (n *NetworkHarness) TearDownAll() error {
|
||||
close(n.lndErrorChan)
|
||||
close(n.quit)
|
||||
|
||||
n.feeService.stop()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -358,6 +368,7 @@ func (n *NetworkHarness) newNode(name string, extraArgs []string,
|
||||
BackendCfg: n.BackendCfg,
|
||||
NetParams: n.netParams,
|
||||
ExtraArgs: extraArgs,
|
||||
FeeURL: n.feeService.url,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -1404,6 +1415,10 @@ func (n *NetworkHarness) sendCoins(ctx context.Context, amt btcutil.Amount,
|
||||
return target.WaitForBalance(expectedBalance, true)
|
||||
}
|
||||
|
||||
func (n *NetworkHarness) SetFeeEstimate(fee chainfee.SatPerKWeight) {
|
||||
n.feeService.setFee(fee)
|
||||
}
|
||||
|
||||
// CopyFile copies the file src to dest.
|
||||
func CopyFile(dest, src string) error {
|
||||
s, err := os.Open(src)
|
||||
|
@ -154,6 +154,8 @@ type NodeConfig struct {
|
||||
ProfilePort int
|
||||
|
||||
AcceptKeySend bool
|
||||
|
||||
FeeURL string
|
||||
}
|
||||
|
||||
func (cfg NodeConfig) P2PAddr() string {
|
||||
@ -232,6 +234,10 @@ func (cfg NodeConfig) genArgs() []string {
|
||||
args = append(args, "--accept-keysend")
|
||||
}
|
||||
|
||||
if cfg.FeeURL != "" {
|
||||
args = append(args, "--feeurl="+cfg.FeeURL)
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user