lntest: use web fee estimator in itests

This commit is contained in:
Joost Jager 2020-09-10 15:48:39 +02:00
parent fc3fd26a3d
commit b6ebf3f27d
No known key found for this signature in database
GPG Key ID: A61B9D4C393C59C7
4 changed files with 166 additions and 0 deletions

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())
}

@ -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"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntest/wait"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
) )
@ -63,6 +64,10 @@ type NetworkHarness struct {
// to main process. // to main process.
lndErrorChan chan error lndErrorChan chan error
// feeService is a web service that provides external fee estimates to
// lnd.
feeService *feeService
quit chan struct{} quit chan struct{}
mtx sync.Mutex mtx sync.Mutex
@ -75,6 +80,8 @@ type NetworkHarness struct {
func NewNetworkHarness(r *rpctest.Harness, b BackendConfig, lndBinary string) ( func NewNetworkHarness(r *rpctest.Harness, b BackendConfig, lndBinary string) (
*NetworkHarness, error) { *NetworkHarness, error) {
feeService := startFeeService()
n := NetworkHarness{ n := NetworkHarness{
activeNodes: make(map[int]*HarnessNode), activeNodes: make(map[int]*HarnessNode),
nodesByPub: make(map[string]*HarnessNode), nodesByPub: make(map[string]*HarnessNode),
@ -84,6 +91,7 @@ func NewNetworkHarness(r *rpctest.Harness, b BackendConfig, lndBinary string) (
netParams: r.ActiveNet, netParams: r.ActiveNet,
Miner: r, Miner: r,
BackendCfg: b, BackendCfg: b,
feeService: feeService,
quit: make(chan struct{}), quit: make(chan struct{}),
lndBinary: lndBinary, lndBinary: lndBinary,
} }
@ -251,6 +259,8 @@ func (n *NetworkHarness) TearDownAll() error {
close(n.lndErrorChan) close(n.lndErrorChan)
close(n.quit) close(n.quit)
n.feeService.stop()
return nil return nil
} }
@ -358,6 +368,7 @@ func (n *NetworkHarness) newNode(name string, extraArgs []string,
BackendCfg: n.BackendCfg, BackendCfg: n.BackendCfg,
NetParams: n.netParams, NetParams: n.netParams,
ExtraArgs: extraArgs, ExtraArgs: extraArgs,
FeeURL: n.feeService.url,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -1404,6 +1415,10 @@ func (n *NetworkHarness) sendCoins(ctx context.Context, amt btcutil.Amount,
return target.WaitForBalance(expectedBalance, true) return target.WaitForBalance(expectedBalance, true)
} }
func (n *NetworkHarness) SetFeeEstimate(fee chainfee.SatPerKWeight) {
n.feeService.setFee(fee)
}
// CopyFile copies the file src to dest. // CopyFile copies the file src to dest.
func CopyFile(dest, src string) error { func CopyFile(dest, src string) error {
s, err := os.Open(src) s, err := os.Open(src)

@ -154,6 +154,8 @@ type NodeConfig struct {
ProfilePort int ProfilePort int
AcceptKeySend bool AcceptKeySend bool
FeeURL string
} }
func (cfg NodeConfig) P2PAddr() string { func (cfg NodeConfig) P2PAddr() string {
@ -232,6 +234,10 @@ func (cfg NodeConfig) genArgs() []string {
args = append(args, "--accept-keysend") args = append(args, "--accept-keysend")
} }
if cfg.FeeURL != "" {
args = append(args, "--feeurl="+cfg.FeeURL)
}
return args return args
} }