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"
|
||||||
"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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user