Merge pull request #4782 from cfromknecht/anchor-wtserver
watchtower: anchor channel support
This commit is contained in:
commit
94f8311667
@ -250,19 +250,44 @@ var policyCommand = cli.Command{
|
|||||||
Name: "policy",
|
Name: "policy",
|
||||||
Usage: "Display the active watchtower client policy configuration.",
|
Usage: "Display the active watchtower client policy configuration.",
|
||||||
Action: actionDecorator(policy),
|
Action: actionDecorator(policy),
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "legacy",
|
||||||
|
Usage: "Retrieve the legacy tower client's current " +
|
||||||
|
"policy. (default)",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "anchor",
|
||||||
|
Usage: "Retrieve the anchor tower client's current policy.",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func policy(ctx *cli.Context) error {
|
func policy(ctx *cli.Context) error {
|
||||||
// Display the command's help message if the number of arguments/flags
|
// Display the command's help message if the number of arguments/flags
|
||||||
// is not what we expect.
|
// is not what we expect.
|
||||||
if ctx.NArg() > 0 || ctx.NumFlags() > 0 {
|
if ctx.NArg() > 0 || ctx.NumFlags() > 1 {
|
||||||
return cli.ShowCommandHelp(ctx, "policy")
|
return cli.ShowCommandHelp(ctx, "policy")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var policyType wtclientrpc.PolicyType
|
||||||
|
switch {
|
||||||
|
case ctx.Bool("anchor"):
|
||||||
|
policyType = wtclientrpc.PolicyType_ANCHOR
|
||||||
|
case ctx.Bool("legacy"):
|
||||||
|
policyType = wtclientrpc.PolicyType_LEGACY
|
||||||
|
|
||||||
|
// For backwards compatibility with original rpc behavior.
|
||||||
|
default:
|
||||||
|
policyType = wtclientrpc.PolicyType_LEGACY
|
||||||
|
}
|
||||||
|
|
||||||
client, cleanUp := getWtclient(ctx)
|
client, cleanUp := getWtclient(ctx)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
|
|
||||||
req := &wtclientrpc.PolicyRequest{}
|
req := &wtclientrpc.PolicyRequest{
|
||||||
|
PolicyType: policyType,
|
||||||
|
}
|
||||||
resp, err := client.Policy(context.Background(), req)
|
resp, err := client.Policy(context.Background(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -183,7 +183,8 @@ type TowerClient interface {
|
|||||||
// abide by the negotiated policy. If the channel we're trying to back
|
// abide by the negotiated policy. If the channel we're trying to back
|
||||||
// up doesn't have a tweak for the remote party's output, then
|
// up doesn't have a tweak for the remote party's output, then
|
||||||
// isTweakless should be true.
|
// isTweakless should be true.
|
||||||
BackupState(*lnwire.ChannelID, *lnwallet.BreachRetribution, bool) error
|
BackupState(*lnwire.ChannelID, *lnwallet.BreachRetribution,
|
||||||
|
channeldb.ChannelType) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// InterceptableHtlcForwarder is the interface to set the interceptor
|
// InterceptableHtlcForwarder is the interface to set the interceptor
|
||||||
|
@ -256,7 +256,7 @@ type ChannelLinkConfig struct {
|
|||||||
|
|
||||||
// TowerClient is an optional engine that manages the signing,
|
// TowerClient is an optional engine that manages the signing,
|
||||||
// encrypting, and uploading of justice transactions to the daemon's
|
// encrypting, and uploading of justice transactions to the daemon's
|
||||||
// configured set of watchtowers.
|
// configured set of watchtowers for legacy channels.
|
||||||
TowerClient TowerClient
|
TowerClient TowerClient
|
||||||
|
|
||||||
// MaxOutgoingCltvExpiry is the maximum outgoing timelock that the link
|
// MaxOutgoingCltvExpiry is the maximum outgoing timelock that the link
|
||||||
@ -435,12 +435,7 @@ func (l *channelLink) Start() error {
|
|||||||
|
|
||||||
// If the config supplied watchtower client, ensure the channel is
|
// If the config supplied watchtower client, ensure the channel is
|
||||||
// registered before trying to use it during operation.
|
// registered before trying to use it during operation.
|
||||||
// TODO(halseth): support anchor types for watchtower.
|
if l.cfg.TowerClient != nil {
|
||||||
state := l.channel.State()
|
|
||||||
if l.cfg.TowerClient != nil && state.ChanType.HasAnchors() {
|
|
||||||
l.log.Warnf("Skipping tower registration for anchor " +
|
|
||||||
"channel type")
|
|
||||||
} else if l.cfg.TowerClient != nil && !state.ChanType.HasAnchors() {
|
|
||||||
err := l.cfg.TowerClient.RegisterChannel(l.ChanID())
|
err := l.cfg.TowerClient.RegisterChannel(l.ChanID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1835,14 +1830,9 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a tower client, we'll proceed in backing up the
|
// If we have a tower client for this channel type, we'll
|
||||||
// state that was just revoked.
|
if l.cfg.TowerClient != nil {
|
||||||
// TODO(halseth): support anchor types for watchtower.
|
state := l.channel.State()
|
||||||
state := l.channel.State()
|
|
||||||
if l.cfg.TowerClient != nil && state.ChanType.HasAnchors() {
|
|
||||||
l.log.Warnf("Skipping tower backup for anchor " +
|
|
||||||
"channel type")
|
|
||||||
} else if l.cfg.TowerClient != nil && !state.ChanType.HasAnchors() {
|
|
||||||
breachInfo, err := lnwallet.NewBreachRetribution(
|
breachInfo, err := lnwallet.NewBreachRetribution(
|
||||||
state, state.RemoteCommitment.CommitHeight-1, 0,
|
state, state.RemoteCommitment.CommitHeight-1, 0,
|
||||||
)
|
)
|
||||||
@ -1852,10 +1842,9 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chanType := l.channel.State().ChanType
|
|
||||||
chanID := l.ChanID()
|
chanID := l.ChanID()
|
||||||
err = l.cfg.TowerClient.BackupState(
|
err = l.cfg.TowerClient.BackupState(
|
||||||
&chanID, breachInfo, chanType.IsTweakless(),
|
&chanID, breachInfo, state.ChanType,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.fail(LinkFailureError{code: ErrInternalError},
|
l.fail(LinkFailureError{code: ErrInternalError},
|
||||||
|
@ -19,6 +19,10 @@ type Config struct {
|
|||||||
// through the watchtower RPC subserver.
|
// through the watchtower RPC subserver.
|
||||||
Client wtclient.Client
|
Client wtclient.Client
|
||||||
|
|
||||||
|
// AnchorClient is the backing watchtower client for anchor channels that
|
||||||
|
// we'll interact through the watchtower RPC subserver.
|
||||||
|
AnchorClient wtclient.Client
|
||||||
|
|
||||||
// Resolver is a custom resolver that will be used to resolve watchtower
|
// Resolver is a custom resolver that will be used to resolve watchtower
|
||||||
// addresses to ensure we don't leak any information when running over
|
// addresses to ensure we don't leak any information when running over
|
||||||
// non-clear networks, e.g. Tor, etc.
|
// non-clear networks, e.g. Tor, etc.
|
||||||
|
@ -14,6 +14,8 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/watchtower"
|
"github.com/lightningnetwork/lnd/watchtower"
|
||||||
"github.com/lightningnetwork/lnd/watchtower/wtclient"
|
"github.com/lightningnetwork/lnd/watchtower/wtclient"
|
||||||
|
"github.com/lightningnetwork/lnd/watchtower/wtdb"
|
||||||
|
"github.com/lightningnetwork/lnd/watchtower/wtpolicy"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"gopkg.in/macaroon-bakery.v2/bakery"
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||||
)
|
)
|
||||||
@ -176,9 +178,14 @@ func (c *WatchtowerClient) AddTower(ctx context.Context,
|
|||||||
IdentityKey: pubKey,
|
IdentityKey: pubKey,
|
||||||
Address: addr,
|
Address: addr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(conner): make atomic via multiplexed client
|
||||||
if err := c.cfg.Client.AddTower(towerAddr); err != nil {
|
if err := c.cfg.Client.AddTower(towerAddr); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := c.cfg.AnchorClient.AddTower(towerAddr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &AddTowerResponse{}, nil
|
return &AddTowerResponse{}, nil
|
||||||
}
|
}
|
||||||
@ -211,7 +218,13 @@ func (c *WatchtowerClient) RemoveTower(ctx context.Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.cfg.Client.RemoveTower(pubKey, addr); err != nil {
|
// TODO(conner): make atomic via multiplexed client
|
||||||
|
err = c.cfg.Client.RemoveTower(pubKey, addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = c.cfg.AnchorClient.RemoveTower(pubKey, addr)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,11 +239,25 @@ func (c *WatchtowerClient) ListTowers(ctx context.Context,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
towers, err := c.cfg.Client.RegisteredTowers()
|
anchorTowers, err := c.cfg.AnchorClient.RegisteredTowers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
legacyTowers, err := c.cfg.Client.RegisteredTowers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter duplicates.
|
||||||
|
towers := make(map[wtdb.TowerID]*wtclient.RegisteredTower)
|
||||||
|
for _, tower := range anchorTowers {
|
||||||
|
towers[tower.Tower.ID] = tower
|
||||||
|
}
|
||||||
|
for _, tower := range legacyTowers {
|
||||||
|
towers[tower.Tower.ID] = tower
|
||||||
|
}
|
||||||
|
|
||||||
rpcTowers := make([]*Tower, 0, len(towers))
|
rpcTowers := make([]*Tower, 0, len(towers))
|
||||||
for _, tower := range towers {
|
for _, tower := range towers {
|
||||||
rpcTower := marshallTower(tower, req.IncludeSessions)
|
rpcTower := marshallTower(tower, req.IncludeSessions)
|
||||||
@ -253,7 +280,11 @@ func (c *WatchtowerClient) GetTowerInfo(ctx context.Context,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tower, err := c.cfg.Client.LookupTower(pubKey)
|
var tower *wtclient.RegisteredTower
|
||||||
|
tower, err = c.cfg.Client.LookupTower(pubKey)
|
||||||
|
if err == wtdb.ErrTowerNotFound {
|
||||||
|
tower, err = c.cfg.AnchorClient.LookupTower(pubKey)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -269,7 +300,24 @@ func (c *WatchtowerClient) Stats(ctx context.Context,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stats := c.cfg.Client.Stats()
|
clientStats := []wtclient.ClientStats{
|
||||||
|
c.cfg.Client.Stats(),
|
||||||
|
c.cfg.AnchorClient.Stats(),
|
||||||
|
}
|
||||||
|
|
||||||
|
var stats wtclient.ClientStats
|
||||||
|
for i := range clientStats {
|
||||||
|
// Grab a reference to the slice index rather than copying bc
|
||||||
|
// ClientStats contains a lock which cannot be copied by value.
|
||||||
|
stat := &clientStats[i]
|
||||||
|
|
||||||
|
stats.NumTasksAccepted += stat.NumTasksAccepted
|
||||||
|
stats.NumTasksIneligible += stat.NumTasksIneligible
|
||||||
|
stats.NumTasksReceived += stat.NumTasksReceived
|
||||||
|
stats.NumSessionsAcquired += stat.NumSessionsAcquired
|
||||||
|
stats.NumSessionsExhausted += stat.NumSessionsExhausted
|
||||||
|
}
|
||||||
|
|
||||||
return &StatsResponse{
|
return &StatsResponse{
|
||||||
NumBackups: uint32(stats.NumTasksAccepted),
|
NumBackups: uint32(stats.NumTasksAccepted),
|
||||||
NumFailedBackups: uint32(stats.NumTasksIneligible),
|
NumFailedBackups: uint32(stats.NumTasksIneligible),
|
||||||
@ -287,7 +335,17 @@ func (c *WatchtowerClient) Policy(ctx context.Context,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
policy := c.cfg.Client.Policy()
|
var policy wtpolicy.Policy
|
||||||
|
switch req.PolicyType {
|
||||||
|
case PolicyType_LEGACY:
|
||||||
|
policy = c.cfg.Client.Policy()
|
||||||
|
case PolicyType_ANCHOR:
|
||||||
|
policy = c.cfg.AnchorClient.Policy()
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown policy type: %v",
|
||||||
|
req.PolicyType)
|
||||||
|
}
|
||||||
|
|
||||||
return &PolicyResponse{
|
return &PolicyResponse{
|
||||||
MaxUpdates: uint32(policy.MaxUpdates),
|
MaxUpdates: uint32(policy.MaxUpdates),
|
||||||
SweepSatPerByte: uint32(policy.SweepFeeRate.FeePerKVByte() / 1000),
|
SweepSatPerByte: uint32(policy.SweepFeeRate.FeePerKVByte() / 1000),
|
||||||
|
@ -24,6 +24,33 @@ var _ = math.Inf
|
|||||||
// proto package needs to be updated.
|
// proto package needs to be updated.
|
||||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
|
type PolicyType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Selects the policy from the legacy tower client.
|
||||||
|
PolicyType_LEGACY PolicyType = 0
|
||||||
|
// Selects the policy from the anchor tower client.
|
||||||
|
PolicyType_ANCHOR PolicyType = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
var PolicyType_name = map[int32]string{
|
||||||
|
0: "LEGACY",
|
||||||
|
1: "ANCHOR",
|
||||||
|
}
|
||||||
|
|
||||||
|
var PolicyType_value = map[string]int32{
|
||||||
|
"LEGACY": 0,
|
||||||
|
"ANCHOR": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x PolicyType) String() string {
|
||||||
|
return proto.EnumName(PolicyType_name, int32(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PolicyType) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_b5f4e7d95a641af2, []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
type AddTowerRequest struct {
|
type AddTowerRequest struct {
|
||||||
// The identifying public key of the watchtower to add.
|
// The identifying public key of the watchtower to add.
|
||||||
Pubkey []byte `protobuf:"bytes,1,opt,name=pubkey,proto3" json:"pubkey,omitempty"`
|
Pubkey []byte `protobuf:"bytes,1,opt,name=pubkey,proto3" json:"pubkey,omitempty"`
|
||||||
@ -579,9 +606,12 @@ func (m *StatsResponse) GetNumSessionsExhausted() uint32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PolicyRequest struct {
|
type PolicyRequest struct {
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
//
|
||||||
XXX_unrecognized []byte `json:"-"`
|
//The client type from which to retrieve the active offering policy.
|
||||||
XXX_sizecache int32 `json:"-"`
|
PolicyType PolicyType `protobuf:"varint,1,opt,name=policy_type,json=policyType,proto3,enum=wtclientrpc.PolicyType" json:"policy_type,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PolicyRequest) Reset() { *m = PolicyRequest{} }
|
func (m *PolicyRequest) Reset() { *m = PolicyRequest{} }
|
||||||
@ -609,6 +639,13 @@ func (m *PolicyRequest) XXX_DiscardUnknown() {
|
|||||||
|
|
||||||
var xxx_messageInfo_PolicyRequest proto.InternalMessageInfo
|
var xxx_messageInfo_PolicyRequest proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *PolicyRequest) GetPolicyType() PolicyType {
|
||||||
|
if m != nil {
|
||||||
|
return m.PolicyType
|
||||||
|
}
|
||||||
|
return PolicyType_LEGACY
|
||||||
|
}
|
||||||
|
|
||||||
type PolicyResponse struct {
|
type PolicyResponse struct {
|
||||||
//
|
//
|
||||||
//The maximum number of updates each session we negotiate with watchtowers
|
//The maximum number of updates each session we negotiate with watchtowers
|
||||||
@ -663,6 +700,7 @@ func (m *PolicyResponse) GetSweepSatPerByte() uint32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
proto.RegisterEnum("wtclientrpc.PolicyType", PolicyType_name, PolicyType_value)
|
||||||
proto.RegisterType((*AddTowerRequest)(nil), "wtclientrpc.AddTowerRequest")
|
proto.RegisterType((*AddTowerRequest)(nil), "wtclientrpc.AddTowerRequest")
|
||||||
proto.RegisterType((*AddTowerResponse)(nil), "wtclientrpc.AddTowerResponse")
|
proto.RegisterType((*AddTowerResponse)(nil), "wtclientrpc.AddTowerResponse")
|
||||||
proto.RegisterType((*RemoveTowerRequest)(nil), "wtclientrpc.RemoveTowerRequest")
|
proto.RegisterType((*RemoveTowerRequest)(nil), "wtclientrpc.RemoveTowerRequest")
|
||||||
@ -681,50 +719,54 @@ func init() {
|
|||||||
func init() { proto.RegisterFile("wtclientrpc/wtclient.proto", fileDescriptor_b5f4e7d95a641af2) }
|
func init() { proto.RegisterFile("wtclientrpc/wtclient.proto", fileDescriptor_b5f4e7d95a641af2) }
|
||||||
|
|
||||||
var fileDescriptor_b5f4e7d95a641af2 = []byte{
|
var fileDescriptor_b5f4e7d95a641af2 = []byte{
|
||||||
// 682 bytes of a gzipped FileDescriptorProto
|
// 739 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x4d, 0x6f, 0xd3, 0x40,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xcd, 0x6e, 0xd3, 0x4a,
|
||||||
0x10, 0x95, 0x1b, 0x12, 0xd2, 0x49, 0xda, 0xa4, 0x1b, 0x5a, 0x19, 0x53, 0x48, 0xf0, 0x29, 0x7c,
|
0x14, 0xbe, 0x6e, 0x6e, 0x72, 0xd3, 0x93, 0xb4, 0x4d, 0x27, 0xb7, 0xbd, 0xb9, 0xa6, 0x90, 0x60,
|
||||||
0x28, 0x11, 0x2d, 0x48, 0x9c, 0x2a, 0xda, 0x42, 0x2b, 0x24, 0x90, 0x22, 0x17, 0x04, 0xe2, 0x80,
|
0xb1, 0x08, 0x05, 0x25, 0x22, 0x05, 0xa9, 0xab, 0x8a, 0x34, 0xb4, 0xa5, 0x52, 0x81, 0xc8, 0x2d,
|
||||||
0xb5, 0xb1, 0xb7, 0x89, 0x55, 0x7b, 0xed, 0x7a, 0xd7, 0x4d, 0xf2, 0xa3, 0xf8, 0x19, 0xfc, 0x00,
|
0xe2, 0x67, 0x81, 0x35, 0xb1, 0xa7, 0x89, 0x55, 0x7b, 0xec, 0xda, 0xe3, 0x26, 0x79, 0x28, 0x1e,
|
||||||
0xfe, 0x0d, 0x47, 0xe4, 0xf5, 0xda, 0xb1, 0x1b, 0x47, 0x1c, 0xe0, 0x16, 0xcf, 0x7b, 0xfb, 0x3c,
|
0x83, 0x07, 0xe0, 0x6d, 0x58, 0x22, 0x8f, 0xc7, 0x8e, 0xdd, 0x3a, 0x62, 0x01, 0x3b, 0xfb, 0x7c,
|
||||||
0x7e, 0xf3, 0x32, 0x0b, 0xda, 0x8c, 0x5b, 0xae, 0x43, 0x28, 0x0f, 0x03, 0x6b, 0x98, 0xfe, 0x1e,
|
0xdf, 0x7c, 0x3e, 0xf3, 0x9d, 0x1f, 0x83, 0x3c, 0x65, 0xba, 0x65, 0x12, 0xca, 0x3c, 0x57, 0xef,
|
||||||
0x04, 0xa1, 0xcf, 0x7d, 0xd4, 0xc8, 0x61, 0xfa, 0x29, 0xb4, 0x8e, 0x6d, 0xfb, 0x93, 0x3f, 0x23,
|
0xc6, 0xcf, 0x1d, 0xd7, 0x73, 0x98, 0x83, 0x2a, 0x29, 0x4c, 0x19, 0xc0, 0x46, 0xdf, 0x30, 0x2e,
|
||||||
0xa1, 0x41, 0xae, 0x23, 0xc2, 0x38, 0xda, 0x83, 0x5a, 0x10, 0x8d, 0xaf, 0xc8, 0x42, 0x55, 0x7a,
|
0x9c, 0x29, 0xf1, 0x54, 0x72, 0x1d, 0x10, 0x9f, 0xa1, 0x6d, 0x28, 0xb9, 0xc1, 0xe8, 0x8a, 0xcc,
|
||||||
0x4a, 0xbf, 0x69, 0xc8, 0x27, 0xa4, 0xc2, 0x5d, 0x6c, 0xdb, 0x21, 0x61, 0x4c, 0xdd, 0xe8, 0x29,
|
0x1b, 0x52, 0x4b, 0x6a, 0x57, 0x55, 0xf1, 0x86, 0x1a, 0xf0, 0x0f, 0x36, 0x0c, 0x8f, 0xf8, 0x7e,
|
||||||
0xfd, 0x4d, 0x23, 0x7d, 0xd4, 0x11, 0xb4, 0x97, 0x22, 0x2c, 0xf0, 0x29, 0x23, 0xfa, 0x19, 0x20,
|
0x63, 0xa5, 0x25, 0xb5, 0x57, 0xd5, 0xf8, 0x55, 0x41, 0x50, 0x5b, 0x88, 0xf8, 0xae, 0x43, 0x7d,
|
||||||
0x83, 0x78, 0xfe, 0x0d, 0xf9, 0x47, 0xed, 0x5d, 0xe8, 0x14, 0x74, 0xa4, 0xfc, 0x57, 0xe8, 0x9c,
|
0xa2, 0x1c, 0x03, 0x52, 0x89, 0xed, 0xdc, 0x90, 0xdf, 0xd4, 0xde, 0x82, 0x7a, 0x46, 0x47, 0xc8,
|
||||||
0x13, 0x2e, 0x6a, 0xef, 0xe9, 0xa5, 0xff, 0x37, 0xfd, 0x27, 0xd0, 0x76, 0xa8, 0xe5, 0x46, 0x36,
|
0x7f, 0x84, 0xfa, 0x09, 0x61, 0x3c, 0x76, 0x4a, 0x2f, 0x9d, 0x5f, 0xe9, 0x3f, 0x86, 0x9a, 0x49,
|
||||||
0x31, 0x19, 0x61, 0xcc, 0xf1, 0x69, 0xf2, 0xa2, 0xba, 0xd1, 0x92, 0xf5, 0x0b, 0x59, 0xd6, 0x7f,
|
0x75, 0x2b, 0x30, 0x88, 0xe6, 0x13, 0xdf, 0x37, 0x1d, 0x1a, 0x7d, 0xa8, 0xac, 0x6e, 0x88, 0xf8,
|
||||||
0x28, 0xd0, 0x14, 0xba, 0xb2, 0x82, 0xba, 0xd0, 0xa0, 0x91, 0x67, 0x8e, 0xb1, 0x75, 0x15, 0x05,
|
0xb9, 0x08, 0x2b, 0x5f, 0x25, 0xa8, 0x72, 0x5d, 0x11, 0x41, 0x4d, 0xa8, 0xd0, 0xc0, 0xd6, 0x46,
|
||||||
0x4c, 0x08, 0x6f, 0x19, 0x40, 0x23, 0xef, 0x24, 0xa9, 0xa0, 0x01, 0x74, 0x62, 0x42, 0x40, 0xa8,
|
0x58, 0xbf, 0x0a, 0x5c, 0x9f, 0x0b, 0xaf, 0xa9, 0x40, 0x03, 0xfb, 0x30, 0x8a, 0xa0, 0x0e, 0xd4,
|
||||||
0xed, 0xd0, 0x49, 0x46, 0xdc, 0x10, 0xc4, 0x1d, 0x1a, 0x79, 0xa3, 0x04, 0x49, 0xf9, 0x5d, 0x68,
|
0x43, 0x82, 0x4b, 0xa8, 0x61, 0xd2, 0x71, 0x42, 0x5c, 0xe1, 0xc4, 0x4d, 0x1a, 0xd8, 0xc3, 0x08,
|
||||||
0x78, 0x78, 0x9e, 0xf1, 0x2a, 0x89, 0xa0, 0x87, 0xe7, 0x29, 0xe1, 0x19, 0x20, 0x36, 0x23, 0x24,
|
0x89, 0xf9, 0x4d, 0xa8, 0xd8, 0x78, 0x96, 0xf0, 0x0a, 0x91, 0xa0, 0x8d, 0x67, 0x31, 0xe1, 0x09,
|
||||||
0x30, 0x19, 0xe6, 0x66, 0x40, 0x42, 0x73, 0xbc, 0xe0, 0x44, 0xbd, 0x23, 0x78, 0x2d, 0x81, 0x5c,
|
0x20, 0x7f, 0x4a, 0x88, 0xab, 0xf9, 0x98, 0x69, 0x2e, 0xf1, 0xb4, 0xd1, 0x9c, 0x91, 0xc6, 0xdf,
|
||||||
0x60, 0x3e, 0x22, 0xe1, 0xc9, 0x82, 0x13, 0xfd, 0x97, 0x02, 0x55, 0xd1, 0xef, 0xda, 0x8f, 0xdf,
|
0x9c, 0xb7, 0xc1, 0x91, 0x73, 0xcc, 0x86, 0xc4, 0x3b, 0x9c, 0x33, 0xa2, 0x7c, 0x97, 0xa0, 0xc8,
|
||||||
0x87, 0x4d, 0xe9, 0x26, 0x89, 0xbb, 0xaa, 0xf4, 0x37, 0x8d, 0x65, 0x01, 0xbd, 0x06, 0x15, 0x5b,
|
0xf3, 0x5d, 0x7a, 0xf9, 0x1d, 0x58, 0x15, 0x6e, 0x92, 0x30, 0xab, 0x42, 0x7b, 0x55, 0x5d, 0x04,
|
||||||
0xdc, 0xb9, 0xc9, 0x9c, 0x31, 0x2d, 0x4c, 0x6d, 0xc7, 0xc6, 0x9c, 0x88, 0xd6, 0xea, 0xc6, 0x5e,
|
0xd0, 0x3e, 0x34, 0xb0, 0xce, 0xcc, 0x9b, 0xc4, 0x19, 0x4d, 0xc7, 0xd4, 0x30, 0x0d, 0xcc, 0x08,
|
||||||
0x82, 0x4b, 0x3f, 0x4e, 0x53, 0x14, 0x3d, 0x86, 0x66, 0xfc, 0xdd, 0x99, 0xa1, 0x49, 0x83, 0xb1,
|
0x4f, 0xad, 0xac, 0x6e, 0x47, 0xb8, 0xf0, 0x63, 0x10, 0xa3, 0xe8, 0x21, 0x54, 0xc3, 0x7b, 0x27,
|
||||||
0x59, 0xa9, 0x99, 0xe8, 0x15, 0xd4, 0x33, 0xb8, 0xda, 0xab, 0xf4, 0x1b, 0x07, 0xf7, 0x07, 0xb9,
|
0x86, 0x46, 0x09, 0x86, 0x66, 0xc5, 0x66, 0xa2, 0x17, 0x50, 0x4e, 0xe0, 0x62, 0xab, 0xd0, 0xae,
|
||||||
0xf8, 0x0d, 0xf2, 0x46, 0x1b, 0x19, 0x55, 0x3f, 0x82, 0x9d, 0x0f, 0x0e, 0x4b, 0xc6, 0xcb, 0xd2,
|
0xf4, 0xfe, 0xef, 0xa4, 0xda, 0xaf, 0x93, 0x36, 0x5a, 0x4d, 0xa8, 0xca, 0x01, 0x6c, 0x9e, 0x99,
|
||||||
0xd9, 0x96, 0xcd, 0x50, 0x29, 0x9f, 0xe1, 0x1b, 0x40, 0xf9, 0xf3, 0x49, 0x66, 0xd0, 0x53, 0xa8,
|
0x7e, 0x54, 0x5e, 0x3f, 0xae, 0x6d, 0x5e, 0x0d, 0xa5, 0xfc, 0x1a, 0xbe, 0x04, 0x94, 0x3e, 0x1f,
|
||||||
0x71, 0x51, 0x51, 0x15, 0xd1, 0x0a, 0x5a, 0x6d, 0xc5, 0x90, 0x0c, 0x7d, 0x1b, 0x9a, 0x17, 0x1c,
|
0xf5, 0x0c, 0xda, 0x85, 0x12, 0xe3, 0x91, 0x86, 0xc4, 0x53, 0x41, 0x77, 0x53, 0x51, 0x05, 0x43,
|
||||||
0xf3, 0xf4, 0xe5, 0xfa, 0x6f, 0x05, 0xb6, 0x64, 0x41, 0xaa, 0xfd, 0xf7, 0x58, 0x3c, 0x07, 0x14,
|
0x59, 0x87, 0xea, 0x39, 0xc3, 0x2c, 0xfe, 0xb8, 0xf2, 0x43, 0x82, 0x35, 0x11, 0x10, 0x6a, 0x7f,
|
||||||
0xf3, 0x2f, 0xb1, 0xe3, 0x12, 0xfb, 0x56, 0x3a, 0xda, 0x34, 0xf2, 0xce, 0x04, 0x90, 0xb2, 0x0f,
|
0xbc, 0x2d, 0x9e, 0x02, 0x0a, 0xf9, 0x97, 0xd8, 0xb4, 0x88, 0x71, 0xab, 0x3b, 0x6a, 0x34, 0xb0,
|
||||||
0x60, 0x37, 0x6f, 0xbe, 0x89, 0xad, 0xeb, 0xc8, 0x09, 0x89, 0x2d, 0xa7, 0xd0, 0xc9, 0x4d, 0xe1,
|
0x8f, 0x39, 0x10, 0xb3, 0x7b, 0xb0, 0x95, 0x36, 0x5f, 0xc3, 0xfa, 0x75, 0x60, 0x7a, 0xc4, 0x10,
|
||||||
0x58, 0x42, 0xe8, 0x25, 0xec, 0x15, 0xce, 0x90, 0xf9, 0x14, 0x47, 0x8c, 0x13, 0x5b, 0xad, 0x8a,
|
0x55, 0xa8, 0xa7, 0xaa, 0xd0, 0x17, 0x10, 0x7a, 0x0e, 0xdb, 0x99, 0x33, 0x64, 0x36, 0xc1, 0x81,
|
||||||
0x43, 0xf7, 0x72, 0x87, 0xde, 0xa5, 0x98, 0xde, 0x82, 0xad, 0x91, 0xef, 0x3a, 0xd6, 0x22, 0xf5,
|
0xcf, 0x88, 0xd1, 0x28, 0xf2, 0x43, 0xff, 0xa6, 0x0e, 0x1d, 0xc5, 0x98, 0x72, 0x0a, 0x6b, 0x43,
|
||||||
0xe2, 0x3b, 0x6c, 0xa7, 0x85, 0xa5, 0x17, 0x71, 0xa2, 0xa3, 0x20, 0xce, 0x45, 0xe6, 0x85, 0x87,
|
0xc7, 0x32, 0xf5, 0x79, 0x5c, 0x88, 0x7d, 0xa8, 0xb8, 0x3c, 0xa0, 0xb1, 0xb9, 0x4b, 0xf8, 0xcd,
|
||||||
0xe7, 0x9f, 0x93, 0xca, 0x9a, 0x44, 0x6f, 0x94, 0x26, 0xfa, 0xe0, 0x67, 0x05, 0xda, 0x5f, 0x30,
|
0xd7, 0x7b, 0xff, 0x65, 0xcc, 0x8c, 0x0e, 0x5c, 0xcc, 0x5d, 0xa2, 0x82, 0x9b, 0x3c, 0x2b, 0x5f,
|
||||||
0xb7, 0xa6, 0x62, 0x16, 0xa7, 0x62, 0x42, 0xe8, 0x1c, 0xea, 0xe9, 0x8e, 0x41, 0xfb, 0x85, 0xc1,
|
0x60, 0x3d, 0x96, 0x5a, 0xb8, 0x18, 0xce, 0x42, 0xe0, 0x86, 0x1d, 0x95, 0xb8, 0x68, 0xe3, 0xd9,
|
||||||
0xdd, 0xda, 0x5f, 0xda, 0xc3, 0x35, 0xa8, 0xec, 0x75, 0x04, 0x8d, 0xdc, 0x42, 0x41, 0xdd, 0x02,
|
0xfb, 0x28, 0xb2, 0x64, 0x16, 0x56, 0x72, 0x67, 0x61, 0xf7, 0x11, 0xc0, 0xe2, 0xcb, 0x08, 0xa0,
|
||||||
0x7b, 0x75, 0x65, 0x69, 0xbd, 0xf5, 0x04, 0xa9, 0xf8, 0x11, 0x60, 0x99, 0x36, 0xf4, 0xa8, 0xc0,
|
0x74, 0x76, 0x74, 0xd2, 0x1f, 0x7c, 0xaa, 0xfd, 0x15, 0x3e, 0xf7, 0xdf, 0x0e, 0x5e, 0xbf, 0x53,
|
||||||
0x5f, 0x89, 0xb1, 0xd6, 0x5d, 0x8b, 0x4b, 0xb9, 0xb7, 0xd0, 0xcc, 0xaf, 0x36, 0x54, 0x6c, 0xa0,
|
0x6b, 0x52, 0xef, 0x5b, 0x01, 0x6a, 0x1f, 0x30, 0xd3, 0x27, 0xbc, 0xd6, 0x03, 0x9e, 0x34, 0x3a,
|
||||||
0x64, 0xeb, 0x69, 0x25, 0x41, 0x46, 0x47, 0x50, 0x15, 0x79, 0x45, 0xc5, 0x3f, 0x5c, 0x3e, 0xd4,
|
0x81, 0x72, 0xbc, 0xc3, 0xd0, 0x4e, 0xe6, 0x2e, 0xb7, 0xf6, 0xa3, 0x7c, 0x7f, 0x09, 0x2a, 0x6e,
|
||||||
0x9a, 0x56, 0x06, 0xc9, 0x2e, 0x8e, 0xa1, 0x96, 0x0c, 0x19, 0x15, 0x59, 0x85, 0x28, 0x68, 0x0f,
|
0x34, 0x84, 0x4a, 0x6a, 0x61, 0xa1, 0x66, 0x86, 0x7d, 0x77, 0x25, 0xca, 0xad, 0xe5, 0x04, 0xa1,
|
||||||
0x4a, 0xb1, 0x44, 0xe2, 0xe4, 0xf0, 0xdb, 0x8b, 0x89, 0xc3, 0xa7, 0xd1, 0x78, 0x60, 0xf9, 0xde,
|
0xf8, 0x06, 0x60, 0xd1, 0xcd, 0xe8, 0x41, 0x86, 0x7f, 0x67, 0x4c, 0xe4, 0xe6, 0x52, 0x5c, 0xc8,
|
||||||
0xd0, 0x75, 0x26, 0x53, 0x4e, 0x1d, 0x3a, 0xa1, 0x84, 0xcf, 0xfc, 0xf0, 0x6a, 0xe8, 0x52, 0x7b,
|
0xbd, 0x82, 0x6a, 0x7a, 0x75, 0xa2, 0x6c, 0x02, 0x39, 0x5b, 0x55, 0xce, 0x19, 0x14, 0x74, 0x00,
|
||||||
0xe8, 0xd2, 0xfc, 0x05, 0x15, 0x06, 0xd6, 0xb8, 0x26, 0x2e, 0xa9, 0xc3, 0x3f, 0x01, 0x00, 0x00,
|
0x45, 0x3e, 0x0f, 0x28, 0x3b, 0xd0, 0xe9, 0xa1, 0x91, 0xe5, 0x3c, 0x48, 0x64, 0xd1, 0x87, 0x52,
|
||||||
0xff, 0xff, 0x5d, 0xba, 0x03, 0x17, 0xc2, 0x06, 0x00, 0x00,
|
0x54, 0x2a, 0x24, 0xe7, 0x74, 0x4e, 0xac, 0x70, 0x2f, 0x17, 0x8b, 0x24, 0x0e, 0xf7, 0x3e, 0x3f,
|
||||||
|
0x1b, 0x9b, 0x6c, 0x12, 0x8c, 0x3a, 0xba, 0x63, 0x77, 0x2d, 0x73, 0x3c, 0x61, 0xd4, 0xa4, 0x63,
|
||||||
|
0x4a, 0xd8, 0xd4, 0xf1, 0xae, 0xba, 0x16, 0x35, 0xba, 0x16, 0x4d, 0xff, 0x00, 0x3d, 0x57, 0x1f,
|
||||||
|
0x95, 0xf8, 0x4f, 0x70, 0xef, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff, 0x99, 0x5f, 0xe1, 0x8e, 0x22,
|
||||||
|
0x07, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@ -254,10 +254,21 @@ func local_request_WatchtowerClient_Stats_0(ctx context.Context, marshaler runti
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
filter_WatchtowerClient_Policy_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
|
||||||
|
)
|
||||||
|
|
||||||
func request_WatchtowerClient_Policy_0(ctx context.Context, marshaler runtime.Marshaler, client WatchtowerClientClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
func request_WatchtowerClient_Policy_0(ctx context.Context, marshaler runtime.Marshaler, client WatchtowerClientClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
var protoReq PolicyRequest
|
var protoReq PolicyRequest
|
||||||
var metadata runtime.ServerMetadata
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
if err := req.ParseForm(); err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WatchtowerClient_Policy_0); err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
msg, err := client.Policy(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
msg, err := client.Policy(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||||
return msg, metadata, err
|
return msg, metadata, err
|
||||||
|
|
||||||
@ -267,6 +278,10 @@ func local_request_WatchtowerClient_Policy_0(ctx context.Context, marshaler runt
|
|||||||
var protoReq PolicyRequest
|
var protoReq PolicyRequest
|
||||||
var metadata runtime.ServerMetadata
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_WatchtowerClient_Policy_0); err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
msg, err := server.Policy(ctx, &protoReq)
|
msg, err := server.Policy(ctx, &protoReq)
|
||||||
return msg, metadata, err
|
return msg, metadata, err
|
||||||
|
|
||||||
|
@ -149,7 +149,19 @@ message StatsResponse {
|
|||||||
uint32 num_sessions_exhausted = 5;
|
uint32 num_sessions_exhausted = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PolicyType {
|
||||||
|
// Selects the policy from the legacy tower client.
|
||||||
|
LEGACY = 0;
|
||||||
|
|
||||||
|
// Selects the policy from the anchor tower client.
|
||||||
|
ANCHOR = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message PolicyRequest {
|
message PolicyRequest {
|
||||||
|
/*
|
||||||
|
The client type from which to retrieve the active offering policy.
|
||||||
|
*/
|
||||||
|
PolicyType policy_type = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PolicyResponse {
|
message PolicyResponse {
|
||||||
|
@ -134,6 +134,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "policy_type",
|
||||||
|
"description": "The client type from which to retrieve the active offering policy.\n\n - LEGACY: Selects the policy from the legacy tower client.\n - ANCHOR: Selects the policy from the anchor tower client.",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"LEGACY",
|
||||||
|
"ANCHOR"
|
||||||
|
],
|
||||||
|
"default": "LEGACY"
|
||||||
|
}
|
||||||
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"WatchtowerClient"
|
"WatchtowerClient"
|
||||||
]
|
]
|
||||||
@ -281,6 +295,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"wtclientrpcPolicyType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"LEGACY",
|
||||||
|
"ANCHOR"
|
||||||
|
],
|
||||||
|
"default": "LEGACY",
|
||||||
|
"description": " - LEGACY: Selects the policy from the legacy tower client.\n - ANCHOR: Selects the policy from the anchor tower client."
|
||||||
|
},
|
||||||
"wtclientrpcRemoveTowerResponse": {
|
"wtclientrpcRemoveTowerResponse": {
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
@ -9070,6 +9070,19 @@ func testRevokedCloseRetributionRemoteHodl(net *lntest.NetworkHarness,
|
|||||||
func testRevokedCloseRetributionAltruistWatchtower(net *lntest.NetworkHarness,
|
func testRevokedCloseRetributionAltruistWatchtower(net *lntest.NetworkHarness,
|
||||||
t *harnessTest) {
|
t *harnessTest) {
|
||||||
|
|
||||||
|
t.t.Run("anchors", func(tt *testing.T) {
|
||||||
|
ht := newHarnessTest(tt, net)
|
||||||
|
testRevokedCloseRetributionAltruistWatchtowerCase(net, ht, true)
|
||||||
|
})
|
||||||
|
t.t.Run("legacy", func(tt *testing.T) {
|
||||||
|
ht := newHarnessTest(tt, net)
|
||||||
|
testRevokedCloseRetributionAltruistWatchtowerCase(net, ht, false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRevokedCloseRetributionAltruistWatchtowerCase(
|
||||||
|
net *lntest.NetworkHarness, t *harnessTest, anchors bool) {
|
||||||
|
|
||||||
ctxb := context.Background()
|
ctxb := context.Background()
|
||||||
const (
|
const (
|
||||||
chanAmt = lnd.MaxBtcFundingAmount
|
chanAmt = lnd.MaxBtcFundingAmount
|
||||||
@ -9080,7 +9093,11 @@ func testRevokedCloseRetributionAltruistWatchtower(net *lntest.NetworkHarness,
|
|||||||
|
|
||||||
// Since we'd like to test some multi-hop failure scenarios, we'll
|
// Since we'd like to test some multi-hop failure scenarios, we'll
|
||||||
// introduce another node into our test network: Carol.
|
// introduce another node into our test network: Carol.
|
||||||
carol, err := net.NewNode("Carol", []string{"--hodl.exit-settle"})
|
carolArgs := []string{"--hodl.exit-settle"}
|
||||||
|
if anchors {
|
||||||
|
carolArgs = append(carolArgs, "--protocol.anchors")
|
||||||
|
}
|
||||||
|
carol, err := net.NewNode("Carol", carolArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create new nodes: %v", err)
|
t.Fatalf("unable to create new nodes: %v", err)
|
||||||
}
|
}
|
||||||
@ -9133,10 +9150,14 @@ func testRevokedCloseRetributionAltruistWatchtower(net *lntest.NetworkHarness,
|
|||||||
// Dave will be the breached party. We set --nolisten to ensure Carol
|
// Dave will be the breached party. We set --nolisten to ensure Carol
|
||||||
// won't be able to connect to him and trigger the channel data
|
// won't be able to connect to him and trigger the channel data
|
||||||
// protection logic automatically.
|
// protection logic automatically.
|
||||||
dave, err := net.NewNode("Dave", []string{
|
daveArgs := []string{
|
||||||
"--nolisten",
|
"--nolisten",
|
||||||
"--wtclient.active",
|
"--wtclient.active",
|
||||||
})
|
}
|
||||||
|
if anchors {
|
||||||
|
daveArgs = append(daveArgs, "--protocol.anchors")
|
||||||
|
}
|
||||||
|
dave, err := net.NewNode("Dave", daveArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create new node: %v", err)
|
t.Fatalf("unable to create new node: %v", err)
|
||||||
}
|
}
|
||||||
@ -9249,7 +9270,9 @@ func testRevokedCloseRetributionAltruistWatchtower(net *lntest.NetworkHarness,
|
|||||||
err = wait.NoError(func() error {
|
err = wait.NoError(func() error {
|
||||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
bkpStats, err := dave.WatchtowerClient.Stats(ctxt, &wtclientrpc.StatsRequest{})
|
bkpStats, err := dave.WatchtowerClient.Stats(ctxt,
|
||||||
|
&wtclientrpc.StatsRequest{},
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
||||||
|
@ -248,9 +248,13 @@ type Config struct {
|
|||||||
// HtlcNotifier is used when creating a ChannelLink.
|
// HtlcNotifier is used when creating a ChannelLink.
|
||||||
HtlcNotifier *htlcswitch.HtlcNotifier
|
HtlcNotifier *htlcswitch.HtlcNotifier
|
||||||
|
|
||||||
// TowerClient is used when creating a ChannelLink.
|
// TowerClient is used by legacy channels to backup revoked states.
|
||||||
TowerClient wtclient.Client
|
TowerClient wtclient.Client
|
||||||
|
|
||||||
|
// AnchorTowerClient is used by anchor channels to backup revoked
|
||||||
|
// states.
|
||||||
|
AnchorTowerClient wtclient.Client
|
||||||
|
|
||||||
// DisconnectPeer is used to disconnect this peer if the cooperative close
|
// DisconnectPeer is used to disconnect this peer if the cooperative close
|
||||||
// process fails.
|
// process fails.
|
||||||
DisconnectPeer func(*btcec.PublicKey) error
|
DisconnectPeer func(*btcec.PublicKey) error
|
||||||
@ -757,6 +761,18 @@ func (p *Brontide) addLink(chanPoint *wire.OutPoint,
|
|||||||
return p.cfg.ChainArb.UpdateContractSignals(*chanPoint, signals)
|
return p.cfg.ChainArb.UpdateContractSignals(*chanPoint, signals)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chanType := lnChan.State().ChanType
|
||||||
|
|
||||||
|
// Select the appropriate tower client based on the channel type. It's
|
||||||
|
// okay if the clients are disabled altogether and these values are nil,
|
||||||
|
// as the link will check for nilness before using either.
|
||||||
|
var towerClient htlcswitch.TowerClient
|
||||||
|
if chanType.HasAnchors() {
|
||||||
|
towerClient = p.cfg.AnchorTowerClient
|
||||||
|
} else {
|
||||||
|
towerClient = p.cfg.TowerClient
|
||||||
|
}
|
||||||
|
|
||||||
linkCfg := htlcswitch.ChannelLinkConfig{
|
linkCfg := htlcswitch.ChannelLinkConfig{
|
||||||
Peer: p,
|
Peer: p,
|
||||||
DecodeHopIterators: p.cfg.Sphinx.DecodeHopIterators,
|
DecodeHopIterators: p.cfg.Sphinx.DecodeHopIterators,
|
||||||
@ -782,7 +798,7 @@ func (p *Brontide) addLink(chanPoint *wire.OutPoint,
|
|||||||
MinFeeUpdateTimeout: htlcswitch.DefaultMinLinkFeeUpdateTimeout,
|
MinFeeUpdateTimeout: htlcswitch.DefaultMinLinkFeeUpdateTimeout,
|
||||||
MaxFeeUpdateTimeout: htlcswitch.DefaultMaxLinkFeeUpdateTimeout,
|
MaxFeeUpdateTimeout: htlcswitch.DefaultMaxLinkFeeUpdateTimeout,
|
||||||
OutgoingCltvRejectDelta: p.cfg.OutgoingCltvRejectDelta,
|
OutgoingCltvRejectDelta: p.cfg.OutgoingCltvRejectDelta,
|
||||||
TowerClient: p.cfg.TowerClient,
|
TowerClient: towerClient,
|
||||||
MaxOutgoingCltvExpiry: p.cfg.MaxOutgoingCltvExpiry,
|
MaxOutgoingCltvExpiry: p.cfg.MaxOutgoingCltvExpiry,
|
||||||
MaxFeeAllocation: p.cfg.MaxChannelFeeAllocation,
|
MaxFeeAllocation: p.cfg.MaxChannelFeeAllocation,
|
||||||
NotifyActiveLink: p.cfg.ChannelNotifier.NotifyActiveLinkEvent,
|
NotifyActiveLink: p.cfg.ChannelNotifier.NotifyActiveLinkEvent,
|
||||||
|
@ -619,8 +619,8 @@ func newRPCServer(cfg *Config, s *server, macService *macaroons.Service,
|
|||||||
cfg, s.cc, cfg.networkDir, macService, atpl, invoiceRegistry,
|
cfg, s.cc, cfg.networkDir, macService, atpl, invoiceRegistry,
|
||||||
s.htlcSwitch, cfg.ActiveNetParams.Params, s.chanRouter,
|
s.htlcSwitch, cfg.ActiveNetParams.Params, s.chanRouter,
|
||||||
routerBackend, s.nodeSigner, s.remoteChanDB, s.sweeper, tower,
|
routerBackend, s.nodeSigner, s.remoteChanDB, s.sweeper, tower,
|
||||||
s.towerClient, cfg.net.ResolveTCPAddr, genInvoiceFeatures,
|
s.towerClient, s.anchorTowerClient, cfg.net.ResolveTCPAddr,
|
||||||
rpcsLog,
|
genInvoiceFeatures, rpcsLog,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
44
server.go
44
server.go
@ -66,6 +66,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/ticker"
|
"github.com/lightningnetwork/lnd/ticker"
|
||||||
"github.com/lightningnetwork/lnd/tor"
|
"github.com/lightningnetwork/lnd/tor"
|
||||||
"github.com/lightningnetwork/lnd/walletunlocker"
|
"github.com/lightningnetwork/lnd/walletunlocker"
|
||||||
|
"github.com/lightningnetwork/lnd/watchtower/blob"
|
||||||
"github.com/lightningnetwork/lnd/watchtower/wtclient"
|
"github.com/lightningnetwork/lnd/watchtower/wtclient"
|
||||||
"github.com/lightningnetwork/lnd/watchtower/wtpolicy"
|
"github.com/lightningnetwork/lnd/watchtower/wtpolicy"
|
||||||
"github.com/lightningnetwork/lnd/watchtower/wtserver"
|
"github.com/lightningnetwork/lnd/watchtower/wtserver"
|
||||||
@ -247,6 +248,8 @@ type server struct {
|
|||||||
|
|
||||||
towerClient wtclient.Client
|
towerClient wtclient.Client
|
||||||
|
|
||||||
|
anchorTowerClient wtclient.Client
|
||||||
|
|
||||||
connMgr *connmgr.ConnManager
|
connMgr *connmgr.ConnManager
|
||||||
|
|
||||||
sigPool *lnwallet.SigPool
|
sigPool *lnwallet.SigPool
|
||||||
@ -1265,6 +1268,29 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy the policy for legacy channels and set the blob flag
|
||||||
|
// signalling support for anchor channels.
|
||||||
|
anchorPolicy := policy
|
||||||
|
anchorPolicy.TxPolicy.BlobType |=
|
||||||
|
blob.Type(blob.FlagAnchorChannel)
|
||||||
|
|
||||||
|
s.anchorTowerClient, err = wtclient.New(&wtclient.Config{
|
||||||
|
Signer: cc.Wallet.Cfg.Signer,
|
||||||
|
NewAddress: newSweepPkScriptGen(cc.Wallet),
|
||||||
|
SecretKeyRing: s.cc.KeyRing,
|
||||||
|
Dial: cfg.net.Dial,
|
||||||
|
AuthDial: authDial,
|
||||||
|
DB: towerClientDB,
|
||||||
|
Policy: anchorPolicy,
|
||||||
|
ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
|
||||||
|
MinBackoff: 10 * time.Second,
|
||||||
|
MaxBackoff: 5 * time.Minute,
|
||||||
|
ForceQuitDelay: wtclient.DefaultForceQuitDelay,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cfg.ExternalHosts) != 0 {
|
if len(cfg.ExternalHosts) != 0 {
|
||||||
@ -1438,6 +1464,12 @@ func (s *server) Start() error {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if s.anchorTowerClient != nil {
|
||||||
|
if err := s.anchorTowerClient.Start(); err != nil {
|
||||||
|
startErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := s.htlcSwitch.Start(); err != nil {
|
if err := s.htlcSwitch.Start(); err != nil {
|
||||||
startErr = err
|
startErr = err
|
||||||
return
|
return
|
||||||
@ -1675,7 +1707,16 @@ func (s *server) Stop() error {
|
|||||||
// tower. If this is halted for any reason, the force quit timer
|
// tower. If this is halted for any reason, the force quit timer
|
||||||
// will kick in and abort to allow this method to return.
|
// will kick in and abort to allow this method to return.
|
||||||
if s.towerClient != nil {
|
if s.towerClient != nil {
|
||||||
s.towerClient.Stop()
|
if err := s.towerClient.Stop(); err != nil {
|
||||||
|
srvrLog.Warnf("Unable to shut down tower "+
|
||||||
|
"client: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.anchorTowerClient != nil {
|
||||||
|
if err := s.anchorTowerClient.Stop(); err != nil {
|
||||||
|
srvrLog.Warnf("Unable to shut down anchor "+
|
||||||
|
"tower client: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.hostAnn != nil {
|
if s.hostAnn != nil {
|
||||||
@ -3036,6 +3077,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq,
|
|||||||
ChannelNotifier: s.channelNotifier,
|
ChannelNotifier: s.channelNotifier,
|
||||||
HtlcNotifier: s.htlcNotifier,
|
HtlcNotifier: s.htlcNotifier,
|
||||||
TowerClient: s.towerClient,
|
TowerClient: s.towerClient,
|
||||||
|
AnchorTowerClient: s.anchorTowerClient,
|
||||||
DisconnectPeer: s.DisconnectPeer,
|
DisconnectPeer: s.DisconnectPeer,
|
||||||
GenNodeAnnouncement: s.genNodeAnnouncement,
|
GenNodeAnnouncement: s.genNodeAnnouncement,
|
||||||
|
|
||||||
|
@ -96,6 +96,7 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config,
|
|||||||
sweeper *sweep.UtxoSweeper,
|
sweeper *sweep.UtxoSweeper,
|
||||||
tower *watchtower.Standalone,
|
tower *watchtower.Standalone,
|
||||||
towerClient wtclient.Client,
|
towerClient wtclient.Client,
|
||||||
|
anchorTowerClient wtclient.Client,
|
||||||
tcpResolver lncfg.TCPResolver,
|
tcpResolver lncfg.TCPResolver,
|
||||||
genInvoiceFeatures func() *lnwire.FeatureVector,
|
genInvoiceFeatures func() *lnwire.FeatureVector,
|
||||||
rpcLogger btclog.Logger) error {
|
rpcLogger btclog.Logger) error {
|
||||||
@ -243,13 +244,16 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config,
|
|||||||
case *wtclientrpc.Config:
|
case *wtclientrpc.Config:
|
||||||
subCfgValue := extractReflectValue(subCfg)
|
subCfgValue := extractReflectValue(subCfg)
|
||||||
|
|
||||||
if towerClient != nil {
|
if towerClient != nil && anchorTowerClient != nil {
|
||||||
subCfgValue.FieldByName("Active").Set(
|
subCfgValue.FieldByName("Active").Set(
|
||||||
reflect.ValueOf(towerClient != nil),
|
reflect.ValueOf(towerClient != nil),
|
||||||
)
|
)
|
||||||
subCfgValue.FieldByName("Client").Set(
|
subCfgValue.FieldByName("Client").Set(
|
||||||
reflect.ValueOf(towerClient),
|
reflect.ValueOf(towerClient),
|
||||||
)
|
)
|
||||||
|
subCfgValue.FieldByName("AnchorClient").Set(
|
||||||
|
reflect.ValueOf(anchorTowerClient),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
subCfgValue.FieldByName("Resolver").Set(
|
subCfgValue.FieldByName("Resolver").Set(
|
||||||
reflect.ValueOf(tcpResolver),
|
reflect.ValueOf(tcpResolver),
|
||||||
|
@ -138,8 +138,9 @@ func (t Type) String() string {
|
|||||||
// supportedTypes is the set of all configurations known to be supported by the
|
// supportedTypes is the set of all configurations known to be supported by the
|
||||||
// package.
|
// package.
|
||||||
var supportedTypes = map[Type]struct{}{
|
var supportedTypes = map[Type]struct{}{
|
||||||
TypeAltruistCommit: {},
|
TypeAltruistCommit: {},
|
||||||
TypeRewardCommit: {},
|
TypeRewardCommit: {},
|
||||||
|
TypeAltruistAnchorCommit: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSupportedType returns true if the given type is supported by the package.
|
// IsSupportedType returns true if the given type is supported by the package.
|
||||||
|
@ -123,6 +123,12 @@ func TestSupportedTypes(t *testing.T) {
|
|||||||
t.Fatalf("default type %s is not supported", blob.TypeAltruistCommit)
|
t.Fatalf("default type %s is not supported", blob.TypeAltruistCommit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assert that the altruist anchor commit types are supported.
|
||||||
|
if !blob.IsSupportedType(blob.TypeAltruistAnchorCommit) {
|
||||||
|
t.Fatalf("default type %s is not supported",
|
||||||
|
blob.TypeAltruistAnchorCommit)
|
||||||
|
}
|
||||||
|
|
||||||
// Assert that all claimed supported types are actually supported.
|
// Assert that all claimed supported types are actually supported.
|
||||||
for _, supType := range blob.SupportedTypes() {
|
for _, supType := range blob.SupportedTypes() {
|
||||||
if blob.IsSupportedType(supType) {
|
if blob.IsSupportedType(supType) {
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package wtclient
|
package wtclient
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
"github.com/btcsuite/btcd/blockchain"
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcutil/txsort"
|
"github.com/btcsuite/btcutil/txsort"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@ -36,6 +39,7 @@ import (
|
|||||||
type backupTask struct {
|
type backupTask struct {
|
||||||
id wtdb.BackupID
|
id wtdb.BackupID
|
||||||
breachInfo *lnwallet.BreachRetribution
|
breachInfo *lnwallet.BreachRetribution
|
||||||
|
chanType channeldb.ChannelType
|
||||||
|
|
||||||
// state-dependent variables
|
// state-dependent variables
|
||||||
|
|
||||||
@ -54,7 +58,7 @@ type backupTask struct {
|
|||||||
// variables.
|
// variables.
|
||||||
func newBackupTask(chanID *lnwire.ChannelID,
|
func newBackupTask(chanID *lnwire.ChannelID,
|
||||||
breachInfo *lnwallet.BreachRetribution,
|
breachInfo *lnwallet.BreachRetribution,
|
||||||
sweepPkScript []byte, isTweakless bool) *backupTask {
|
sweepPkScript []byte, chanType channeldb.ChannelType) *backupTask {
|
||||||
|
|
||||||
// Parse the non-dust outputs from the breach transaction,
|
// Parse the non-dust outputs from the breach transaction,
|
||||||
// simultaneously computing the total amount contained in the inputs
|
// simultaneously computing the total amount contained in the inputs
|
||||||
@ -85,17 +89,35 @@ func newBackupTask(chanID *lnwire.ChannelID,
|
|||||||
totalAmt += breachInfo.RemoteOutputSignDesc.Output.Value
|
totalAmt += breachInfo.RemoteOutputSignDesc.Output.Value
|
||||||
}
|
}
|
||||||
if breachInfo.LocalOutputSignDesc != nil {
|
if breachInfo.LocalOutputSignDesc != nil {
|
||||||
witnessType := input.CommitmentNoDelay
|
var witnessType input.WitnessType
|
||||||
if isTweakless {
|
switch {
|
||||||
|
case chanType.HasAnchors():
|
||||||
|
witnessType = input.CommitmentToRemoteConfirmed
|
||||||
|
case chanType.IsTweakless():
|
||||||
witnessType = input.CommitSpendNoDelayTweakless
|
witnessType = input.CommitSpendNoDelayTweakless
|
||||||
|
default:
|
||||||
|
witnessType = input.CommitmentNoDelay
|
||||||
}
|
}
|
||||||
|
|
||||||
toRemoteInput = input.NewBaseInput(
|
// Anchor channels have a CSV-encumbered to-remote output. We'll
|
||||||
&breachInfo.LocalOutpoint,
|
// construct a CSV input in that case and assign the proper CSV
|
||||||
witnessType,
|
// delay of 1, otherwise we fallback to the a regular P2WKH
|
||||||
breachInfo.LocalOutputSignDesc,
|
// to-remote output for tweaked or tweakless channels.
|
||||||
0,
|
if chanType.HasAnchors() {
|
||||||
)
|
toRemoteInput = input.NewCsvInput(
|
||||||
|
&breachInfo.LocalOutpoint,
|
||||||
|
witnessType,
|
||||||
|
breachInfo.LocalOutputSignDesc,
|
||||||
|
0, 1,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
toRemoteInput = input.NewBaseInput(
|
||||||
|
&breachInfo.LocalOutpoint,
|
||||||
|
witnessType,
|
||||||
|
breachInfo.LocalOutputSignDesc,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
totalAmt += breachInfo.LocalOutputSignDesc.Output.Value
|
totalAmt += breachInfo.LocalOutputSignDesc.Output.Value
|
||||||
}
|
}
|
||||||
@ -106,6 +128,7 @@ func newBackupTask(chanID *lnwire.ChannelID,
|
|||||||
CommitHeight: breachInfo.RevokedStateNum,
|
CommitHeight: breachInfo.RevokedStateNum,
|
||||||
},
|
},
|
||||||
breachInfo: breachInfo,
|
breachInfo: breachInfo,
|
||||||
|
chanType: chanType,
|
||||||
toLocalInput: toLocalInput,
|
toLocalInput: toLocalInput,
|
||||||
toRemoteInput: toRemoteInput,
|
toRemoteInput: toRemoteInput,
|
||||||
totalAmt: btcutil.Amount(totalAmt),
|
totalAmt: btcutil.Amount(totalAmt),
|
||||||
@ -145,13 +168,28 @@ func (t *backupTask) bindSession(session *wtdb.ClientSessionBody) error {
|
|||||||
// underestimate the size by one byte. The diferrence in weight
|
// underestimate the size by one byte. The diferrence in weight
|
||||||
// can cause different output values on the sweep transaction,
|
// can cause different output values on the sweep transaction,
|
||||||
// so we mimic the original bug and create signatures using the
|
// so we mimic the original bug and create signatures using the
|
||||||
// original weight estimate.
|
// original weight estimate. For anchor channels we'll go ahead
|
||||||
weightEstimate.AddWitnessInput(
|
// an use the correct penalty witness when signing our justice
|
||||||
input.ToLocalPenaltyWitnessSize - 1,
|
// transactions.
|
||||||
)
|
if t.chanType.HasAnchors() {
|
||||||
|
weightEstimate.AddWitnessInput(
|
||||||
|
input.ToLocalPenaltyWitnessSize,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
weightEstimate.AddWitnessInput(
|
||||||
|
input.ToLocalPenaltyWitnessSize - 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if t.toRemoteInput != nil {
|
if t.toRemoteInput != nil {
|
||||||
weightEstimate.AddWitnessInput(input.P2WKHWitnessSize)
|
// Legacy channels (both tweaked and non-tweaked) spend from
|
||||||
|
// P2WKH output. Anchor channels spend a to-remote confirmed
|
||||||
|
// P2WSH output.
|
||||||
|
if t.chanType.HasAnchors() {
|
||||||
|
weightEstimate.AddWitnessInput(input.ToRemoteConfirmedWitnessSize)
|
||||||
|
} else {
|
||||||
|
weightEstimate.AddWitnessInput(input.P2WKHWitnessSize)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All justice transactions have a p2wkh output paying to the victim.
|
// All justice transactions have a p2wkh output paying to the victim.
|
||||||
@ -163,6 +201,12 @@ func (t *backupTask) bindSession(session *wtdb.ClientSessionBody) error {
|
|||||||
weightEstimate.AddP2WKHOutput()
|
weightEstimate.AddP2WKHOutput()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.chanType.HasAnchors() != session.Policy.IsAnchorChannel() {
|
||||||
|
log.Criticalf("Invalid task (has_anchors=%t) for session "+
|
||||||
|
"(has_anchors=%t)", t.chanType.HasAnchors(),
|
||||||
|
session.Policy.IsAnchorChannel())
|
||||||
|
}
|
||||||
|
|
||||||
// Now, compute the output values depending on whether FlagReward is set
|
// Now, compute the output values depending on whether FlagReward is set
|
||||||
// in the current session's policy.
|
// in the current session's policy.
|
||||||
outputs, err := session.Policy.ComputeJusticeTxOuts(
|
outputs, err := session.Policy.ComputeJusticeTxOuts(
|
||||||
@ -219,9 +263,10 @@ func (t *backupTask) craftSessionPayload(
|
|||||||
// information. This will either be contain both the to-local and
|
// information. This will either be contain both the to-local and
|
||||||
// to-remote outputs, or only be the to-local output.
|
// to-remote outputs, or only be the to-local output.
|
||||||
inputs := t.inputs()
|
inputs := t.inputs()
|
||||||
for prevOutPoint := range inputs {
|
for prevOutPoint, input := range inputs {
|
||||||
justiceTxn.AddTxIn(&wire.TxIn{
|
justiceTxn.AddTxIn(&wire.TxIn{
|
||||||
PreviousOutPoint: prevOutPoint,
|
PreviousOutPoint: prevOutPoint,
|
||||||
|
Sequence: input.BlocksToMaturity(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +333,12 @@ func (t *backupTask) craftSessionPayload(
|
|||||||
case input.CommitSpendNoDelayTweakless:
|
case input.CommitSpendNoDelayTweakless:
|
||||||
fallthrough
|
fallthrough
|
||||||
case input.CommitmentNoDelay:
|
case input.CommitmentNoDelay:
|
||||||
|
fallthrough
|
||||||
|
case input.CommitmentToRemoteConfirmed:
|
||||||
copy(justiceKit.CommitToRemoteSig[:], signature[:])
|
copy(justiceKit.CommitToRemoteSig[:], signature[:])
|
||||||
|
default:
|
||||||
|
return hint, nil, fmt.Errorf("invalid witness type: %v",
|
||||||
|
inp.WitnessType())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
@ -74,7 +75,7 @@ type backupTaskTest struct {
|
|||||||
bindErr error
|
bindErr error
|
||||||
expSweepScript []byte
|
expSweepScript []byte
|
||||||
signer input.Signer
|
signer input.Signer
|
||||||
tweakless bool
|
chanType channeldb.ChannelType
|
||||||
}
|
}
|
||||||
|
|
||||||
// genTaskTest creates a instance of a backupTaskTest using the passed
|
// genTaskTest creates a instance of a backupTaskTest using the passed
|
||||||
@ -92,7 +93,13 @@ func genTaskTest(
|
|||||||
expSweepAmt int64,
|
expSweepAmt int64,
|
||||||
expRewardAmt int64,
|
expRewardAmt int64,
|
||||||
bindErr error,
|
bindErr error,
|
||||||
tweakless bool) backupTaskTest {
|
chanType channeldb.ChannelType) backupTaskTest {
|
||||||
|
|
||||||
|
// Set the anchor flag in the blob type if the session needs to support
|
||||||
|
// anchor channels.
|
||||||
|
if chanType.HasAnchors() {
|
||||||
|
blobType |= blob.Type(blob.FlagAnchorChannel)
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the key pairs for all keys used in the test.
|
// Parse the key pairs for all keys used in the test.
|
||||||
revSK, revPK := btcec.PrivKeyFromBytes(
|
revSK, revPK := btcec.PrivKeyFromBytes(
|
||||||
@ -192,17 +199,31 @@ func genTaskTest(
|
|||||||
Index: index,
|
Index: index,
|
||||||
}
|
}
|
||||||
|
|
||||||
witnessType := input.CommitmentNoDelay
|
var witnessType input.WitnessType
|
||||||
if tweakless {
|
switch {
|
||||||
|
case chanType.HasAnchors():
|
||||||
|
witnessType = input.CommitmentToRemoteConfirmed
|
||||||
|
case chanType.IsTweakless():
|
||||||
witnessType = input.CommitSpendNoDelayTweakless
|
witnessType = input.CommitSpendNoDelayTweakless
|
||||||
|
default:
|
||||||
|
witnessType = input.CommitmentNoDelay
|
||||||
}
|
}
|
||||||
|
|
||||||
toRemoteInput = input.NewBaseInput(
|
if chanType.HasAnchors() {
|
||||||
&breachInfo.LocalOutpoint,
|
toRemoteInput = input.NewCsvInput(
|
||||||
witnessType,
|
&breachInfo.LocalOutpoint,
|
||||||
breachInfo.LocalOutputSignDesc,
|
witnessType,
|
||||||
0,
|
breachInfo.LocalOutputSignDesc,
|
||||||
)
|
0, 1,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
toRemoteInput = input.NewBaseInput(
|
||||||
|
&breachInfo.LocalOutpoint,
|
||||||
|
witnessType,
|
||||||
|
breachInfo.LocalOutputSignDesc,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return backupTaskTest{
|
return backupTaskTest{
|
||||||
@ -227,7 +248,7 @@ func genTaskTest(
|
|||||||
bindErr: bindErr,
|
bindErr: bindErr,
|
||||||
expSweepScript: makeAddrSlice(22),
|
expSweepScript: makeAddrSlice(22),
|
||||||
signer: signer,
|
signer: signer,
|
||||||
tweakless: tweakless,
|
chanType: chanType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,60 +274,97 @@ var (
|
|||||||
func TestBackupTask(t *testing.T) {
|
func TestBackupTask(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
chanTypes := []channeldb.ChannelType{
|
||||||
|
channeldb.SingleFunderBit,
|
||||||
|
channeldb.SingleFunderTweaklessBit,
|
||||||
|
channeldb.AnchorOutputsBit,
|
||||||
|
}
|
||||||
|
|
||||||
var backupTaskTests []backupTaskTest
|
var backupTaskTests []backupTaskTest
|
||||||
for _, tweakless := range []bool{true, false} {
|
for _, chanType := range chanTypes {
|
||||||
|
// Depending on whether the test is for anchor channels or
|
||||||
|
// legacy (tweaked and non-tweaked) channels, adjust the
|
||||||
|
// expected sweep amount to accommodate. These are different for
|
||||||
|
// several reasons:
|
||||||
|
// - anchor to-remote outputs require a P2WSH sweep rather
|
||||||
|
// than a P2WKH sweep.
|
||||||
|
// - the to-local weight estimate fixes an off-by-one.
|
||||||
|
// In tests related to the dust threshold, the size difference
|
||||||
|
// between the channel types makes it so that the threshold fee
|
||||||
|
// rate is slightly lower (since the transactions are heavier).
|
||||||
|
var (
|
||||||
|
expSweepCommitNoRewardBoth int64 = 299241
|
||||||
|
expSweepCommitNoRewardLocal int64 = 199514
|
||||||
|
expSweepCommitNoRewardRemote int64 = 99561
|
||||||
|
expSweepCommitRewardBoth int64 = 296117
|
||||||
|
expSweepCommitRewardLocal int64 = 197390
|
||||||
|
expSweepCommitRewardRemote int64 = 98437
|
||||||
|
sweepFeeRateNoRewardRemoteDust chainfee.SatPerKWeight = 227500
|
||||||
|
sweepFeeRateRewardRemoteDust chainfee.SatPerKWeight = 175000
|
||||||
|
)
|
||||||
|
if chanType.HasAnchors() {
|
||||||
|
expSweepCommitNoRewardBoth = 299236
|
||||||
|
expSweepCommitNoRewardLocal = 199513
|
||||||
|
expSweepCommitNoRewardRemote = 99557
|
||||||
|
expSweepCommitRewardBoth = 296112
|
||||||
|
expSweepCommitRewardLocal = 197389
|
||||||
|
expSweepCommitRewardRemote = 98433
|
||||||
|
sweepFeeRateNoRewardRemoteDust = 225000
|
||||||
|
sweepFeeRateRewardRemoteDust = 173750
|
||||||
|
}
|
||||||
|
|
||||||
backupTaskTests = append(backupTaskTests, []backupTaskTest{
|
backupTaskTests = append(backupTaskTests, []backupTaskTest{
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit no-reward, both outputs",
|
"commit no-reward, both outputs",
|
||||||
100, // stateNum
|
100, // stateNum
|
||||||
200000, // toLocalAmt
|
200000, // toLocalAmt
|
||||||
100000, // toRemoteAmt
|
100000, // toRemoteAmt
|
||||||
blobTypeCommitNoReward, // blobType
|
blobTypeCommitNoReward, // blobType
|
||||||
1000, // sweepFeeRate
|
1000, // sweepFeeRate
|
||||||
nil, // rewardScript
|
nil, // rewardScript
|
||||||
299241, // expSweepAmt
|
expSweepCommitNoRewardBoth, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
nil, // bindErr
|
nil, // bindErr
|
||||||
tweakless,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit no-reward, to-local output only",
|
"commit no-reward, to-local output only",
|
||||||
1000, // stateNum
|
1000, // stateNum
|
||||||
200000, // toLocalAmt
|
200000, // toLocalAmt
|
||||||
0, // toRemoteAmt
|
0, // toRemoteAmt
|
||||||
blobTypeCommitNoReward, // blobType
|
blobTypeCommitNoReward, // blobType
|
||||||
1000, // sweepFeeRate
|
1000, // sweepFeeRate
|
||||||
nil, // rewardScript
|
nil, // rewardScript
|
||||||
199514, // expSweepAmt
|
expSweepCommitNoRewardLocal, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
nil, // bindErr
|
nil, // bindErr
|
||||||
tweakless,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit no-reward, to-remote output only",
|
"commit no-reward, to-remote output only",
|
||||||
1, // stateNum
|
1, // stateNum
|
||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
100000, // toRemoteAmt
|
100000, // toRemoteAmt
|
||||||
blobTypeCommitNoReward, // blobType
|
blobTypeCommitNoReward, // blobType
|
||||||
1000, // sweepFeeRate
|
1000, // sweepFeeRate
|
||||||
nil, // rewardScript
|
nil, // rewardScript
|
||||||
99561, // expSweepAmt
|
expSweepCommitNoRewardRemote, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
nil, // bindErr
|
nil, // bindErr
|
||||||
tweakless,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit no-reward, to-remote output only, creates dust",
|
"commit no-reward, to-remote output only, creates dust",
|
||||||
1, // stateNum
|
1, // stateNum
|
||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
100000, // toRemoteAmt
|
100000, // toRemoteAmt
|
||||||
blobTypeCommitNoReward, // blobType
|
blobTypeCommitNoReward, // blobType
|
||||||
227500, // sweepFeeRate
|
sweepFeeRateNoRewardRemoteDust, // sweepFeeRate
|
||||||
nil, // rewardScript
|
nil, // rewardScript
|
||||||
0, // expSweepAmt
|
0, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
wtpolicy.ErrCreatesDust, // bindErr
|
wtpolicy.ErrCreatesDust, // bindErr
|
||||||
tweakless,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit no-reward, no outputs, fee rate exceeds inputs",
|
"commit no-reward, no outputs, fee rate exceeds inputs",
|
||||||
@ -319,7 +377,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
0, // expSweepAmt
|
0, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
wtpolicy.ErrFeeExceedsInputs, // bindErr
|
wtpolicy.ErrFeeExceedsInputs, // bindErr
|
||||||
tweakless,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit no-reward, no outputs, fee rate of 0 creates dust",
|
"commit no-reward, no outputs, fee rate of 0 creates dust",
|
||||||
@ -332,59 +390,59 @@ func TestBackupTask(t *testing.T) {
|
|||||||
0, // expSweepAmt
|
0, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
wtpolicy.ErrCreatesDust, // bindErr
|
wtpolicy.ErrCreatesDust, // bindErr
|
||||||
tweakless,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit reward, both outputs",
|
"commit reward, both outputs",
|
||||||
100, // stateNum
|
100, // stateNum
|
||||||
200000, // toLocalAmt
|
200000, // toLocalAmt
|
||||||
100000, // toRemoteAmt
|
100000, // toRemoteAmt
|
||||||
blobTypeCommitReward, // blobType
|
blobTypeCommitReward, // blobType
|
||||||
1000, // sweepFeeRate
|
1000, // sweepFeeRate
|
||||||
addrScript, // rewardScript
|
addrScript, // rewardScript
|
||||||
296117, // expSweepAmt
|
expSweepCommitRewardBoth, // expSweepAmt
|
||||||
3000, // expRewardAmt
|
3000, // expRewardAmt
|
||||||
nil, // bindErr
|
nil, // bindErr
|
||||||
tweakless,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit reward, to-local output only",
|
"commit reward, to-local output only",
|
||||||
1000, // stateNum
|
1000, // stateNum
|
||||||
200000, // toLocalAmt
|
200000, // toLocalAmt
|
||||||
0, // toRemoteAmt
|
0, // toRemoteAmt
|
||||||
blobTypeCommitReward, // blobType
|
blobTypeCommitReward, // blobType
|
||||||
1000, // sweepFeeRate
|
1000, // sweepFeeRate
|
||||||
addrScript, // rewardScript
|
addrScript, // rewardScript
|
||||||
197390, // expSweepAmt
|
expSweepCommitRewardLocal, // expSweepAmt
|
||||||
2000, // expRewardAmt
|
2000, // expRewardAmt
|
||||||
nil, // bindErr
|
nil, // bindErr
|
||||||
tweakless,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit reward, to-remote output only",
|
"commit reward, to-remote output only",
|
||||||
1, // stateNum
|
1, // stateNum
|
||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
100000, // toRemoteAmt
|
100000, // toRemoteAmt
|
||||||
blobTypeCommitReward, // blobType
|
blobTypeCommitReward, // blobType
|
||||||
1000, // sweepFeeRate
|
1000, // sweepFeeRate
|
||||||
addrScript, // rewardScript
|
addrScript, // rewardScript
|
||||||
98437, // expSweepAmt
|
expSweepCommitRewardRemote, // expSweepAmt
|
||||||
1000, // expRewardAmt
|
1000, // expRewardAmt
|
||||||
nil, // bindErr
|
nil, // bindErr
|
||||||
tweakless,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit reward, to-remote output only, creates dust",
|
"commit reward, to-remote output only, creates dust",
|
||||||
1, // stateNum
|
1, // stateNum
|
||||||
0, // toLocalAmt
|
0, // toLocalAmt
|
||||||
100000, // toRemoteAmt
|
100000, // toRemoteAmt
|
||||||
blobTypeCommitReward, // blobType
|
blobTypeCommitReward, // blobType
|
||||||
175000, // sweepFeeRate
|
sweepFeeRateRewardRemoteDust, // sweepFeeRate
|
||||||
addrScript, // rewardScript
|
addrScript, // rewardScript
|
||||||
0, // expSweepAmt
|
0, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
wtpolicy.ErrCreatesDust, // bindErr
|
wtpolicy.ErrCreatesDust, // bindErr
|
||||||
tweakless,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit reward, no outputs, fee rate exceeds inputs",
|
"commit reward, no outputs, fee rate exceeds inputs",
|
||||||
@ -397,7 +455,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
0, // expSweepAmt
|
0, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
wtpolicy.ErrFeeExceedsInputs, // bindErr
|
wtpolicy.ErrFeeExceedsInputs, // bindErr
|
||||||
tweakless,
|
chanType,
|
||||||
),
|
),
|
||||||
genTaskTest(
|
genTaskTest(
|
||||||
"commit reward, no outputs, fee rate of 0 creates dust",
|
"commit reward, no outputs, fee rate of 0 creates dust",
|
||||||
@ -410,7 +468,7 @@ func TestBackupTask(t *testing.T) {
|
|||||||
0, // expSweepAmt
|
0, // expSweepAmt
|
||||||
0, // expRewardAmt
|
0, // expRewardAmt
|
||||||
wtpolicy.ErrCreatesDust, // bindErr
|
wtpolicy.ErrCreatesDust, // bindErr
|
||||||
tweakless,
|
chanType,
|
||||||
),
|
),
|
||||||
}...)
|
}...)
|
||||||
}
|
}
|
||||||
@ -430,7 +488,7 @@ func testBackupTask(t *testing.T, test backupTaskTest) {
|
|||||||
// Create a new backupTask from the channel id and breach info.
|
// Create a new backupTask from the channel id and breach info.
|
||||||
task := newBackupTask(
|
task := newBackupTask(
|
||||||
&test.chanID, test.breachInfo, test.expSweepScript,
|
&test.chanID, test.breachInfo, test.expSweepScript,
|
||||||
test.tweakless,
|
test.chanType,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert that all parameters set during initialization are properly
|
// Assert that all parameters set during initialization are properly
|
||||||
|
@ -10,6 +10,9 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btclog"
|
||||||
|
"github.com/lightningnetwork/lnd/build"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
@ -40,13 +43,14 @@ const (
|
|||||||
DefaultForceQuitDelay = 10 * time.Second
|
DefaultForceQuitDelay = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// genActiveSessionFilter generates a filter that selects active sessions that
|
||||||
// activeSessionFilter is a filter that ignored any sessions which are
|
// also match the desired channel type, either legacy or anchor.
|
||||||
// not active.
|
func genActiveSessionFilter(anchor bool) func(*wtdb.ClientSession) bool {
|
||||||
activeSessionFilter = func(s *wtdb.ClientSession) bool {
|
return func(s *wtdb.ClientSession) bool {
|
||||||
return s.Status == wtdb.CSessionActive
|
return s.Status == wtdb.CSessionActive &&
|
||||||
|
anchor == s.Policy.IsAnchorChannel()
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
// RegisteredTower encompasses information about a registered watchtower with
|
// RegisteredTower encompasses information about a registered watchtower with
|
||||||
// the client.
|
// the client.
|
||||||
@ -103,7 +107,8 @@ type Client interface {
|
|||||||
// negotiated policy. If the channel we're trying to back up doesn't
|
// negotiated policy. If the channel we're trying to back up doesn't
|
||||||
// have a tweak for the remote party's output, then isTweakless should
|
// have a tweak for the remote party's output, then isTweakless should
|
||||||
// be true.
|
// be true.
|
||||||
BackupState(*lnwire.ChannelID, *lnwallet.BreachRetribution, bool) error
|
BackupState(*lnwire.ChannelID, *lnwallet.BreachRetribution,
|
||||||
|
channeldb.ChannelType) error
|
||||||
|
|
||||||
// Start initializes the watchtower client, allowing it process requests
|
// Start initializes the watchtower client, allowing it process requests
|
||||||
// to backup revoked channel states.
|
// to backup revoked channel states.
|
||||||
@ -228,6 +233,8 @@ type TowerClient struct {
|
|||||||
|
|
||||||
cfg *Config
|
cfg *Config
|
||||||
|
|
||||||
|
log btclog.Logger
|
||||||
|
|
||||||
pipeline *taskPipeline
|
pipeline *taskPipeline
|
||||||
|
|
||||||
negotiator SessionNegotiator
|
negotiator SessionNegotiator
|
||||||
@ -274,10 +281,18 @@ func New(config *Config) (*TowerClient, error) {
|
|||||||
cfg.WriteTimeout = DefaultWriteTimeout
|
cfg.WriteTimeout = DefaultWriteTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prefix := "(legacy)"
|
||||||
|
if cfg.Policy.IsAnchorChannel() {
|
||||||
|
prefix = "(anchor)"
|
||||||
|
}
|
||||||
|
plog := build.NewPrefixLog(prefix, log)
|
||||||
|
|
||||||
// Next, load all candidate sessions and towers from the database into
|
// Next, load all candidate sessions and towers from the database into
|
||||||
// the client. We will use any of these session if their policies match
|
// the client. We will use any of these session if their policies match
|
||||||
// the current policy of the client, otherwise they will be ignored and
|
// the current policy of the client, otherwise they will be ignored and
|
||||||
// new sessions will be requested.
|
// new sessions will be requested.
|
||||||
|
isAnchorClient := cfg.Policy.IsAnchorChannel()
|
||||||
|
activeSessionFilter := genActiveSessionFilter(isAnchorClient)
|
||||||
candidateSessions, err := getClientSessions(
|
candidateSessions, err := getClientSessions(
|
||||||
cfg.DB, cfg.SecretKeyRing, nil, activeSessionFilter,
|
cfg.DB, cfg.SecretKeyRing, nil, activeSessionFilter,
|
||||||
)
|
)
|
||||||
@ -287,7 +302,7 @@ func New(config *Config) (*TowerClient, error) {
|
|||||||
|
|
||||||
var candidateTowers []*wtdb.Tower
|
var candidateTowers []*wtdb.Tower
|
||||||
for _, s := range candidateSessions {
|
for _, s := range candidateSessions {
|
||||||
log.Infof("Using private watchtower %s, offering policy %s",
|
plog.Infof("Using private watchtower %s, offering policy %s",
|
||||||
s.Tower, cfg.Policy)
|
s.Tower, cfg.Policy)
|
||||||
candidateTowers = append(candidateTowers, s.Tower)
|
candidateTowers = append(candidateTowers, s.Tower)
|
||||||
}
|
}
|
||||||
@ -301,6 +316,7 @@ func New(config *Config) (*TowerClient, error) {
|
|||||||
|
|
||||||
c := &TowerClient{
|
c := &TowerClient{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
log: plog,
|
||||||
pipeline: newTaskPipeline(),
|
pipeline: newTaskPipeline(),
|
||||||
candidateTowers: newTowerListIterator(candidateTowers...),
|
candidateTowers: newTowerListIterator(candidateTowers...),
|
||||||
candidateSessions: candidateSessions,
|
candidateSessions: candidateSessions,
|
||||||
@ -422,7 +438,7 @@ func (c *TowerClient) buildHighestCommitHeights() {
|
|||||||
func (c *TowerClient) Start() error {
|
func (c *TowerClient) Start() error {
|
||||||
var err error
|
var err error
|
||||||
c.started.Do(func() {
|
c.started.Do(func() {
|
||||||
log.Infof("Starting watchtower client")
|
c.log.Infof("Starting watchtower client")
|
||||||
|
|
||||||
// First, restart a session queue for any sessions that have
|
// First, restart a session queue for any sessions that have
|
||||||
// committed but unacked state updates. This ensures that these
|
// committed but unacked state updates. This ensures that these
|
||||||
@ -430,7 +446,7 @@ func (c *TowerClient) Start() error {
|
|||||||
// restart.
|
// restart.
|
||||||
for _, session := range c.candidateSessions {
|
for _, session := range c.candidateSessions {
|
||||||
if len(session.CommittedUpdates) > 0 {
|
if len(session.CommittedUpdates) > 0 {
|
||||||
log.Infof("Starting session=%s to process "+
|
c.log.Infof("Starting session=%s to process "+
|
||||||
"%d committed backups", session.ID,
|
"%d committed backups", session.ID,
|
||||||
len(session.CommittedUpdates))
|
len(session.CommittedUpdates))
|
||||||
c.initActiveQueue(session)
|
c.initActiveQueue(session)
|
||||||
@ -460,7 +476,7 @@ func (c *TowerClient) Start() error {
|
|||||||
// Stop idempotently initiates a graceful shutdown of the watchtower client.
|
// Stop idempotently initiates a graceful shutdown of the watchtower client.
|
||||||
func (c *TowerClient) Stop() error {
|
func (c *TowerClient) Stop() error {
|
||||||
c.stopped.Do(func() {
|
c.stopped.Do(func() {
|
||||||
log.Debugf("Stopping watchtower client")
|
c.log.Debugf("Stopping watchtower client")
|
||||||
|
|
||||||
// 1. To ensure we don't hang forever on shutdown due to
|
// 1. To ensure we don't hang forever on shutdown due to
|
||||||
// unintended failures, we'll delay a call to force quit the
|
// unintended failures, we'll delay a call to force quit the
|
||||||
@ -493,7 +509,7 @@ func (c *TowerClient) Stop() error {
|
|||||||
// queues, we no longer need to negotiate sessions.
|
// queues, we no longer need to negotiate sessions.
|
||||||
c.negotiator.Stop()
|
c.negotiator.Stop()
|
||||||
|
|
||||||
log.Debugf("Waiting for active session queues to finish "+
|
c.log.Debugf("Waiting for active session queues to finish "+
|
||||||
"draining, stats: %s", c.stats)
|
"draining, stats: %s", c.stats)
|
||||||
|
|
||||||
// 5. Shutdown all active session queues in parallel. These will
|
// 5. Shutdown all active session queues in parallel. These will
|
||||||
@ -509,7 +525,7 @@ func (c *TowerClient) Stop() error {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Client successfully stopped, stats: %s", c.stats)
|
c.log.Debugf("Client successfully stopped, stats: %s", c.stats)
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -518,7 +534,7 @@ func (c *TowerClient) Stop() error {
|
|||||||
// client. This should only be executed if Stop is unable to exit cleanly.
|
// client. This should only be executed if Stop is unable to exit cleanly.
|
||||||
func (c *TowerClient) ForceQuit() {
|
func (c *TowerClient) ForceQuit() {
|
||||||
c.forced.Do(func() {
|
c.forced.Do(func() {
|
||||||
log.Infof("Force quitting watchtower client")
|
c.log.Infof("Force quitting watchtower client")
|
||||||
|
|
||||||
// 1. Shutdown the backup queue, which will prevent any further
|
// 1. Shutdown the backup queue, which will prevent any further
|
||||||
// updates from being accepted. In practice, the links should be
|
// updates from being accepted. In practice, the links should be
|
||||||
@ -543,7 +559,7 @@ func (c *TowerClient) ForceQuit() {
|
|||||||
return s.ForceQuit
|
return s.ForceQuit
|
||||||
})
|
})
|
||||||
|
|
||||||
log.Infof("Watchtower client unclean shutdown complete, "+
|
c.log.Infof("Watchtower client unclean shutdown complete, "+
|
||||||
"stats: %s", c.stats)
|
"stats: %s", c.stats)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -592,7 +608,8 @@ func (c *TowerClient) RegisterChannel(chanID lnwire.ChannelID) error {
|
|||||||
// - breached outputs contain too little value to sweep at the target sweep fee
|
// - breached outputs contain too little value to sweep at the target sweep fee
|
||||||
// rate.
|
// rate.
|
||||||
func (c *TowerClient) BackupState(chanID *lnwire.ChannelID,
|
func (c *TowerClient) BackupState(chanID *lnwire.ChannelID,
|
||||||
breachInfo *lnwallet.BreachRetribution, isTweakless bool) error {
|
breachInfo *lnwallet.BreachRetribution,
|
||||||
|
chanType channeldb.ChannelType) error {
|
||||||
|
|
||||||
// Retrieve the cached sweep pkscript used for this channel.
|
// Retrieve the cached sweep pkscript used for this channel.
|
||||||
c.backupMu.Lock()
|
c.backupMu.Lock()
|
||||||
@ -606,7 +623,7 @@ func (c *TowerClient) BackupState(chanID *lnwire.ChannelID,
|
|||||||
height, ok := c.chanCommitHeights[*chanID]
|
height, ok := c.chanCommitHeights[*chanID]
|
||||||
if ok && breachInfo.RevokedStateNum <= height {
|
if ok && breachInfo.RevokedStateNum <= height {
|
||||||
c.backupMu.Unlock()
|
c.backupMu.Unlock()
|
||||||
log.Debugf("Ignoring duplicate backup for chanid=%v at height=%d",
|
c.log.Debugf("Ignoring duplicate backup for chanid=%v at height=%d",
|
||||||
chanID, breachInfo.RevokedStateNum)
|
chanID, breachInfo.RevokedStateNum)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -618,7 +635,7 @@ func (c *TowerClient) BackupState(chanID *lnwire.ChannelID,
|
|||||||
c.backupMu.Unlock()
|
c.backupMu.Unlock()
|
||||||
|
|
||||||
task := newBackupTask(
|
task := newBackupTask(
|
||||||
chanID, breachInfo, summary.SweepPkScript, isTweakless,
|
chanID, breachInfo, summary.SweepPkScript, chanType,
|
||||||
)
|
)
|
||||||
|
|
||||||
return c.pipeline.QueueBackupTask(task)
|
return c.pipeline.QueueBackupTask(task)
|
||||||
@ -669,15 +686,15 @@ func (c *TowerClient) nextSessionQueue() *sessionQueue {
|
|||||||
func (c *TowerClient) backupDispatcher() {
|
func (c *TowerClient) backupDispatcher() {
|
||||||
defer c.wg.Done()
|
defer c.wg.Done()
|
||||||
|
|
||||||
log.Tracef("Starting backup dispatcher")
|
c.log.Tracef("Starting backup dispatcher")
|
||||||
defer log.Tracef("Stopping backup dispatcher")
|
defer c.log.Tracef("Stopping backup dispatcher")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
switch {
|
switch {
|
||||||
|
|
||||||
// No active session queue and no additional sessions.
|
// No active session queue and no additional sessions.
|
||||||
case c.sessionQueue == nil && len(c.candidateSessions) == 0:
|
case c.sessionQueue == nil && len(c.candidateSessions) == 0:
|
||||||
log.Infof("Requesting new session.")
|
c.log.Infof("Requesting new session.")
|
||||||
|
|
||||||
// Immediately request a new session.
|
// Immediately request a new session.
|
||||||
c.negotiator.RequestSession()
|
c.negotiator.RequestSession()
|
||||||
@ -688,7 +705,7 @@ func (c *TowerClient) backupDispatcher() {
|
|||||||
awaitSession:
|
awaitSession:
|
||||||
select {
|
select {
|
||||||
case session := <-c.negotiator.NewSessions():
|
case session := <-c.negotiator.NewSessions():
|
||||||
log.Infof("Acquired new session with id=%s",
|
c.log.Infof("Acquired new session with id=%s",
|
||||||
session.ID)
|
session.ID)
|
||||||
c.candidateSessions[session.ID] = session
|
c.candidateSessions[session.ID] = session
|
||||||
c.stats.sessionAcquired()
|
c.stats.sessionAcquired()
|
||||||
@ -698,7 +715,7 @@ func (c *TowerClient) backupDispatcher() {
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
case <-c.statTicker.C:
|
case <-c.statTicker.C:
|
||||||
log.Infof("Client stats: %s", c.stats)
|
c.log.Infof("Client stats: %s", c.stats)
|
||||||
|
|
||||||
// A new tower has been requested to be added. We'll
|
// A new tower has been requested to be added. We'll
|
||||||
// update our persisted and in-memory state and consider
|
// update our persisted and in-memory state and consider
|
||||||
@ -732,7 +749,7 @@ func (c *TowerClient) backupDispatcher() {
|
|||||||
// backup tasks.
|
// backup tasks.
|
||||||
c.sessionQueue = c.nextSessionQueue()
|
c.sessionQueue = c.nextSessionQueue()
|
||||||
if c.sessionQueue != nil {
|
if c.sessionQueue != nil {
|
||||||
log.Debugf("Loaded next candidate session "+
|
c.log.Debugf("Loaded next candidate session "+
|
||||||
"queue id=%s", c.sessionQueue.ID())
|
"queue id=%s", c.sessionQueue.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -759,13 +776,13 @@ func (c *TowerClient) backupDispatcher() {
|
|||||||
// we can request new sessions before the session is
|
// we can request new sessions before the session is
|
||||||
// fully empty, which this case would handle.
|
// fully empty, which this case would handle.
|
||||||
case session := <-c.negotiator.NewSessions():
|
case session := <-c.negotiator.NewSessions():
|
||||||
log.Warnf("Acquired new session with id=%s "+
|
c.log.Warnf("Acquired new session with id=%s "+
|
||||||
"while processing tasks", session.ID)
|
"while processing tasks", session.ID)
|
||||||
c.candidateSessions[session.ID] = session
|
c.candidateSessions[session.ID] = session
|
||||||
c.stats.sessionAcquired()
|
c.stats.sessionAcquired()
|
||||||
|
|
||||||
case <-c.statTicker.C:
|
case <-c.statTicker.C:
|
||||||
log.Infof("Client stats: %s", c.stats)
|
c.log.Infof("Client stats: %s", c.stats)
|
||||||
|
|
||||||
// Process each backup task serially from the queue of
|
// Process each backup task serially from the queue of
|
||||||
// revoked states.
|
// revoked states.
|
||||||
@ -776,7 +793,7 @@ func (c *TowerClient) backupDispatcher() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Processing %v", task.id)
|
c.log.Debugf("Processing %v", task.id)
|
||||||
|
|
||||||
c.stats.taskReceived()
|
c.stats.taskReceived()
|
||||||
c.processTask(task)
|
c.processTask(task)
|
||||||
@ -821,7 +838,7 @@ func (c *TowerClient) processTask(task *backupTask) {
|
|||||||
// sessionQueue will be removed if accepting the task left the sessionQueue in
|
// sessionQueue will be removed if accepting the task left the sessionQueue in
|
||||||
// an exhausted state.
|
// an exhausted state.
|
||||||
func (c *TowerClient) taskAccepted(task *backupTask, newStatus reserveStatus) {
|
func (c *TowerClient) taskAccepted(task *backupTask, newStatus reserveStatus) {
|
||||||
log.Infof("Queued %v successfully for session %v",
|
c.log.Infof("Queued %v successfully for session %v",
|
||||||
task.id, c.sessionQueue.ID())
|
task.id, c.sessionQueue.ID())
|
||||||
|
|
||||||
c.stats.taskAccepted()
|
c.stats.taskAccepted()
|
||||||
@ -840,7 +857,7 @@ func (c *TowerClient) taskAccepted(task *backupTask, newStatus reserveStatus) {
|
|||||||
case reserveExhausted:
|
case reserveExhausted:
|
||||||
c.stats.sessionExhausted()
|
c.stats.sessionExhausted()
|
||||||
|
|
||||||
log.Debugf("Session %s exhausted", c.sessionQueue.ID())
|
c.log.Debugf("Session %s exhausted", c.sessionQueue.ID())
|
||||||
|
|
||||||
// This task left the session exhausted, set it to nil and
|
// This task left the session exhausted, set it to nil and
|
||||||
// proceed to the next loop so we can consume another
|
// proceed to the next loop so we can consume another
|
||||||
@ -863,13 +880,13 @@ func (c *TowerClient) taskRejected(task *backupTask, curStatus reserveStatus) {
|
|||||||
case reserveAvailable:
|
case reserveAvailable:
|
||||||
c.stats.taskIneligible()
|
c.stats.taskIneligible()
|
||||||
|
|
||||||
log.Infof("Ignoring ineligible %v", task.id)
|
c.log.Infof("Ignoring ineligible %v", task.id)
|
||||||
|
|
||||||
err := c.cfg.DB.MarkBackupIneligible(
|
err := c.cfg.DB.MarkBackupIneligible(
|
||||||
task.id.ChanID, task.id.CommitHeight,
|
task.id.ChanID, task.id.CommitHeight,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to mark %v ineligible: %v",
|
c.log.Errorf("Unable to mark %v ineligible: %v",
|
||||||
task.id, err)
|
task.id, err)
|
||||||
|
|
||||||
// It is safe to not handle this error, even if we could
|
// It is safe to not handle this error, even if we could
|
||||||
@ -889,7 +906,7 @@ func (c *TowerClient) taskRejected(task *backupTask, curStatus reserveStatus) {
|
|||||||
case reserveExhausted:
|
case reserveExhausted:
|
||||||
c.stats.sessionExhausted()
|
c.stats.sessionExhausted()
|
||||||
|
|
||||||
log.Debugf("Session %v exhausted, %v queued for next session",
|
c.log.Debugf("Session %v exhausted, %v queued for next session",
|
||||||
c.sessionQueue.ID(), task.id)
|
c.sessionQueue.ID(), task.id)
|
||||||
|
|
||||||
// Cache the task that we pulled off, so that we can process it
|
// Cache the task that we pulled off, so that we can process it
|
||||||
@ -918,7 +935,7 @@ func (c *TowerClient) readMessage(peer wtserver.Peer) (wtwire.Message, error) {
|
|||||||
err := peer.SetReadDeadline(time.Now().Add(c.cfg.ReadTimeout))
|
err := peer.SetReadDeadline(time.Now().Add(c.cfg.ReadTimeout))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to set read deadline: %v", err)
|
err = fmt.Errorf("unable to set read deadline: %v", err)
|
||||||
log.Errorf("Unable to read msg: %v", err)
|
c.log.Errorf("Unable to read msg: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -926,7 +943,7 @@ func (c *TowerClient) readMessage(peer wtserver.Peer) (wtwire.Message, error) {
|
|||||||
rawMsg, err := peer.ReadNextMessage()
|
rawMsg, err := peer.ReadNextMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to read message: %v", err)
|
err = fmt.Errorf("unable to read message: %v", err)
|
||||||
log.Errorf("Unable to read msg: %v", err)
|
c.log.Errorf("Unable to read msg: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -936,11 +953,11 @@ func (c *TowerClient) readMessage(peer wtserver.Peer) (wtwire.Message, error) {
|
|||||||
msg, err := wtwire.ReadMessage(msgReader, 0)
|
msg, err := wtwire.ReadMessage(msgReader, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to parse message: %v", err)
|
err = fmt.Errorf("unable to parse message: %v", err)
|
||||||
log.Errorf("Unable to read msg: %v", err)
|
c.log.Errorf("Unable to read msg: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logMessage(peer, msg, true)
|
c.logMessage(peer, msg, true)
|
||||||
|
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
@ -953,7 +970,7 @@ func (c *TowerClient) sendMessage(peer wtserver.Peer, msg wtwire.Message) error
|
|||||||
_, err := wtwire.WriteMessage(&b, msg, 0)
|
_, err := wtwire.WriteMessage(&b, msg, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Unable to encode msg: %v", err)
|
err = fmt.Errorf("Unable to encode msg: %v", err)
|
||||||
log.Errorf("Unable to send msg: %v", err)
|
c.log.Errorf("Unable to send msg: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -962,16 +979,16 @@ func (c *TowerClient) sendMessage(peer wtserver.Peer, msg wtwire.Message) error
|
|||||||
err = peer.SetWriteDeadline(time.Now().Add(c.cfg.WriteTimeout))
|
err = peer.SetWriteDeadline(time.Now().Add(c.cfg.WriteTimeout))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to set write deadline: %v", err)
|
err = fmt.Errorf("unable to set write deadline: %v", err)
|
||||||
log.Errorf("Unable to send msg: %v", err)
|
c.log.Errorf("Unable to send msg: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logMessage(peer, msg, false)
|
c.logMessage(peer, msg, false)
|
||||||
|
|
||||||
// Write out the full message to the remote peer.
|
// Write out the full message to the remote peer.
|
||||||
_, err = peer.Write(b.Bytes())
|
_, err = peer.Write(b.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to send msg: %v", err)
|
c.log.Errorf("Unable to send msg: %v", err)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1065,6 +1082,8 @@ func (c *TowerClient) handleNewTower(msg *newTowerMsg) error {
|
|||||||
c.candidateTowers.AddCandidate(tower)
|
c.candidateTowers.AddCandidate(tower)
|
||||||
|
|
||||||
// Include all of its corresponding sessions to our set of candidates.
|
// Include all of its corresponding sessions to our set of candidates.
|
||||||
|
isAnchorClient := c.cfg.Policy.IsAnchorChannel()
|
||||||
|
activeSessionFilter := genActiveSessionFilter(isAnchorClient)
|
||||||
sessions, err := getClientSessions(
|
sessions, err := getClientSessions(
|
||||||
c.cfg.DB, c.cfg.SecretKeyRing, &tower.ID, activeSessionFilter,
|
c.cfg.DB, c.cfg.SecretKeyRing, &tower.ID, activeSessionFilter,
|
||||||
)
|
)
|
||||||
@ -1232,7 +1251,9 @@ func (c *TowerClient) Policy() wtpolicy.Policy {
|
|||||||
// logMessage writes information about a message received from a remote peer,
|
// logMessage writes information about a message received from a remote peer,
|
||||||
// using directional prepositions to signal whether the message was sent or
|
// using directional prepositions to signal whether the message was sent or
|
||||||
// received.
|
// received.
|
||||||
func logMessage(peer wtserver.Peer, msg wtwire.Message, read bool) {
|
func (c *TowerClient) logMessage(
|
||||||
|
peer wtserver.Peer, msg wtwire.Message, read bool) {
|
||||||
|
|
||||||
var action = "Received"
|
var action = "Received"
|
||||||
var preposition = "from"
|
var preposition = "from"
|
||||||
if !read {
|
if !read {
|
||||||
@ -1245,7 +1266,7 @@ func logMessage(peer wtserver.Peer, msg wtwire.Message, read bool) {
|
|||||||
summary = "(" + summary + ")"
|
summary = "(" + summary + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("%s %s%v %s %x@%s", action, msg.MsgType(), summary,
|
c.log.Debugf("%s %s%v %s %x@%s", action, msg.MsgType(), summary,
|
||||||
preposition, peer.RemotePub().SerializeCompressed(),
|
preposition, peer.RemotePub().SerializeCompressed(),
|
||||||
peer.RemoteAddr())
|
peer.RemoteAddr())
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
@ -633,7 +634,7 @@ func (h *testHarness) backupState(id, i uint64, expErr error) {
|
|||||||
_, retribution := h.channel(id).getState(i)
|
_, retribution := h.channel(id).getState(i)
|
||||||
|
|
||||||
chanID := chanIDFromInt(id)
|
chanID := chanIDFromInt(id)
|
||||||
err := h.client.BackupState(&chanID, retribution, false)
|
err := h.client.BackupState(&chanID, retribution, channeldb.SingleFunderBit)
|
||||||
if err != expErr {
|
if err != expErr {
|
||||||
h.t.Fatalf("back error mismatch, want: %v, got: %v",
|
h.t.Fatalf("back error mismatch, want: %v, got: %v",
|
||||||
expErr, err)
|
expErr, err)
|
||||||
|
@ -112,8 +112,19 @@ var _ SessionNegotiator = (*sessionNegotiator)(nil)
|
|||||||
|
|
||||||
// newSessionNegotiator initializes a fresh sessionNegotiator instance.
|
// newSessionNegotiator initializes a fresh sessionNegotiator instance.
|
||||||
func newSessionNegotiator(cfg *NegotiatorConfig) *sessionNegotiator {
|
func newSessionNegotiator(cfg *NegotiatorConfig) *sessionNegotiator {
|
||||||
|
// Generate the set of features the negitator will present to the tower
|
||||||
|
// upon connection. For anchor channels, we'll conditionally signal that
|
||||||
|
// we require support for anchor channels depdening on the requested
|
||||||
|
// policy.
|
||||||
|
features := []lnwire.FeatureBit{
|
||||||
|
wtwire.AltruistSessionsRequired,
|
||||||
|
}
|
||||||
|
if cfg.Policy.IsAnchorChannel() {
|
||||||
|
features = append(features, wtwire.AnchorCommitRequired)
|
||||||
|
}
|
||||||
|
|
||||||
localInit := wtwire.NewInitMessage(
|
localInit := wtwire.NewInitMessage(
|
||||||
lnwire.NewRawFeatureVector(wtwire.AltruistSessionsRequired),
|
lnwire.NewRawFeatureVector(features...),
|
||||||
cfg.ChainHash,
|
cfg.ChainHash,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -120,6 +120,11 @@ func (p Policy) String() string {
|
|||||||
p.SweepFeeRate)
|
p.SweepFeeRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAnchorChannel returns true if the session policy requires anchor channels.
|
||||||
|
func (p Policy) IsAnchorChannel() bool {
|
||||||
|
return p.TxPolicy.BlobType.IsAnchorChannel()
|
||||||
|
}
|
||||||
|
|
||||||
// Validate ensures that the policy satisfies some minimal correctness
|
// Validate ensures that the policy satisfies some minimal correctness
|
||||||
// constraints.
|
// constraints.
|
||||||
func (p Policy) Validate() error {
|
func (p Policy) Validate() error {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/lightningnetwork/lnd/watchtower/blob"
|
"github.com/lightningnetwork/lnd/watchtower/blob"
|
||||||
"github.com/lightningnetwork/lnd/watchtower/wtpolicy"
|
"github.com/lightningnetwork/lnd/watchtower/wtpolicy"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var validationTests = []struct {
|
var validationTests = []struct {
|
||||||
@ -91,3 +92,21 @@ func TestPolicyValidate(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPolicyIsAnchorChannel asserts that the IsAnchorChannel helper properly
|
||||||
|
// reflects the anchor bit of the policy's blob type.
|
||||||
|
func TestPolicyIsAnchorChannel(t *testing.T) {
|
||||||
|
policyNoAnchor := wtpolicy.Policy{
|
||||||
|
TxPolicy: wtpolicy.TxPolicy{
|
||||||
|
BlobType: blob.TypeAltruistCommit,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.Equal(t, false, policyNoAnchor.IsAnchorChannel())
|
||||||
|
|
||||||
|
policyAnchor := wtpolicy.Policy{
|
||||||
|
TxPolicy: wtpolicy.TxPolicy{
|
||||||
|
BlobType: blob.TypeAltruistAnchorCommit,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.Equal(t, true, policyAnchor.IsAnchorChannel())
|
||||||
|
}
|
||||||
|
@ -96,7 +96,10 @@ type Server struct {
|
|||||||
// sessions and send state updates.
|
// sessions and send state updates.
|
||||||
func New(cfg *Config) (*Server, error) {
|
func New(cfg *Config) (*Server, error) {
|
||||||
localInit := wtwire.NewInitMessage(
|
localInit := wtwire.NewInitMessage(
|
||||||
lnwire.NewRawFeatureVector(wtwire.AltruistSessionsOptional),
|
lnwire.NewRawFeatureVector(
|
||||||
|
wtwire.AltruistSessionsOptional,
|
||||||
|
wtwire.AnchorCommitOptional,
|
||||||
|
),
|
||||||
cfg.ChainHash,
|
cfg.ChainHash,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -161,6 +161,28 @@ type createSessionTestCase struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var createSessionTests = []createSessionTestCase{
|
var createSessionTests = []createSessionTestCase{
|
||||||
|
{
|
||||||
|
name: "duplicate session create altruist anchor commit",
|
||||||
|
initMsg: wtwire.NewInitMessage(
|
||||||
|
lnwire.NewRawFeatureVector(),
|
||||||
|
testnetChainHash,
|
||||||
|
),
|
||||||
|
createMsg: &wtwire.CreateSession{
|
||||||
|
BlobType: blob.TypeAltruistAnchorCommit,
|
||||||
|
MaxUpdates: 1000,
|
||||||
|
RewardBase: 0,
|
||||||
|
RewardRate: 0,
|
||||||
|
SweepFeeRate: 10000,
|
||||||
|
},
|
||||||
|
expReply: &wtwire.CreateSessionReply{
|
||||||
|
Code: wtwire.CodeOK,
|
||||||
|
Data: []byte{},
|
||||||
|
},
|
||||||
|
expDupReply: &wtwire.CreateSessionReply{
|
||||||
|
Code: wtwire.CodeOK,
|
||||||
|
Data: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "duplicate session create",
|
name: "duplicate session create",
|
||||||
initMsg: wtwire.NewInitMessage(
|
initMsg: wtwire.NewInitMessage(
|
||||||
|
@ -7,6 +7,8 @@ import "github.com/lightningnetwork/lnd/lnwire"
|
|||||||
var FeatureNames = map[lnwire.FeatureBit]string{
|
var FeatureNames = map[lnwire.FeatureBit]string{
|
||||||
AltruistSessionsRequired: "altruist-sessions",
|
AltruistSessionsRequired: "altruist-sessions",
|
||||||
AltruistSessionsOptional: "altruist-sessions",
|
AltruistSessionsOptional: "altruist-sessions",
|
||||||
|
AnchorCommitRequired: "anchor-commit",
|
||||||
|
AnchorCommitOptional: "anchor-commit",
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -19,4 +21,13 @@ const (
|
|||||||
// support a remote party who understand the protocol for creating and
|
// support a remote party who understand the protocol for creating and
|
||||||
// updating watchtower sessions.
|
// updating watchtower sessions.
|
||||||
AltruistSessionsOptional lnwire.FeatureBit = 1
|
AltruistSessionsOptional lnwire.FeatureBit = 1
|
||||||
|
|
||||||
|
// AnchorCommitRequired specifies that the advertising tower requires
|
||||||
|
// the remote party to negotiate sessions for protecting anchor
|
||||||
|
// channels.
|
||||||
|
AnchorCommitRequired lnwire.FeatureBit = 2
|
||||||
|
|
||||||
|
// AnchorCommitOptional specifies that the advertising tower allows the
|
||||||
|
// remote party to negotiate sessions for protecting anchor channels.
|
||||||
|
AnchorCommitOptional lnwire.FeatureBit = 3
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user