mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-25 16:23:49 +02:00
Merge pull request #6716 from Crypt-iQ/zeroconfacceptor
multi: add zeroconfacceptor to default reject incoming channels
This commit is contained in:
commit
be5bc79444
@ -23,6 +23,8 @@ func NewChainedAcceptor() *ChainedAcceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddAcceptor adds a ChannelAcceptor to this ChainedAcceptor.
|
// AddAcceptor adds a ChannelAcceptor to this ChainedAcceptor.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the MultiplexAcceptor interface.
|
||||||
func (c *ChainedAcceptor) AddAcceptor(acceptor ChannelAcceptor) uint64 {
|
func (c *ChainedAcceptor) AddAcceptor(acceptor ChannelAcceptor) uint64 {
|
||||||
id := atomic.AddUint64(&c.acceptorID, 1)
|
id := atomic.AddUint64(&c.acceptorID, 1)
|
||||||
|
|
||||||
@ -36,12 +38,22 @@ func (c *ChainedAcceptor) AddAcceptor(acceptor ChannelAcceptor) uint64 {
|
|||||||
|
|
||||||
// RemoveAcceptor removes a ChannelAcceptor from this ChainedAcceptor given
|
// RemoveAcceptor removes a ChannelAcceptor from this ChainedAcceptor given
|
||||||
// an ID.
|
// an ID.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the MultiplexAcceptor interface.
|
||||||
func (c *ChainedAcceptor) RemoveAcceptor(id uint64) {
|
func (c *ChainedAcceptor) RemoveAcceptor(id uint64) {
|
||||||
c.acceptorsMtx.Lock()
|
c.acceptorsMtx.Lock()
|
||||||
delete(c.acceptors, id)
|
delete(c.acceptors, id)
|
||||||
c.acceptorsMtx.Unlock()
|
c.acceptorsMtx.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// numAcceptors returns the number of acceptors contained in the
|
||||||
|
// ChainedAcceptor.
|
||||||
|
func (c *ChainedAcceptor) numAcceptors() int {
|
||||||
|
c.acceptorsMtx.RLock()
|
||||||
|
defer c.acceptorsMtx.RUnlock()
|
||||||
|
return len(c.acceptors)
|
||||||
|
}
|
||||||
|
|
||||||
// Accept evaluates the results of all ChannelAcceptors in the acceptors map
|
// Accept evaluates the results of all ChannelAcceptors in the acceptors map
|
||||||
// and returns the conjunction of all these predicates.
|
// and returns the conjunction of all these predicates.
|
||||||
//
|
//
|
||||||
@ -91,5 +103,5 @@ func (c *ChainedAcceptor) Accept(req *ChannelAcceptRequest) *ChannelAcceptRespon
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A compile-time constraint to ensure ChainedAcceptor implements the
|
// A compile-time constraint to ensure ChainedAcceptor implements the
|
||||||
// ChannelAcceptor interface.
|
// MultiplexAcceptor interface.
|
||||||
var _ ChannelAcceptor = (*ChainedAcceptor)(nil)
|
var _ MultiplexAcceptor = (*ChainedAcceptor)(nil)
|
||||||
|
@ -118,3 +118,16 @@ func (c *ChannelAcceptResponse) RejectChannel() bool {
|
|||||||
type ChannelAcceptor interface {
|
type ChannelAcceptor interface {
|
||||||
Accept(req *ChannelAcceptRequest) *ChannelAcceptResponse
|
Accept(req *ChannelAcceptRequest) *ChannelAcceptResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MultiplexAcceptor is an interface that abstracts the ability of a
|
||||||
|
// ChannelAcceptor to contain sub-ChannelAcceptors.
|
||||||
|
type MultiplexAcceptor interface {
|
||||||
|
// Embed the ChannelAcceptor.
|
||||||
|
ChannelAcceptor
|
||||||
|
|
||||||
|
// AddAcceptor nests a ChannelAcceptor inside the MultiplexAcceptor.
|
||||||
|
AddAcceptor(acceptor ChannelAcceptor) uint64
|
||||||
|
|
||||||
|
// Remove a sub-ChannelAcceptor.
|
||||||
|
RemoveAcceptor(id uint64)
|
||||||
|
}
|
||||||
|
@ -258,20 +258,73 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
|
|||||||
pendingChanID := req.OpenChanMsg.PendingChannelID
|
pendingChanID := req.OpenChanMsg.PendingChannelID
|
||||||
|
|
||||||
// Map the channel commitment type to its RPC
|
// Map the channel commitment type to its RPC
|
||||||
// counterpart.
|
// counterpart. Also determine whether the zero-conf or
|
||||||
var commitmentType lnrpc.CommitmentType
|
// scid-alias channel types are set.
|
||||||
|
var (
|
||||||
|
commitmentType lnrpc.CommitmentType
|
||||||
|
wantsZeroConf bool
|
||||||
|
wantsScidAlias bool
|
||||||
|
)
|
||||||
|
|
||||||
if req.OpenChanMsg.ChannelType != nil {
|
if req.OpenChanMsg.ChannelType != nil {
|
||||||
channelFeatures := lnwire.RawFeatureVector(
|
channelFeatures := lnwire.RawFeatureVector(
|
||||||
*req.OpenChanMsg.ChannelType,
|
*req.OpenChanMsg.ChannelType,
|
||||||
)
|
)
|
||||||
switch {
|
switch {
|
||||||
case channelFeatures.OnlyContains(
|
case channelFeatures.OnlyContains(
|
||||||
|
lnwire.ZeroConfRequired,
|
||||||
|
lnwire.ScidAliasRequired,
|
||||||
lnwire.ScriptEnforcedLeaseRequired,
|
lnwire.ScriptEnforcedLeaseRequired,
|
||||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
lnwire.StaticRemoteKeyRequired,
|
lnwire.StaticRemoteKeyRequired,
|
||||||
):
|
):
|
||||||
commitmentType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE
|
commitmentType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE
|
||||||
|
|
||||||
|
case channelFeatures.OnlyContains(
|
||||||
|
lnwire.ZeroConfRequired,
|
||||||
|
lnwire.ScriptEnforcedLeaseRequired,
|
||||||
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
|
lnwire.StaticRemoteKeyRequired,
|
||||||
|
):
|
||||||
|
commitmentType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE
|
||||||
|
|
||||||
|
case channelFeatures.OnlyContains(
|
||||||
|
lnwire.ScidAliasRequired,
|
||||||
|
lnwire.ScriptEnforcedLeaseRequired,
|
||||||
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
|
lnwire.StaticRemoteKeyRequired,
|
||||||
|
):
|
||||||
|
commitmentType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE
|
||||||
|
|
||||||
|
case channelFeatures.OnlyContains(
|
||||||
|
lnwire.ScriptEnforcedLeaseRequired,
|
||||||
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
|
lnwire.StaticRemoteKeyRequired,
|
||||||
|
):
|
||||||
|
commitmentType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE
|
||||||
|
|
||||||
|
case channelFeatures.OnlyContains(
|
||||||
|
lnwire.ZeroConfRequired,
|
||||||
|
lnwire.ScidAliasRequired,
|
||||||
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
|
lnwire.StaticRemoteKeyRequired,
|
||||||
|
):
|
||||||
|
commitmentType = lnrpc.CommitmentType_ANCHORS
|
||||||
|
|
||||||
|
case channelFeatures.OnlyContains(
|
||||||
|
lnwire.ZeroConfRequired,
|
||||||
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
|
lnwire.StaticRemoteKeyRequired,
|
||||||
|
):
|
||||||
|
commitmentType = lnrpc.CommitmentType_ANCHORS
|
||||||
|
|
||||||
|
case channelFeatures.OnlyContains(
|
||||||
|
lnwire.ScidAliasRequired,
|
||||||
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
|
lnwire.StaticRemoteKeyRequired,
|
||||||
|
):
|
||||||
|
commitmentType = lnrpc.CommitmentType_ANCHORS
|
||||||
|
|
||||||
case channelFeatures.OnlyContains(
|
case channelFeatures.OnlyContains(
|
||||||
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
||||||
lnwire.StaticRemoteKeyRequired,
|
lnwire.StaticRemoteKeyRequired,
|
||||||
@ -291,6 +344,20 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
|
|||||||
"in channel acceptor request: %v",
|
"in channel acceptor request: %v",
|
||||||
req.OpenChanMsg.ChannelType)
|
req.OpenChanMsg.ChannelType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if channelFeatures.IsSet(
|
||||||
|
lnwire.ZeroConfRequired,
|
||||||
|
) {
|
||||||
|
|
||||||
|
wantsZeroConf = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if channelFeatures.IsSet(
|
||||||
|
lnwire.ScidAliasRequired,
|
||||||
|
) {
|
||||||
|
|
||||||
|
wantsScidAlias = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
acceptRequests[pendingChanID] = newRequest
|
acceptRequests[pendingChanID] = newRequest
|
||||||
@ -311,6 +378,8 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
|
|||||||
MaxAcceptedHtlcs: uint32(req.OpenChanMsg.MaxAcceptedHTLCs),
|
MaxAcceptedHtlcs: uint32(req.OpenChanMsg.MaxAcceptedHTLCs),
|
||||||
ChannelFlags: uint32(req.OpenChanMsg.ChannelFlags),
|
ChannelFlags: uint32(req.OpenChanMsg.ChannelFlags),
|
||||||
CommitmentType: commitmentType,
|
CommitmentType: commitmentType,
|
||||||
|
WantsZeroConf: wantsZeroConf,
|
||||||
|
WantsScidAlias: wantsScidAlias,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.send(chanAcceptReq); err != nil {
|
if err := r.send(chanAcceptReq); err != nil {
|
||||||
|
68
chanacceptor/zeroconfacceptor.go
Normal file
68
chanacceptor/zeroconfacceptor.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package chanacceptor
|
||||||
|
|
||||||
|
import "github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
|
||||||
|
// ZeroConfAcceptor wraps a regular ChainedAcceptor. If no acceptors are in the
|
||||||
|
// ChainedAcceptor, then Accept will reject all channel open requests. This
|
||||||
|
// should only be enabled when the zero-conf feature bit is set and is used to
|
||||||
|
// protect users from a malicious counter-party double-spending the zero-conf
|
||||||
|
// funding tx.
|
||||||
|
type ZeroConfAcceptor struct {
|
||||||
|
chainedAcceptor *ChainedAcceptor
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewZeroConfAcceptor initializes a ZeroConfAcceptor.
|
||||||
|
func NewZeroConfAcceptor() *ZeroConfAcceptor {
|
||||||
|
return &ZeroConfAcceptor{
|
||||||
|
chainedAcceptor: NewChainedAcceptor(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAcceptor adds a sub-ChannelAcceptor to the internal ChainedAcceptor.
|
||||||
|
func (z *ZeroConfAcceptor) AddAcceptor(acceptor ChannelAcceptor) uint64 {
|
||||||
|
return z.chainedAcceptor.AddAcceptor(acceptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAcceptor removes a sub-ChannelAcceptor from the internal
|
||||||
|
// ChainedAcceptor.
|
||||||
|
func (z *ZeroConfAcceptor) RemoveAcceptor(id uint64) {
|
||||||
|
z.chainedAcceptor.RemoveAcceptor(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept will deny the channel open request if the internal ChainedAcceptor is
|
||||||
|
// empty. If the internal ChainedAcceptor has any acceptors, then Accept will
|
||||||
|
// instead be called on it.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the ChannelAcceptor interface.
|
||||||
|
func (z *ZeroConfAcceptor) Accept(
|
||||||
|
req *ChannelAcceptRequest) *ChannelAcceptResponse {
|
||||||
|
|
||||||
|
// Alias for less verbosity.
|
||||||
|
channelType := req.OpenChanMsg.ChannelType
|
||||||
|
|
||||||
|
// Check if the channel type sets the zero-conf bit.
|
||||||
|
var zeroConfSet bool
|
||||||
|
|
||||||
|
if channelType != nil {
|
||||||
|
channelFeatures := lnwire.RawFeatureVector(*channelType)
|
||||||
|
zeroConfSet = channelFeatures.IsSet(lnwire.ZeroConfRequired)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no acceptors and the counter-party is requesting a zero
|
||||||
|
// conf channel, reject the attempt.
|
||||||
|
if z.chainedAcceptor.numAcceptors() == 0 && zeroConfSet {
|
||||||
|
// Deny the channel open request.
|
||||||
|
rejectChannel := NewChannelAcceptResponse(
|
||||||
|
false, nil, nil, 0, 0, 0, 0, 0, 0, false,
|
||||||
|
)
|
||||||
|
return rejectChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, the ChainedAcceptor has sub-acceptors, so call Accept on
|
||||||
|
// it.
|
||||||
|
return z.chainedAcceptor.Accept(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compile-time constraint to ensure ZeroConfAcceptor implements the
|
||||||
|
// MultiplexAcceptor interface.
|
||||||
|
var _ MultiplexAcceptor = (*ZeroConfAcceptor)(nil)
|
83
chanacceptor/zeroconfacceptor_test.go
Normal file
83
chanacceptor/zeroconfacceptor_test.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package chanacceptor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dummyAcceptor is a ChannelAcceptor that will never return a failure.
|
||||||
|
type dummyAcceptor struct{}
|
||||||
|
|
||||||
|
func (d *dummyAcceptor) Accept(
|
||||||
|
req *ChannelAcceptRequest) *ChannelAcceptResponse {
|
||||||
|
|
||||||
|
return &ChannelAcceptResponse{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestZeroConfAcceptorNormal verifies that the ZeroConfAcceptor will let
|
||||||
|
// requests go through for non-zero-conf channels if there are no
|
||||||
|
// sub-acceptors.
|
||||||
|
func TestZeroConfAcceptorNormal(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Create the zero-conf acceptor.
|
||||||
|
zeroAcceptor := NewZeroConfAcceptor()
|
||||||
|
|
||||||
|
// Assert that calling Accept won't return a failure.
|
||||||
|
req := &ChannelAcceptRequest{
|
||||||
|
OpenChanMsg: &lnwire.OpenChannel{},
|
||||||
|
}
|
||||||
|
resp := zeroAcceptor.Accept(req)
|
||||||
|
require.False(t, resp.RejectChannel())
|
||||||
|
|
||||||
|
// Add a dummyAcceptor to the zero-conf acceptor. Assert that Accept
|
||||||
|
// does not return a failure.
|
||||||
|
dummy := &dummyAcceptor{}
|
||||||
|
dummyID := zeroAcceptor.AddAcceptor(dummy)
|
||||||
|
resp = zeroAcceptor.Accept(req)
|
||||||
|
require.False(t, resp.RejectChannel())
|
||||||
|
|
||||||
|
// Remove the dummyAcceptor from the zero-conf acceptor and assert that
|
||||||
|
// Accept doesn't return a failure.
|
||||||
|
zeroAcceptor.RemoveAcceptor(dummyID)
|
||||||
|
resp = zeroAcceptor.Accept(req)
|
||||||
|
require.False(t, resp.RejectChannel())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestZeroConfAcceptorZC verifies that the ZeroConfAcceptor will fail
|
||||||
|
// zero-conf channel opens unless a sub-acceptor exists.
|
||||||
|
func TestZeroConfAcceptorZC(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Create the zero-conf acceptor.
|
||||||
|
zeroAcceptor := NewZeroConfAcceptor()
|
||||||
|
|
||||||
|
channelType := new(lnwire.ChannelType)
|
||||||
|
*channelType = lnwire.ChannelType(*lnwire.NewRawFeatureVector(
|
||||||
|
lnwire.ZeroConfRequired,
|
||||||
|
))
|
||||||
|
|
||||||
|
// Assert that calling Accept results in failure.
|
||||||
|
req := &ChannelAcceptRequest{
|
||||||
|
OpenChanMsg: &lnwire.OpenChannel{
|
||||||
|
ChannelType: channelType,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp := zeroAcceptor.Accept(req)
|
||||||
|
require.True(t, resp.RejectChannel())
|
||||||
|
|
||||||
|
// Add a dummyAcceptor to the zero-conf acceptor. Assert that Accept
|
||||||
|
// does not return a failure.
|
||||||
|
dummy := &dummyAcceptor{}
|
||||||
|
dummyID := zeroAcceptor.AddAcceptor(dummy)
|
||||||
|
resp = zeroAcceptor.Accept(req)
|
||||||
|
require.False(t, resp.RejectChannel())
|
||||||
|
|
||||||
|
// Remove the dummyAcceptor from the zero-conf acceptor and assert that
|
||||||
|
// Accept returns a failure.
|
||||||
|
zeroAcceptor.RemoveAcceptor(dummyID)
|
||||||
|
resp = zeroAcceptor.Accept(req)
|
||||||
|
require.True(t, resp.RejectChannel())
|
||||||
|
}
|
@ -13,6 +13,9 @@
|
|||||||
information. A new `listaliases` API has also been added that returns a data dump of all
|
information. A new `listaliases` API has also been added that returns a data dump of all
|
||||||
existing alias info.](https://github.com/lightningnetwork/lnd/pull/6734)
|
existing alias info.](https://github.com/lightningnetwork/lnd/pull/6734)
|
||||||
|
|
||||||
|
* [Adds a `ZeroConfAcceptor` that rejects any zero-conf channel opens unless an RPC `ChannelAcceptor` is
|
||||||
|
active. This is a safety measure to avoid funds loss.](https://github.com/lightningnetwork/lnd/pull/6716)
|
||||||
|
|
||||||
## Build system
|
## Build system
|
||||||
|
|
||||||
* [Add the release build directory to the `.gitignore` file to avoid the release
|
* [Add the release build directory to the `.gitignore` file to avoid the release
|
||||||
|
@ -1369,6 +1369,18 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer,
|
|||||||
zeroConf = featureVec.IsSet(lnwire.ZeroConfRequired)
|
zeroConf = featureVec.IsSet(lnwire.ZeroConfRequired)
|
||||||
scid = featureVec.IsSet(lnwire.ScidAliasRequired)
|
scid = featureVec.IsSet(lnwire.ScidAliasRequired)
|
||||||
|
|
||||||
|
// If the zero-conf channel type was negotiated, ensure that
|
||||||
|
// the acceptor allows it.
|
||||||
|
if zeroConf && !acceptorResp.ZeroConf {
|
||||||
|
// Fail the funding flow.
|
||||||
|
flowErr := fmt.Errorf("channel acceptor blocked " +
|
||||||
|
"zero-conf channel negotiation")
|
||||||
|
f.failFundingFlow(
|
||||||
|
peer, msg.PendingChannelID, flowErr,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// If the zero-conf channel type wasn't negotiated and the
|
// If the zero-conf channel type wasn't negotiated and the
|
||||||
// fundee still wants a zero-conf channel, perform more checks.
|
// fundee still wants a zero-conf channel, perform more checks.
|
||||||
// Require that both sides have the scid-alias feature bit set.
|
// Require that both sides have the scid-alias feature bit set.
|
||||||
|
@ -25,7 +25,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/chainreg"
|
"github.com/lightningnetwork/lnd/chainreg"
|
||||||
"github.com/lightningnetwork/lnd/chanacceptor"
|
acpt "github.com/lightningnetwork/lnd/chanacceptor"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/channelnotifier"
|
"github.com/lightningnetwork/lnd/channelnotifier"
|
||||||
"github.com/lightningnetwork/lnd/discovery"
|
"github.com/lightningnetwork/lnd/discovery"
|
||||||
@ -222,6 +222,19 @@ func (m *mockChanEvent) NotifyPendingOpenChannelEvent(outpoint wire.OutPoint,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mockZeroConfAcceptor always accepts the channel open request for zero-conf
|
||||||
|
// channels. It will set the ZeroConf bool in the ChannelAcceptResponse. This
|
||||||
|
// is needed to properly unit test the zero-conf logic in the funding manager.
|
||||||
|
type mockZeroConfAcceptor struct{}
|
||||||
|
|
||||||
|
func (m *mockZeroConfAcceptor) Accept(
|
||||||
|
req *acpt.ChannelAcceptRequest) *acpt.ChannelAcceptResponse {
|
||||||
|
|
||||||
|
return &acpt.ChannelAcceptResponse{
|
||||||
|
ZeroConf: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type newChannelMsg struct {
|
type newChannelMsg struct {
|
||||||
channel *channeldb.OpenChannel
|
channel *channeldb.OpenChannel
|
||||||
err chan error
|
err chan error
|
||||||
@ -400,7 +413,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
|
|||||||
|
|
||||||
var chanIDSeed [32]byte
|
var chanIDSeed [32]byte
|
||||||
|
|
||||||
chainedAcceptor := chanacceptor.NewChainedAcceptor()
|
chainedAcceptor := acpt.NewChainedAcceptor()
|
||||||
|
|
||||||
fundingCfg := Config{
|
fundingCfg := Config{
|
||||||
IDKey: privKey.PubKey(),
|
IDKey: privKey.PubKey(),
|
||||||
@ -556,7 +569,7 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) {
|
|||||||
|
|
||||||
oldCfg := alice.fundingMgr.cfg
|
oldCfg := alice.fundingMgr.cfg
|
||||||
|
|
||||||
chainedAcceptor := chanacceptor.NewChainedAcceptor()
|
chainedAcceptor := acpt.NewChainedAcceptor()
|
||||||
|
|
||||||
f, err := NewFundingManager(Config{
|
f, err := NewFundingManager(Config{
|
||||||
IDKey: oldCfg.IDKey,
|
IDKey: oldCfg.IDKey,
|
||||||
@ -3760,6 +3773,11 @@ func TestFundingManagerZeroConf(t *testing.T) {
|
|||||||
*lnwire.NewRawFeatureVector(channelTypeBits...),
|
*lnwire.NewRawFeatureVector(channelTypeBits...),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Create a default-accept channelacceptor so that the test passes and
|
||||||
|
// we don't have to use any goroutines.
|
||||||
|
mockAcceptor := &mockZeroConfAcceptor{}
|
||||||
|
bob.fundingMgr.cfg.OpenChannelPredicate = mockAcceptor
|
||||||
|
|
||||||
// Call fundChannel with the zero-conf ChannelType.
|
// Call fundChannel with the zero-conf ChannelType.
|
||||||
fundingTx := fundChannel(
|
fundingTx := fundChannel(
|
||||||
t, alice, bob, fundingAmt, pushAmt, false, 1, updateChan, true,
|
t, alice, bob, fundingAmt, pushAmt, false, 1, updateChan, true,
|
||||||
|
15
lnd.go
15
lnd.go
@ -496,15 +496,22 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the ChainedAcceptor.
|
// Initialize the MultiplexAcceptor. If lnd was started with the
|
||||||
chainedAcceptor := chanacceptor.NewChainedAcceptor()
|
// zero-conf feature bit, then this will be a ZeroConfAcceptor.
|
||||||
|
// Otherwise, this will be a ChainedAcceptor.
|
||||||
|
var multiAcceptor chanacceptor.MultiplexAcceptor
|
||||||
|
if cfg.ProtocolOptions.ZeroConf() {
|
||||||
|
multiAcceptor = chanacceptor.NewZeroConfAcceptor()
|
||||||
|
} else {
|
||||||
|
multiAcceptor = chanacceptor.NewChainedAcceptor()
|
||||||
|
}
|
||||||
|
|
||||||
// Set up the core server which will listen for incoming peer
|
// Set up the core server which will listen for incoming peer
|
||||||
// connections.
|
// connections.
|
||||||
server, err := newServer(
|
server, err := newServer(
|
||||||
cfg, cfg.Listeners, dbs, activeChainControl, &idKeyDesc,
|
cfg, cfg.Listeners, dbs, activeChainControl, &idKeyDesc,
|
||||||
activeChainControl.Cfg.WalletUnlockParams.ChansToRestore,
|
activeChainControl.Cfg.WalletUnlockParams.ChansToRestore,
|
||||||
chainedAcceptor, torController,
|
multiAcceptor, torController,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mkErr("unable to create server: %v", err)
|
return mkErr("unable to create server: %v", err)
|
||||||
@ -534,7 +541,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg,
|
|||||||
// start the RPC server.
|
// start the RPC server.
|
||||||
err = rpcServer.addDeps(
|
err = rpcServer.addDeps(
|
||||||
server, interceptorChain.MacaroonService(), cfg.SubRPCServers,
|
server, interceptorChain.MacaroonService(), cfg.SubRPCServers,
|
||||||
atplManager, server.invoices, tower, chainedAcceptor,
|
atplManager, server.invoices, tower, multiAcceptor,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mkErr("unable to add deps to RPC server: %v", err)
|
return mkErr("unable to add deps to RPC server: %v", err)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -926,6 +926,14 @@ message ChannelAcceptRequest {
|
|||||||
|
|
||||||
// The commitment type the initiator wishes to use for the proposed channel.
|
// The commitment type the initiator wishes to use for the proposed channel.
|
||||||
CommitmentType commitment_type = 14;
|
CommitmentType commitment_type = 14;
|
||||||
|
|
||||||
|
// Whether the initiator wants to open a zero-conf channel via the channel
|
||||||
|
// type.
|
||||||
|
bool wants_zero_conf = 15;
|
||||||
|
|
||||||
|
// Whether the initiator wants to use the scid-alias channel type. This is
|
||||||
|
// separate from the feature bit.
|
||||||
|
bool wants_scid_alias = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ChannelAcceptResponse {
|
message ChannelAcceptResponse {
|
||||||
|
@ -3423,6 +3423,14 @@
|
|||||||
"commitment_type": {
|
"commitment_type": {
|
||||||
"$ref": "#/definitions/lnrpcCommitmentType",
|
"$ref": "#/definitions/lnrpcCommitmentType",
|
||||||
"description": "The commitment type the initiator wishes to use for the proposed channel."
|
"description": "The commitment type the initiator wishes to use for the proposed channel."
|
||||||
|
},
|
||||||
|
"wants_zero_conf": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the initiator wants to open a zero-conf channel via the channel\ntype."
|
||||||
|
},
|
||||||
|
"wants_scid_alias": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the initiator wants to use the scid-alias channel type. This is\nseparate from the feature bit."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1063,6 +1063,18 @@ func testChanRestoreScenario(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
)
|
)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
// If we are testing zero-conf channels, setup a
|
||||||
|
// ChannelAcceptor for the fundee.
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
if testCase.zeroConf {
|
||||||
|
// Setup a ChannelAcceptor.
|
||||||
|
var ctxc context.Context
|
||||||
|
ctxc, cancel = context.WithCancel(ctxb)
|
||||||
|
acceptStream, err := to.ChannelAcceptor(ctxc)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
go acceptChannel(t, true, acceptStream)
|
||||||
|
}
|
||||||
|
|
||||||
var fundingShim *lnrpc.FundingShim
|
var fundingShim *lnrpc.FundingShim
|
||||||
if testCase.commitmentType == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE {
|
if testCase.commitmentType == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE {
|
||||||
_, minerHeight, err := net.Miner.Client.GetBestBlock()
|
_, minerHeight, err := net.Miner.Client.GetBestBlock()
|
||||||
@ -1085,6 +1097,11 @@ func testChanRestoreScenario(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
t, net, from, to, params,
|
t, net, from, to, params,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Remove the ChannelAcceptor.
|
||||||
|
if testCase.zeroConf {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for both sides to see the opened channel.
|
// Wait for both sides to see the opened channel.
|
||||||
err = dave.WaitForNetworkChannelOpen(chanPoint)
|
err = dave.WaitForNetworkChannelOpen(chanPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -265,6 +265,23 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
net, t, alice, bob, chanAmt, thawHeight, true,
|
net, t, alice, bob, chanAmt, thawHeight, true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a zero-conf channel is being opened, the nodes are signalling the
|
||||||
|
// zero-conf feature bit. Setup a ChannelAcceptor for the fundee.
|
||||||
|
ctxb := context.Background()
|
||||||
|
|
||||||
|
var (
|
||||||
|
cancel context.CancelFunc
|
||||||
|
ctxc context.Context
|
||||||
|
)
|
||||||
|
|
||||||
|
if zeroConf {
|
||||||
|
ctxc, cancel = context.WithCancel(ctxb)
|
||||||
|
acceptStream, err := bob.ChannelAcceptor(ctxc)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
go acceptChannel(t, true, acceptStream)
|
||||||
|
}
|
||||||
|
|
||||||
aliceChanPoint := openChannelAndAssert(
|
aliceChanPoint := openChannelAndAssert(
|
||||||
t, net, alice, bob,
|
t, net, alice, bob,
|
||||||
lntest.OpenChannelParams{
|
lntest.OpenChannelParams{
|
||||||
@ -275,6 +292,11 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Remove the ChannelAcceptor for Bob.
|
||||||
|
if zeroConf {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
err := alice.WaitForNetworkChannelOpen(aliceChanPoint)
|
err := alice.WaitForNetworkChannelOpen(aliceChanPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("alice didn't report channel: %v", err)
|
t.Fatalf("alice didn't report channel: %v", err)
|
||||||
@ -320,6 +342,16 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
net, t, bob, carol, chanAmt, thawHeight, true,
|
net, t, bob, carol, chanAmt, thawHeight, true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup a ChannelAcceptor for Carol if a zero-conf channel open is
|
||||||
|
// being attempted.
|
||||||
|
if zeroConf {
|
||||||
|
ctxc, cancel = context.WithCancel(ctxb)
|
||||||
|
acceptStream, err := carol.ChannelAcceptor(ctxc)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
go acceptChannel(t, true, acceptStream)
|
||||||
|
}
|
||||||
|
|
||||||
bobChanPoint := openChannelAndAssert(
|
bobChanPoint := openChannelAndAssert(
|
||||||
t, net, bob, carol,
|
t, net, bob, carol,
|
||||||
lntest.OpenChannelParams{
|
lntest.OpenChannelParams{
|
||||||
@ -329,6 +361,12 @@ func createThreeHopNetwork(t *harnessTest, net *lntest.NetworkHarness,
|
|||||||
ZeroConf: zeroConf,
|
ZeroConf: zeroConf,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Remove the ChannelAcceptor for Carol.
|
||||||
|
if zeroConf {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
err = bob.WaitForNetworkChannelOpen(bobChanPoint)
|
err = bob.WaitForNetworkChannelOpen(bobChanPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("alice didn't report channel: %v", err)
|
t.Fatalf("alice didn't report channel: %v", err)
|
||||||
|
@ -19,6 +19,8 @@ import (
|
|||||||
// testZeroConfChannelOpen tests that opening a zero-conf channel works and
|
// testZeroConfChannelOpen tests that opening a zero-conf channel works and
|
||||||
// sending payments also works.
|
// sending payments also works.
|
||||||
func testZeroConfChannelOpen(net *lntest.NetworkHarness, t *harnessTest) {
|
func testZeroConfChannelOpen(net *lntest.NetworkHarness, t *harnessTest) {
|
||||||
|
ctxb := context.Background()
|
||||||
|
|
||||||
// Since option-scid-alias is opt-in, the provided harness nodes will
|
// Since option-scid-alias is opt-in, the provided harness nodes will
|
||||||
// not have the feature bit set. Also need to set anchors as those are
|
// not have the feature bit set. Also need to set anchors as those are
|
||||||
// default-off in itests.
|
// default-off in itests.
|
||||||
@ -44,7 +46,6 @@ func testZeroConfChannelOpen(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Wait for both Bob and Carol to view the channel as active.
|
// Wait for both Bob and Carol to view the channel as active.
|
||||||
ctxb := context.Background()
|
|
||||||
err := net.Bob.WaitForNetworkChannelOpen(fundingPoint)
|
err := net.Bob.WaitForNetworkChannelOpen(fundingPoint)
|
||||||
require.NoError(t.t, err, "bob didn't report channel")
|
require.NoError(t.t, err, "bob didn't report channel")
|
||||||
err = carol.WaitForNetworkChannelOpen(fundingPoint)
|
err = carol.WaitForNetworkChannelOpen(fundingPoint)
|
||||||
@ -60,6 +61,12 @@ func testZeroConfChannelOpen(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
// Ensure that both Carol and Dave are connected.
|
// Ensure that both Carol and Dave are connected.
|
||||||
net.EnsureConnected(t.t, carol, dave)
|
net.EnsureConnected(t.t, carol, dave)
|
||||||
|
|
||||||
|
// Setup a ChannelAcceptor for Dave.
|
||||||
|
ctxc, cancel := context.WithCancel(ctxb)
|
||||||
|
acceptStream, err := dave.ChannelAcceptor(ctxc)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
go acceptChannel(t, true, acceptStream)
|
||||||
|
|
||||||
// Open a private zero-conf anchors channel of 1M satoshis.
|
// Open a private zero-conf anchors channel of 1M satoshis.
|
||||||
params := lntest.OpenChannelParams{
|
params := lntest.OpenChannelParams{
|
||||||
Amt: chanAmt,
|
Amt: chanAmt,
|
||||||
@ -69,6 +76,9 @@ func testZeroConfChannelOpen(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
}
|
}
|
||||||
chanOpenUpdate := openChannelStream(t, net, carol, dave, params)
|
chanOpenUpdate := openChannelStream(t, net, carol, dave, params)
|
||||||
|
|
||||||
|
// Remove the ChannelAcceptor.
|
||||||
|
cancel()
|
||||||
|
|
||||||
// We should receive the OpenStatusUpdate_ChanOpen update without
|
// We should receive the OpenStatusUpdate_ChanOpen update without
|
||||||
// having to mine any blocks.
|
// having to mine any blocks.
|
||||||
fundingPoint2, err := net.WaitForChannelOpen(chanOpenUpdate)
|
fundingPoint2, err := net.WaitForChannelOpen(chanOpenUpdate)
|
||||||
@ -153,10 +163,19 @@ func testZeroConfChannelOpen(net *lntest.NetworkHarness, t *harnessTest) {
|
|||||||
// Give Eve some coins to fund the channel.
|
// Give Eve some coins to fund the channel.
|
||||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, eve)
|
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, eve)
|
||||||
|
|
||||||
|
// Setup a ChannelAcceptor.
|
||||||
|
ctxc, cancel = context.WithCancel(ctxb)
|
||||||
|
acceptStream, err = carol.ChannelAcceptor(ctxc)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
go acceptChannel(t, true, acceptStream)
|
||||||
|
|
||||||
// We'll open a public zero-conf anchors channel of 1M satoshis.
|
// We'll open a public zero-conf anchors channel of 1M satoshis.
|
||||||
params.Private = false
|
params.Private = false
|
||||||
chanOpenUpdate2 := openChannelStream(t, net, eve, carol, params)
|
chanOpenUpdate2 := openChannelStream(t, net, eve, carol, params)
|
||||||
|
|
||||||
|
// Remove the ChannelAcceptor.
|
||||||
|
cancel()
|
||||||
|
|
||||||
// Wait to receive the OpenStatusUpdate_ChanOpen update.
|
// Wait to receive the OpenStatusUpdate_ChanOpen update.
|
||||||
fundingPoint3, err := net.WaitForChannelOpen(chanOpenUpdate2)
|
fundingPoint3, err := net.WaitForChannelOpen(chanOpenUpdate2)
|
||||||
require.NoError(t.t, err, "error while waiting for channel open")
|
require.NoError(t.t, err, "error while waiting for channel open")
|
||||||
@ -581,6 +600,12 @@ func testPrivateUpdateAlias(net *lntest.NetworkHarness, t *harnessTest,
|
|||||||
err = dave.WaitForNetworkChannelOpen(fundingPoint)
|
err = dave.WaitForNetworkChannelOpen(fundingPoint)
|
||||||
require.NoError(t.t, err, "dave didn't report channel")
|
require.NoError(t.t, err, "dave didn't report channel")
|
||||||
|
|
||||||
|
// Setup a ChannelAcceptor for Dave.
|
||||||
|
ctxc, cancel := context.WithCancel(ctxb)
|
||||||
|
acceptStream, err := dave.ChannelAcceptor(ctxc)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
go acceptChannel(t, zeroConf, acceptStream)
|
||||||
|
|
||||||
// Open a private channel, optionally specifying a channel-type.
|
// Open a private channel, optionally specifying a channel-type.
|
||||||
params := lntest.OpenChannelParams{
|
params := lntest.OpenChannelParams{
|
||||||
Amt: chanAmt,
|
Amt: chanAmt,
|
||||||
@ -592,6 +617,9 @@ func testPrivateUpdateAlias(net *lntest.NetworkHarness, t *harnessTest,
|
|||||||
}
|
}
|
||||||
chanOpenUpdate := openChannelStream(t, net, carol, dave, params)
|
chanOpenUpdate := openChannelStream(t, net, carol, dave, params)
|
||||||
|
|
||||||
|
// Remove the ChannelAcceptor.
|
||||||
|
cancel()
|
||||||
|
|
||||||
if !zeroConf {
|
if !zeroConf {
|
||||||
// If this is not a zero-conf channel, mine a single block to
|
// If this is not a zero-conf channel, mine a single block to
|
||||||
// confirm the channel.
|
// confirm the channel.
|
||||||
|
@ -493,3 +493,21 @@ func getOutputIndex(t *harnessTest, miner *lntest.HarnessMiner,
|
|||||||
|
|
||||||
return p2trOutputIndex
|
return p2trOutputIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// acceptChannel is used to accept a single channel that comes across. This
|
||||||
|
// should be run in a goroutine and is used to test nodes with the zero-conf
|
||||||
|
// feature bit.
|
||||||
|
func acceptChannel(t *harnessTest, zeroConf bool,
|
||||||
|
stream lnrpc.Lightning_ChannelAcceptorClient) {
|
||||||
|
|
||||||
|
req, err := stream.Recv()
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
|
||||||
|
resp := &lnrpc.ChannelAcceptResponse{
|
||||||
|
Accept: true,
|
||||||
|
PendingChanId: req.PendingChanId,
|
||||||
|
ZeroConf: zeroConf,
|
||||||
|
}
|
||||||
|
err = stream.Send(resp)
|
||||||
|
require.NoError(t.t, err)
|
||||||
|
}
|
||||||
|
@ -612,7 +612,7 @@ type rpcServer struct {
|
|||||||
|
|
||||||
// chanPredicate is used in the bidirectional ChannelAcceptor streaming
|
// chanPredicate is used in the bidirectional ChannelAcceptor streaming
|
||||||
// method.
|
// method.
|
||||||
chanPredicate *chanacceptor.ChainedAcceptor
|
chanPredicate chanacceptor.MultiplexAcceptor
|
||||||
|
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
|
|
||||||
@ -676,7 +676,7 @@ func newRPCServer(cfg *Config, interceptorChain *rpcperms.InterceptorChain,
|
|||||||
func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
|
func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
|
||||||
subServerCgs *subRPCServerConfigs, atpl *autopilot.Manager,
|
subServerCgs *subRPCServerConfigs, atpl *autopilot.Manager,
|
||||||
invoiceRegistry *invoices.InvoiceRegistry, tower *watchtower.Standalone,
|
invoiceRegistry *invoices.InvoiceRegistry, tower *watchtower.Standalone,
|
||||||
chanPredicate *chanacceptor.ChainedAcceptor) error {
|
chanPredicate chanacceptor.MultiplexAcceptor) error {
|
||||||
|
|
||||||
// Set up router rpc backend.
|
// Set up router rpc backend.
|
||||||
selfNode, err := s.graphDB.SourceNode()
|
selfNode, err := s.graphDB.SourceNode()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user