mirror of
https://github.com/aljazceru/lspd.git
synced 2025-12-20 07:14:22 +01:00
Spend unconfirmed utxos if minconfs=0
This commit is contained in:
@@ -76,7 +76,11 @@ func (c *ClnClient) IsConnected(destination []byte) (bool, error) {
|
|||||||
|
|
||||||
func (c *ClnClient) OpenChannel(req *OpenChannelRequest) (*wire.OutPoint, error) {
|
func (c *ClnClient) OpenChannel(req *OpenChannelRequest) (*wire.OutPoint, error) {
|
||||||
pubkey := hex.EncodeToString(req.Destination)
|
pubkey := hex.EncodeToString(req.Destination)
|
||||||
minConfs := uint16(req.MinConfs)
|
var minConfs *uint16
|
||||||
|
if req.MinConfs != nil {
|
||||||
|
m := uint16(*req.MinConfs)
|
||||||
|
minConfs = &m
|
||||||
|
}
|
||||||
var minDepth *uint16
|
var minDepth *uint16
|
||||||
if req.IsZeroConf {
|
if req.IsZeroConf {
|
||||||
var d uint16 = 0
|
var d uint16 = 0
|
||||||
@@ -110,7 +114,7 @@ func (c *ClnClient) OpenChannel(req *OpenChannelRequest) (*wire.OutPoint, error)
|
|||||||
glightning.NewSat(int(req.CapacitySat)),
|
glightning.NewSat(int(req.CapacitySat)),
|
||||||
rate,
|
rate,
|
||||||
!req.IsPrivate,
|
!req.IsPrivate,
|
||||||
&minConfs,
|
minConfs,
|
||||||
glightning.NewMsat(0),
|
glightning.NewMsat(0),
|
||||||
minDepth,
|
minDepth,
|
||||||
glightning.NewSat(0),
|
glightning.NewSat(0),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/breez/lspd/cln_plugin/proto"
|
"github.com/breez/lspd/cln_plugin/proto"
|
||||||
|
"github.com/breez/lspd/config"
|
||||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/record"
|
"github.com/lightningnetwork/lnd/record"
|
||||||
@@ -23,7 +24,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ClnHtlcInterceptor struct {
|
type ClnHtlcInterceptor struct {
|
||||||
config *NodeConfig
|
config *config.NodeConfig
|
||||||
pluginAddress string
|
pluginAddress string
|
||||||
client *ClnClient
|
client *ClnClient
|
||||||
pluginClient proto.ClnPluginClient
|
pluginClient proto.ClnPluginClient
|
||||||
@@ -34,7 +35,7 @@ type ClnHtlcInterceptor struct {
|
|||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClnHtlcInterceptor(conf *NodeConfig) (*ClnHtlcInterceptor, error) {
|
func NewClnHtlcInterceptor(conf *config.NodeConfig) (*ClnHtlcInterceptor, error) {
|
||||||
if conf.Cln == nil {
|
if conf.Cln == nil {
|
||||||
return nil, fmt.Errorf("missing cln config")
|
return nil, fmt.Errorf("missing cln config")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package config
|
||||||
|
|
||||||
type NodeConfig struct {
|
type NodeConfig struct {
|
||||||
// Name of the LSP. If empty, the node's alias will be taken instead.
|
// Name of the LSP. If empty, the node's alias will be taken instead.
|
||||||
@@ -35,7 +35,7 @@ type NodeConfig struct {
|
|||||||
|
|
||||||
// Minimum number of confirmations inputs for zero conf channel opens should
|
// Minimum number of confirmations inputs for zero conf channel opens should
|
||||||
// have.
|
// have.
|
||||||
MinConfs uint32 `json:"minConfs,string"`
|
MinConfs *uint32 `json:"minConfs,string"`
|
||||||
|
|
||||||
// Smallest htlc amount routed over channels opened with the OpenChannel
|
// Smallest htlc amount routed over channels opened with the OpenChannel
|
||||||
// rpc call.
|
// rpc call.
|
||||||
2
go.mod
2
go.mod
@@ -4,7 +4,7 @@ go 1.19
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go v1.30.20
|
github.com/aws/aws-sdk-go v1.30.20
|
||||||
github.com/breez/lntest v0.0.18
|
github.com/breez/lntest v0.0.19
|
||||||
github.com/btcsuite/btcd v0.23.3
|
github.com/btcsuite/btcd v0.23.3
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.2.1
|
github.com/btcsuite/btcd/btcec/v2 v2.2.1
|
||||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1
|
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/breez/lspd/chain"
|
"github.com/breez/lspd/chain"
|
||||||
|
"github.com/breez/lspd/config"
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||||
@@ -49,7 +50,7 @@ type interceptResult struct {
|
|||||||
onionBlob []byte
|
onionBlob []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func intercept(client LightningClient, config *NodeConfig, nextHop string, reqPaymentHash []byte, reqOutgoingAmountMsat uint64, reqOutgoingExpiry uint32, reqIncomingExpiry uint32) interceptResult {
|
func intercept(client LightningClient, config *config.NodeConfig, nextHop string, reqPaymentHash []byte, reqOutgoingAmountMsat uint64, reqOutgoingExpiry uint32, reqIncomingExpiry uint32) interceptResult {
|
||||||
reqPaymentHashStr := hex.EncodeToString(reqPaymentHash)
|
reqPaymentHashStr := hex.EncodeToString(reqPaymentHash)
|
||||||
resp, _, _ := payHashGroup.Do(reqPaymentHashStr, func() (interface{}, error) {
|
resp, _, _ := payHashGroup.Do(reqPaymentHashStr, func() (interface{}, error) {
|
||||||
paymentHash, paymentSecret, destination, incomingAmountMsat, outgoingAmountMsat, channelPoint, err := paymentInfo(reqPaymentHash)
|
paymentHash, paymentSecret, destination, incomingAmountMsat, outgoingAmountMsat, channelPoint, err := paymentInfo(reqPaymentHash)
|
||||||
@@ -224,7 +225,7 @@ func intercept(client LightningClient, config *NodeConfig, nextHop string, reqPa
|
|||||||
return resp.(interceptResult)
|
return resp.(interceptResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkPayment(config *NodeConfig, incomingAmountMsat, outgoingAmountMsat int64) error {
|
func checkPayment(config *config.NodeConfig, incomingAmountMsat, outgoingAmountMsat int64) error {
|
||||||
fees := incomingAmountMsat * config.ChannelFeePermyriad / 10_000 / 1_000 * 1_000
|
fees := incomingAmountMsat * config.ChannelFeePermyriad / 10_000 / 1_000 * 1_000
|
||||||
if fees < config.ChannelMinimumFeeMsat {
|
if fees < config.ChannelMinimumFeeMsat {
|
||||||
fees = config.ChannelMinimumFeeMsat
|
fees = config.ChannelMinimumFeeMsat
|
||||||
@@ -235,7 +236,7 @@ func checkPayment(config *NodeConfig, incomingAmountMsat, outgoingAmountMsat int
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func openChannel(client LightningClient, config *NodeConfig, paymentHash, destination []byte, incomingAmountMsat int64) (*wire.OutPoint, error) {
|
func openChannel(client LightningClient, config *config.NodeConfig, paymentHash, destination []byte, incomingAmountMsat int64) (*wire.OutPoint, error) {
|
||||||
capacity := incomingAmountMsat/1000 + config.AdditionalChannelCapacity
|
capacity := incomingAmountMsat/1000 + config.AdditionalChannelCapacity
|
||||||
if capacity == config.PublicChannelAmount {
|
if capacity == config.PublicChannelAmount {
|
||||||
capacity++
|
capacity++
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/breez/lntest"
|
"github.com/breez/lntest"
|
||||||
|
"github.com/breez/lspd/config"
|
||||||
lspd "github.com/breez/lspd/rpc"
|
lspd "github.com/breez/lspd/rpc"
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
ecies "github.com/ecies/go/v2"
|
ecies "github.com/ecies/go/v2"
|
||||||
@@ -41,7 +42,7 @@ type clnLspNodeRuntime struct {
|
|||||||
cleanups []*lntest.Cleanup
|
cleanups []*lntest.Cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClnLspdNode(h *lntest.TestHarness, m *lntest.Miner, name string) LspNode {
|
func NewClnLspdNode(h *lntest.TestHarness, m *lntest.Miner, name string, nodeConfig *config.NodeConfig) LspNode {
|
||||||
scriptDir := h.GetDirectory("lspd")
|
scriptDir := h.GetDirectory("lspd")
|
||||||
pluginBinary := *clnPluginExec
|
pluginBinary := *clnPluginExec
|
||||||
pluginPort, err := lntest.GetPort()
|
pluginPort, err := lntest.GetPort()
|
||||||
@@ -60,12 +61,11 @@ func NewClnLspdNode(h *lntest.TestHarness, m *lntest.Miner, name string) LspNode
|
|||||||
"--dev-allowdustreserve=true",
|
"--dev-allowdustreserve=true",
|
||||||
}
|
}
|
||||||
lightningNode := lntest.NewClnNode(h, m, name, args...)
|
lightningNode := lntest.NewClnNode(h, m, name, args...)
|
||||||
cln := fmt.Sprintf(
|
cln := &config.ClnConfig{
|
||||||
`{ "pluginAddress": "%s", "socketPath": "%s" }`,
|
PluginAddress: pluginAddress,
|
||||||
pluginAddress,
|
SocketPath: filepath.Join(lightningNode.SocketDir(), lightningNode.SocketFile()),
|
||||||
filepath.Join(lightningNode.SocketDir(), lightningNode.SocketFile()),
|
}
|
||||||
)
|
lspbase, err := newLspd(h, name, nodeConfig, nil, cln)
|
||||||
lspbase, err := newLspd(h, name, nil, &cln)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.T.Fatalf("failed to initialize lspd")
|
h.T.Fatalf("failed to initialize lspd")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func TestConfigParameters(t *testing.T) {
|
|||||||
m := lntest.NewMiner(h)
|
m := lntest.NewMiner(h)
|
||||||
m.Start()
|
m.Start()
|
||||||
|
|
||||||
lsp := NewClnLspdNode(h, m, "lsp")
|
lsp := NewClnLspdNode(h, m, "lsp", nil)
|
||||||
lsp.Start()
|
lsp.Start()
|
||||||
|
|
||||||
log.Printf("Waiting %v to allow lsp server to activate.", htlcInterceptorDelay)
|
log.Printf("Waiting %v to allow lsp server to activate.", htlcInterceptorDelay)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/breez/lntest"
|
"github.com/breez/lntest"
|
||||||
|
"github.com/breez/lspd/config"
|
||||||
lspd "github.com/breez/lspd/rpc"
|
lspd "github.com/breez/lspd/rpc"
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
ecies "github.com/ecies/go/v2"
|
ecies "github.com/ecies/go/v2"
|
||||||
@@ -37,7 +38,7 @@ type lndLspNodeRuntime struct {
|
|||||||
cleanups []*lntest.Cleanup
|
cleanups []*lntest.Cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLndLspdNode(h *lntest.TestHarness, m *lntest.Miner, name string) LspNode {
|
func NewLndLspdNode(h *lntest.TestHarness, m *lntest.Miner, name string, nodeConfig *config.NodeConfig) LspNode {
|
||||||
args := []string{
|
args := []string{
|
||||||
"--protocol.zero-conf",
|
"--protocol.zero-conf",
|
||||||
"--protocol.option-scid-alias",
|
"--protocol.option-scid-alias",
|
||||||
@@ -50,12 +51,12 @@ func NewLndLspdNode(h *lntest.TestHarness, m *lntest.Miner, name string) LspNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
lightningNode := lntest.NewLndNode(h, m, name, args...)
|
lightningNode := lntest.NewLndNode(h, m, name, args...)
|
||||||
j, _ := json.Marshal(map[string]string{
|
lnd := &config.LndConfig{
|
||||||
"address": lightningNode.GrpcHost(),
|
Address: lightningNode.GrpcHost(),
|
||||||
"cert": string(lightningNode.TlsCert()),
|
Cert: string(lightningNode.TlsCert()),
|
||||||
"macaroon": hex.EncodeToString(lightningNode.Macaroon())})
|
Macaroon: hex.EncodeToString(lightningNode.Macaroon()),
|
||||||
lnd := string(j)
|
}
|
||||||
lspBase, err := newLspd(h, name, &lnd, nil)
|
lspBase, err := newLspd(h, name, nodeConfig, lnd, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.T.Fatalf("failed to initialize lspd")
|
h.T.Fatalf("failed to initialize lspd")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package itest
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@@ -11,6 +13,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/breez/lntest"
|
"github.com/breez/lntest"
|
||||||
|
"github.com/breez/lspd/config"
|
||||||
lspd "github.com/breez/lspd/rpc"
|
lspd "github.com/breez/lspd/rpc"
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
@@ -58,7 +61,7 @@ type lspBase struct {
|
|||||||
postgresBackend *PostgresContainer
|
postgresBackend *PostgresContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLspd(h *lntest.TestHarness, name string, lnd *string, cln *string, envExt ...string) (*lspBase, error) {
|
func newLspd(h *lntest.TestHarness, name string, nodeConfig *config.NodeConfig, lnd *config.LndConfig, cln *config.ClnConfig, envExt ...string) (*lspBase, error) {
|
||||||
scriptDir := h.GetDirectory(fmt.Sprintf("lspd-%s", name))
|
scriptDir := h.GetDirectory(fmt.Sprintf("lspd-%s", name))
|
||||||
log.Printf("%s: Creating LSPD in dir %s", name, scriptDir)
|
log.Printf("%s: Creating LSPD in dir %s", name, scriptDir)
|
||||||
|
|
||||||
@@ -88,32 +91,45 @@ func newLspd(h *lntest.TestHarness, name string, lnd *string, cln *string, envEx
|
|||||||
eciesPubl := ecies.NewPrivateKeyFromBytes(lspdPrivateKeyBytes).PublicKey
|
eciesPubl := ecies.NewPrivateKeyFromBytes(lspdPrivateKeyBytes).PublicKey
|
||||||
host := "localhost"
|
host := "localhost"
|
||||||
grpcAddress := fmt.Sprintf("%s:%d", host, lspdPort)
|
grpcAddress := fmt.Sprintf("%s:%d", host, lspdPort)
|
||||||
var ext string
|
minConfs := uint32(1)
|
||||||
if lnd != nil {
|
conf := &config.NodeConfig{
|
||||||
ext = fmt.Sprintf(`"lnd": %s`, *lnd)
|
Name: name,
|
||||||
} else if cln != nil {
|
LspdPrivateKey: hex.EncodeToString(lspdPrivateKeyBytes),
|
||||||
ext = fmt.Sprintf(`"cln": %s`, *cln)
|
Token: "hello",
|
||||||
} else {
|
Host: "host:port",
|
||||||
h.T.Fatalf("%s: need either lnd or cln config", name)
|
PublicChannelAmount: 1000183,
|
||||||
|
ChannelAmount: 100000,
|
||||||
|
ChannelPrivate: false,
|
||||||
|
TargetConf: 6,
|
||||||
|
MinConfs: &minConfs,
|
||||||
|
MinHtlcMsat: 600,
|
||||||
|
BaseFeeMsat: 1000,
|
||||||
|
FeeRate: 0.000001,
|
||||||
|
TimeLockDelta: 144,
|
||||||
|
ChannelFeePermyriad: 40,
|
||||||
|
ChannelMinimumFeeMsat: 2000000,
|
||||||
|
AdditionalChannelCapacity: 100000,
|
||||||
|
MaxInactiveDuration: 3888000,
|
||||||
|
Lnd: lnd,
|
||||||
|
Cln: cln,
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := fmt.Sprintf(
|
if nodeConfig != nil {
|
||||||
`NODES='[ { "name": "%s", "lspdPrivateKey": "%x", "token": "hello", "host": "host:port",`+
|
if nodeConfig.MinConfs != nil {
|
||||||
` "publicChannelAmount": "1000183", "channelAmount": "100000", "channelPrivate": false,`+
|
conf.MinConfs = nodeConfig.MinConfs
|
||||||
` "targetConf": "6", "minConfs": "1", "minHtlcMsat": "600", "baseFeeMsat": "1000", "feeRate": "0.000001",`+
|
}
|
||||||
` "timeLockDelta": "144", "channelFeePermyriad": "40", "channelMinimumFeeMsat": "2000000",`+
|
}
|
||||||
` "additionalChannelCapacity": "100000", "maxInactiveDuration": "3888000", %s}]'`,
|
|
||||||
name,
|
log.Printf("%+v", conf)
|
||||||
lspdPrivateKeyBytes,
|
confJson, _ := json.Marshal(conf)
|
||||||
ext,
|
nodes := fmt.Sprintf(`NODES='[%s]'`, string(confJson))
|
||||||
)
|
|
||||||
env := []string{
|
env := []string{
|
||||||
nodes,
|
nodes,
|
||||||
fmt.Sprintf("DATABASE_URL=%s", postgresBackend.ConnectionString()),
|
fmt.Sprintf("DATABASE_URL=%s", postgresBackend.ConnectionString()),
|
||||||
fmt.Sprintf("LISTEN_ADDRESS=%s", grpcAddress),
|
fmt.Sprintf("LISTEN_ADDRESS=%s", grpcAddress),
|
||||||
fmt.Sprintf("USE_MEMPOOL_FEE_ESTIMATION=true"),
|
"USE_MEMPOOL_FEE_ESTIMATION=true",
|
||||||
fmt.Sprintf("MEMPOOL_API_BASE_URL=https://mempool.space/api/v1/"),
|
"MEMPOOL_API_BASE_URL=https://mempool.space/api/v1/",
|
||||||
fmt.Sprintf("MEMPOOL_PRIORITY=economy"),
|
"MEMPOOL_PRIORITY=economy",
|
||||||
}
|
}
|
||||||
|
|
||||||
env = append(env, envExt...)
|
env = append(env, envExt...)
|
||||||
|
|||||||
@@ -7,31 +7,55 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/breez/lntest"
|
"github.com/breez/lntest"
|
||||||
|
"github.com/breez/lspd/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultTimeout time.Duration = time.Second * 120
|
var defaultTimeout time.Duration = time.Second * 120
|
||||||
|
|
||||||
func TestLspd(t *testing.T) {
|
func TestLspd(t *testing.T) {
|
||||||
testCases := allTestCases
|
testCases := allTestCases
|
||||||
runTests(t, testCases, "LND-lspd", func(h *lntest.TestHarness, m *lntest.Miner) (LspNode, BreezClient) {
|
runTests(t, testCases, "LND-lspd", lndLspFunc, lndClientFunc)
|
||||||
return NewLndLspdNode(h, m, "lsp"), newLndBreezClient(h, m, "breez-client")
|
runTests(t, testCases, "CLN-lspd", clnLspFunc, clnClientFunc)
|
||||||
})
|
|
||||||
|
|
||||||
runTests(t, testCases, "CLN-lspd", func(h *lntest.TestHarness, m *lntest.Miner) (LspNode, BreezClient) {
|
|
||||||
return NewClnLspdNode(h, m, "lsp"), newClnBreezClient(h, m, "breez-client")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTests(t *testing.T, testCases []*testCase, prefix string, nodesFunc func(h *lntest.TestHarness, m *lntest.Miner) (LspNode, BreezClient)) {
|
func lndLspFunc(h *lntest.TestHarness, m *lntest.Miner, c *config.NodeConfig) LspNode {
|
||||||
|
return NewLndLspdNode(h, m, "lsp", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clnLspFunc(h *lntest.TestHarness, m *lntest.Miner, c *config.NodeConfig) LspNode {
|
||||||
|
return NewClnLspdNode(h, m, "lsp", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lndClientFunc(h *lntest.TestHarness, m *lntest.Miner) BreezClient {
|
||||||
|
return newLndBreezClient(h, m, "breez-client")
|
||||||
|
}
|
||||||
|
|
||||||
|
func clnClientFunc(h *lntest.TestHarness, m *lntest.Miner) BreezClient {
|
||||||
|
return newClnBreezClient(h, m, "breez-client")
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTests(
|
||||||
|
t *testing.T,
|
||||||
|
testCases []*testCase,
|
||||||
|
prefix string,
|
||||||
|
lspFunc LspFunc,
|
||||||
|
clientFunc ClientFunc,
|
||||||
|
) {
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
testCase := testCase
|
testCase := testCase
|
||||||
t.Run(fmt.Sprintf("%s: %s", prefix, testCase.name), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s: %s", prefix, testCase.name), func(t *testing.T) {
|
||||||
runTest(t, testCase, prefix, nodesFunc)
|
runTest(t, testCase, prefix, lspFunc, clientFunc)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTest(t *testing.T, testCase *testCase, prefix string, nodesFunc func(h *lntest.TestHarness, m *lntest.Miner) (LspNode, BreezClient)) {
|
func runTest(
|
||||||
|
t *testing.T,
|
||||||
|
testCase *testCase,
|
||||||
|
prefix string,
|
||||||
|
lspFunc LspFunc,
|
||||||
|
clientFunc ClientFunc,
|
||||||
|
) {
|
||||||
log.Printf("%s: Running test case '%s'", prefix, testCase.name)
|
log.Printf("%s: Running test case '%s'", prefix, testCase.name)
|
||||||
var dd time.Duration
|
var dd time.Duration
|
||||||
to := testCase.timeout
|
to := testCase.timeout
|
||||||
@@ -49,8 +73,12 @@ func runTest(t *testing.T, testCase *testCase, prefix string, nodesFunc func(h *
|
|||||||
miner := lntest.NewMiner(h)
|
miner := lntest.NewMiner(h)
|
||||||
miner.Start()
|
miner.Start()
|
||||||
log.Printf("Creating lsp")
|
log.Printf("Creating lsp")
|
||||||
lsp, c := nodesFunc(h, miner)
|
var lsp LspNode
|
||||||
|
if !testCase.skipCreateLsp {
|
||||||
|
lsp = lspFunc(h, miner, nil)
|
||||||
lsp.Start()
|
lsp.Start()
|
||||||
|
}
|
||||||
|
c := clientFunc(h, miner)
|
||||||
c.Start()
|
c.Start()
|
||||||
log.Printf("Run testcase")
|
log.Printf("Run testcase")
|
||||||
testCase.test(&testParams{
|
testCase.test(&testParams{
|
||||||
@@ -59,12 +87,15 @@ func runTest(t *testing.T, testCase *testCase, prefix string, nodesFunc func(h *
|
|||||||
m: miner,
|
m: miner,
|
||||||
c: c,
|
c: c,
|
||||||
lsp: lsp,
|
lsp: lsp,
|
||||||
|
lspFunc: lspFunc,
|
||||||
|
clientFunc: clientFunc,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
name string
|
name string
|
||||||
test func(t *testParams)
|
test func(t *testParams)
|
||||||
|
skipCreateLsp bool
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,4 +136,9 @@ var allTestCases = []*testCase{
|
|||||||
name: "registerPaymentWithTag",
|
name: "registerPaymentWithTag",
|
||||||
test: registerPaymentWithTag,
|
test: registerPaymentWithTag,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "testOpenZeroConfUtxo",
|
||||||
|
test: testOpenZeroConfUtxo,
|
||||||
|
skipCreateLsp: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,20 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/breez/lntest"
|
"github.com/breez/lntest"
|
||||||
|
"github.com/breez/lspd/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type LspFunc func(h *lntest.TestHarness, m *lntest.Miner, c *config.NodeConfig) LspNode
|
||||||
|
type ClientFunc func(h *lntest.TestHarness, m *lntest.Miner) BreezClient
|
||||||
|
|
||||||
type testParams struct {
|
type testParams struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
h *lntest.TestHarness
|
h *lntest.TestHarness
|
||||||
m *lntest.Miner
|
m *lntest.Miner
|
||||||
c BreezClient
|
c BreezClient
|
||||||
lsp LspNode
|
lsp LspNode
|
||||||
|
lspFunc LspFunc
|
||||||
|
clientFunc ClientFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *testParams) T() *testing.T {
|
func (h *testParams) T() *testing.T {
|
||||||
|
|||||||
72
itest/zero_conf_utxo_test.go
Normal file
72
itest/zero_conf_utxo_test.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package itest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/breez/lntest"
|
||||||
|
"github.com/breez/lspd/config"
|
||||||
|
lspd "github.com/breez/lspd/rpc"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testOpenZeroConfUtxo(p *testParams) {
|
||||||
|
alice := lntest.NewClnNode(p.h, p.m, "Alice")
|
||||||
|
alice.Start()
|
||||||
|
alice.Fund(10000000)
|
||||||
|
|
||||||
|
minConfs := uint32(0)
|
||||||
|
lsp := p.lspFunc(p.h, p.m, &config.NodeConfig{MinConfs: &minConfs})
|
||||||
|
lsp.Start()
|
||||||
|
|
||||||
|
log.Print("Opening channel between Alice and the lsp")
|
||||||
|
channel := alice.OpenChannel(lsp.LightningNode(), &lntest.OpenChannelOptions{
|
||||||
|
AmountSat: publicChanAmount,
|
||||||
|
})
|
||||||
|
channelId := alice.WaitForChannelReady(channel)
|
||||||
|
|
||||||
|
// Send an unconfirmed utxo to the lsp
|
||||||
|
initialHeight := p.m.GetBlockHeight()
|
||||||
|
addr := lsp.LightningNode().GetNewAddress()
|
||||||
|
p.m.SendToAddress(addr, 200000)
|
||||||
|
|
||||||
|
log.Printf("Adding bob's invoices")
|
||||||
|
outerAmountMsat := uint64(2100000)
|
||||||
|
innerAmountMsat, lspAmountMsat := calculateInnerAmountMsat(lsp, outerAmountMsat)
|
||||||
|
description := "Please pay me"
|
||||||
|
innerInvoice, outerInvoice := GenerateInvoices(p.BreezClient(),
|
||||||
|
generateInvoicesRequest{
|
||||||
|
innerAmountMsat: innerAmountMsat,
|
||||||
|
outerAmountMsat: outerAmountMsat,
|
||||||
|
description: description,
|
||||||
|
lsp: lsp,
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Print("Connecting bob to lspd")
|
||||||
|
p.BreezClient().Node().ConnectPeer(lsp.LightningNode())
|
||||||
|
|
||||||
|
log.Printf("Registering payment with lsp")
|
||||||
|
RegisterPayment(lsp, &lspd.PaymentInformation{
|
||||||
|
PaymentHash: innerInvoice.paymentHash,
|
||||||
|
PaymentSecret: innerInvoice.paymentSecret,
|
||||||
|
Destination: p.BreezClient().Node().NodeId(),
|
||||||
|
IncomingAmountMsat: int64(outerAmountMsat),
|
||||||
|
OutgoingAmountMsat: int64(lspAmountMsat),
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: Fix race waiting for htlc interceptor.
|
||||||
|
log.Printf("Waiting %v to allow htlc interceptor to activate.", htlcInterceptorDelay)
|
||||||
|
<-time.After(htlcInterceptorDelay)
|
||||||
|
log.Printf("Alice paying")
|
||||||
|
route := constructRoute(lsp.LightningNode(), p.BreezClient().Node(), channelId, lntest.NewShortChanIDFromString("1x0x0"), outerAmountMsat)
|
||||||
|
payResp, err := alice.PayViaRoute(outerAmountMsat, outerInvoice.paymentHash, outerInvoice.paymentSecret, route)
|
||||||
|
lntest.CheckError(p.t, err)
|
||||||
|
bobInvoice := p.BreezClient().Node().GetInvoice(payResp.PaymentHash)
|
||||||
|
|
||||||
|
assert.Equal(p.t, payResp.PaymentPreimage, bobInvoice.PaymentPreimage)
|
||||||
|
assert.Equal(p.t, innerAmountMsat, bobInvoice.AmountReceivedMsat)
|
||||||
|
|
||||||
|
// Make sure there's not accidently a block mined in between
|
||||||
|
finalHeight := p.m.GetBlockHeight()
|
||||||
|
assert.Equal(p.t, initialHeight, finalHeight)
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ type OpenChannelRequest struct {
|
|||||||
MinHtlcMsat uint64
|
MinHtlcMsat uint64
|
||||||
IsPrivate bool
|
IsPrivate bool
|
||||||
IsZeroConf bool
|
IsZeroConf bool
|
||||||
MinConfs uint32
|
MinConfs *uint32
|
||||||
FeeSatPerVByte *float64
|
FeeSatPerVByte *float64
|
||||||
TargetConf *uint32
|
TargetConf *uint32
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/breez/lspd/basetypes"
|
"github.com/breez/lspd/basetypes"
|
||||||
|
"github.com/breez/lspd/config"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
@@ -24,7 +25,7 @@ type LndClient struct {
|
|||||||
conn *grpc.ClientConn
|
conn *grpc.ClientConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLndClient(conf *LndConfig) (*LndClient, error) {
|
func NewLndClient(conf *config.LndConfig) (*LndClient, error) {
|
||||||
_, err := hex.DecodeString(conf.Macaroon)
|
_, err := hex.DecodeString(conf.Macaroon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to decode macaroon: %w", err)
|
return nil, fmt.Errorf("failed to decode macaroon: %w", err)
|
||||||
@@ -103,7 +104,14 @@ func (c *LndClient) OpenChannel(req *OpenChannelRequest) (*wire.OutPoint, error)
|
|||||||
Private: req.IsPrivate,
|
Private: req.IsPrivate,
|
||||||
CommitmentType: lnrpc.CommitmentType_ANCHORS,
|
CommitmentType: lnrpc.CommitmentType_ANCHORS,
|
||||||
ZeroConf: req.IsZeroConf,
|
ZeroConf: req.IsZeroConf,
|
||||||
MinConfs: int32(req.MinConfs),
|
}
|
||||||
|
|
||||||
|
if req.MinConfs != nil {
|
||||||
|
minConfs := *req.MinConfs
|
||||||
|
lnReq.MinConfs = int32(minConfs)
|
||||||
|
if minConfs == 0 {
|
||||||
|
lnReq.SpendUnconfirmed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.FeeSatPerVByte != nil {
|
if req.FeeSatPerVByte != nil {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/breez/lspd/config"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
@@ -14,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type LndHtlcInterceptor struct {
|
type LndHtlcInterceptor struct {
|
||||||
config *NodeConfig
|
config *config.NodeConfig
|
||||||
client *LndClient
|
client *LndClient
|
||||||
stopRequested bool
|
stopRequested bool
|
||||||
initWg sync.WaitGroup
|
initWg sync.WaitGroup
|
||||||
@@ -23,7 +24,7 @@ type LndHtlcInterceptor struct {
|
|||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLndHtlcInterceptor(conf *NodeConfig) (*LndHtlcInterceptor, error) {
|
func NewLndHtlcInterceptor(conf *config.NodeConfig) (*LndHtlcInterceptor, error) {
|
||||||
if conf.Lnd == nil {
|
if conf.Lnd == nil {
|
||||||
return nil, fmt.Errorf("missing lnd configuration")
|
return nil, fmt.Errorf("missing lnd configuration")
|
||||||
}
|
}
|
||||||
|
|||||||
3
main.go
3
main.go
@@ -11,6 +11,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/breez/lspd/chain"
|
"github.com/breez/lspd/chain"
|
||||||
|
"github.com/breez/lspd/config"
|
||||||
"github.com/breez/lspd/mempool"
|
"github.com/breez/lspd/mempool"
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
)
|
)
|
||||||
@@ -26,7 +27,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
n := os.Getenv("NODES")
|
n := os.Getenv("NODES")
|
||||||
var nodes []*NodeConfig
|
var nodes []*config.NodeConfig
|
||||||
err := json.Unmarshal([]byte(n), &nodes)
|
err := json.Unmarshal([]byte(n), &nodes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to unmarshal NODES env: %v", err)
|
log.Fatalf("failed to unmarshal NODES env: %v", err)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/breez/lspd/btceclegacy"
|
"github.com/breez/lspd/btceclegacy"
|
||||||
|
"github.com/breez/lspd/config"
|
||||||
lspdrpc "github.com/breez/lspd/rpc"
|
lspdrpc "github.com/breez/lspd/rpc"
|
||||||
ecies "github.com/ecies/go/v2"
|
ecies "github.com/ecies/go/v2"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
@@ -39,7 +40,7 @@ type server struct {
|
|||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
client LightningClient
|
client LightningClient
|
||||||
nodeConfig *NodeConfig
|
nodeConfig *config.NodeConfig
|
||||||
privateKey *btcec.PrivateKey
|
privateKey *btcec.PrivateKey
|
||||||
publicKey *btcec.PublicKey
|
publicKey *btcec.PublicKey
|
||||||
eciesPrivateKey *ecies.PrivateKey
|
eciesPrivateKey *ecies.PrivateKey
|
||||||
@@ -259,7 +260,7 @@ func (s *server) CheckChannels(ctx context.Context, in *lspdrpc.Encrypted) (*lsp
|
|||||||
return &lspdrpc.Encrypted{Data: encrypted}, nil
|
return &lspdrpc.Encrypted{Data: encrypted}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGrpcServer(configs []*NodeConfig, address string, certmagicDomain string) (*server, error) {
|
func NewGrpcServer(configs []*config.NodeConfig, address string, certmagicDomain string) (*server, error) {
|
||||||
if len(configs) == 0 {
|
if len(configs) == 0 {
|
||||||
return nil, fmt.Errorf("no nodes supplied")
|
return nil, fmt.Errorf("no nodes supplied")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user