Spend unconfirmed utxos if minconfs=0

This commit is contained in:
Jesse de Wit
2023-03-04 12:53:07 +01:00
parent 14f93d934e
commit 3b0dd351f4
17 changed files with 227 additions and 79 deletions

View File

@@ -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),

View File

@@ -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")
} }

View File

@@ -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
View File

@@ -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

View File

@@ -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++

View File

@@ -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")
} }

View File

@@ -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)

View File

@@ -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")
} }

View File

@@ -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...)

View File

@@ -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,
},
} }

View File

@@ -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 {

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

View File

@@ -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
} }

View File

@@ -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 {

View File

@@ -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")
} }

View File

@@ -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)

View File

@@ -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")
} }