Merge pull request #8239 from ellemouton/loadSessionByActiveTower
wtclient: add DeactivateTower and TerminateSession commands
This commit is contained in:
24 files changed
+2731
-762
No files matched your search
+86
-1
@@ -10,7 +10,8 @@ import (
|
|||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// wtclientCommands will return nil for non-wtclientrpc builds.
|
// wtclientCommands is a list of commands that can be used to interact with the
|
||||||
|
// watchtower client.
|
||||||
func wtclientCommands() []cli.Command {
|
func wtclientCommands() []cli.Command {
|
||||||
return []cli.Command{
|
return []cli.Command{
|
||||||
{
|
{
|
||||||
@@ -20,10 +21,12 @@ func wtclientCommands() []cli.Command {
|
|||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
addTowerCommand,
|
addTowerCommand,
|
||||||
removeTowerCommand,
|
removeTowerCommand,
|
||||||
|
deactivateTowerCommand,
|
||||||
listTowersCommand,
|
listTowersCommand,
|
||||||
getTowerCommand,
|
getTowerCommand,
|
||||||
statsCommand,
|
statsCommand,
|
||||||
policyCommand,
|
policyCommand,
|
||||||
|
sessionCommands,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -84,6 +87,44 @@ func addTower(ctx *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var deactivateTowerCommand = cli.Command{
|
||||||
|
Name: "deactivate",
|
||||||
|
Usage: "Deactivate a watchtower to temporarily prevent its use for " +
|
||||||
|
"sessions/backups.",
|
||||||
|
ArgsUsage: "pubkey",
|
||||||
|
Action: actionDecorator(deactivateTower),
|
||||||
|
}
|
||||||
|
|
||||||
|
func deactivateTower(ctx *cli.Context) error {
|
||||||
|
ctxc := getContext()
|
||||||
|
|
||||||
|
// Display the command's help message if the number of arguments/flags
|
||||||
|
// is not what we expect.
|
||||||
|
if ctx.NArg() != 1 || ctx.NumFlags() > 0 {
|
||||||
|
return cli.ShowCommandHelp(ctx, "deactivate")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey, err := hex.DecodeString(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid public key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, cleanUp := getWtclient(ctx)
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
req := &wtclientrpc.DeactivateTowerRequest{
|
||||||
|
Pubkey: pubKey,
|
||||||
|
}
|
||||||
|
resp, err := client.DeactivateTower(ctxc, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
printRespJSON(resp)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var removeTowerCommand = cli.Command{
|
var removeTowerCommand = cli.Command{
|
||||||
Name: "remove",
|
Name: "remove",
|
||||||
Usage: "Remove a watchtower to prevent its use for future " +
|
Usage: "Remove a watchtower to prevent its use for future " +
|
||||||
@@ -333,3 +374,47 @@ func policy(ctx *cli.Context) error {
|
|||||||
printRespJSON(resp)
|
printRespJSON(resp)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sessionCommands = cli.Command{
|
||||||
|
Name: "session",
|
||||||
|
Subcommands: []cli.Command{
|
||||||
|
terminateSessionCommand,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var terminateSessionCommand = cli.Command{
|
||||||
|
Name: "terminate",
|
||||||
|
ArgsUsage: "id",
|
||||||
|
Action: actionDecorator(terminateSession),
|
||||||
|
}
|
||||||
|
|
||||||
|
func terminateSession(ctx *cli.Context) error {
|
||||||
|
ctxc := getContext()
|
||||||
|
|
||||||
|
// Display the command's help message if the number of arguments/flags
|
||||||
|
// is not what we expect.
|
||||||
|
if ctx.NArg() > 1 || ctx.NumFlags() != 0 {
|
||||||
|
return cli.ShowCommandHelp(ctx, "terminate")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, cleanUp := getWtclient(ctx)
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
sessionID, err := hex.DecodeString(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid session ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.TerminateSession(
|
||||||
|
ctxc, &wtclientrpc.TerminateSessionRequest{
|
||||||
|
SessionId: sessionID,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
printRespJSON(resp)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -155,6 +155,13 @@
|
|||||||
about the txid and don't want the calling code to block while the channel
|
about the txid and don't want the calling code to block while the channel
|
||||||
drains the active HTLCs.
|
drains the active HTLCs.
|
||||||
|
|
||||||
|
* [New watchtower client DeactivateTower and
|
||||||
|
TerminateSession](https://github.com/lightningnetwork/lnd/pull/8239) commands
|
||||||
|
have been added. The DeactivateTower command can be used to mark a tower as
|
||||||
|
inactive so that its sessions are not loaded on startup and so that the tower
|
||||||
|
is not considered for session negotiation. TerminateSession can be used to
|
||||||
|
mark a specific session as terminal so that that specific is never used again.
|
||||||
|
|
||||||
## lncli Additions
|
## lncli Additions
|
||||||
|
|
||||||
* Deprecate `bumpclosefee` for `bumpforceclosefee` to accommodate for the fact
|
* Deprecate `bumpclosefee` for `bumpforceclosefee` to accommodate for the fact
|
||||||
|
|||||||
+2
-10
@@ -519,16 +519,8 @@ var allTestCases = []*lntest.TestCase{
|
|||||||
TestFunc: testLookupHtlcResolution,
|
TestFunc: testLookupHtlcResolution,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "watchtower session management",
|
Name: "watchtower",
|
||||||
TestFunc: testWatchtowerSessionManagement,
|
TestFunc: testWatchtower,
|
||||||
},
|
|
||||||
{
|
|
||||||
// NOTE: this test must be put in the same tranche as
|
|
||||||
// `testWatchtowerSessionManagement` to avoid parallel use of
|
|
||||||
// the default watchtower port.
|
|
||||||
Name: "revoked uncooperative close retribution altruist " +
|
|
||||||
"watchtower",
|
|
||||||
TestFunc: testRevokedCloseRetributionAltruistWatchtower,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "channel fundmax",
|
Name: "channel fundmax",
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
@@ -12,7 +11,6 @@ import (
|
|||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/funding"
|
"github.com/lightningnetwork/lnd/funding"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/wtclientrpc"
|
|
||||||
"github.com/lightningnetwork/lnd/lntest"
|
"github.com/lightningnetwork/lnd/lntest"
|
||||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -707,304 +705,3 @@ func testRevokedCloseRetributionRemoteHodl(ht *lntest.HarnessTest) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// testRevokedCloseRetributionAltruistWatchtower establishes a channel between
|
|
||||||
// Carol and Dave, where Carol is using a third node Willy as her watchtower.
|
|
||||||
// After sending some payments, Dave reverts his state and force closes to
|
|
||||||
// trigger a breach. Carol is kept offline throughout the process and the test
|
|
||||||
// asserts that Willy responds by broadcasting the justice transaction on
|
|
||||||
// Carol's behalf sweeping her funds without a reward.
|
|
||||||
func testRevokedCloseRetributionAltruistWatchtower(ht *lntest.HarnessTest) {
|
|
||||||
for _, commitType := range []lnrpc.CommitmentType{
|
|
||||||
lnrpc.CommitmentType_LEGACY,
|
|
||||||
lnrpc.CommitmentType_ANCHORS,
|
|
||||||
lnrpc.CommitmentType_SIMPLE_TAPROOT,
|
|
||||||
} {
|
|
||||||
testName := fmt.Sprintf("%v", commitType.String())
|
|
||||||
ct := commitType
|
|
||||||
testFunc := func(ht *lntest.HarnessTest) {
|
|
||||||
testRevokedCloseRetributionAltruistWatchtowerCase(
|
|
||||||
ht, ct,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
success := ht.Run(testName, func(tt *testing.T) {
|
|
||||||
st := ht.Subtest(tt)
|
|
||||||
|
|
||||||
st.RunTestCase(&lntest.TestCase{
|
|
||||||
Name: testName,
|
|
||||||
TestFunc: testFunc,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
if !success {
|
|
||||||
// Log failure time to help relate the lnd logs to the
|
|
||||||
// failure.
|
|
||||||
ht.Logf("Failure time: %v", time.Now().Format(
|
|
||||||
"2006-01-02 15:04:05.000",
|
|
||||||
))
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testRevokedCloseRetributionAltruistWatchtowerCase(ht *lntest.HarnessTest,
|
|
||||||
commitType lnrpc.CommitmentType) {
|
|
||||||
|
|
||||||
const (
|
|
||||||
chanAmt = funding.MaxBtcFundingAmount
|
|
||||||
paymentAmt = 10000
|
|
||||||
numInvoices = 6
|
|
||||||
externalIP = "1.2.3.4"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Since we'd like to test some multi-hop failure scenarios, we'll
|
|
||||||
// introduce another node into our test network: Carol.
|
|
||||||
carolArgs := lntest.NodeArgsForCommitType(commitType)
|
|
||||||
carolArgs = append(carolArgs, "--hodl.exit-settle")
|
|
||||||
|
|
||||||
carol := ht.NewNode("Carol", carolArgs)
|
|
||||||
|
|
||||||
// Willy the watchtower will protect Dave from Carol's breach. He will
|
|
||||||
// remain online in order to punish Carol on Dave's behalf, since the
|
|
||||||
// breach will happen while Dave is offline.
|
|
||||||
willy := ht.NewNode(
|
|
||||||
"Willy", []string{
|
|
||||||
"--watchtower.active",
|
|
||||||
"--watchtower.externalip=" + externalIP,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
willyInfo := willy.RPC.GetInfoWatchtower()
|
|
||||||
|
|
||||||
// Assert that Willy has one listener and it is 0.0.0.0:9911 or
|
|
||||||
// [::]:9911. Since no listener is explicitly specified, one of these
|
|
||||||
// should be the default depending on whether the host supports IPv6 or
|
|
||||||
// not.
|
|
||||||
require.Len(ht, willyInfo.Listeners, 1, "Willy should have 1 listener")
|
|
||||||
listener := willyInfo.Listeners[0]
|
|
||||||
if listener != "0.0.0.0:9911" && listener != "[::]:9911" {
|
|
||||||
ht.Fatalf("expected listener on 0.0.0.0:9911 or [::]:9911, "+
|
|
||||||
"got %v", listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert the Willy's URIs properly display the chosen external IP.
|
|
||||||
require.Len(ht, willyInfo.Uris, 1, "Willy should have 1 uri")
|
|
||||||
require.Contains(ht, willyInfo.Uris[0], externalIP)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// protection logic automatically.
|
|
||||||
daveArgs := lntest.NodeArgsForCommitType(commitType)
|
|
||||||
daveArgs = append(daveArgs, "--nolisten", "--wtclient.active")
|
|
||||||
dave := ht.NewNode("Dave", daveArgs)
|
|
||||||
|
|
||||||
addTowerReq := &wtclientrpc.AddTowerRequest{
|
|
||||||
Pubkey: willyInfo.Pubkey,
|
|
||||||
Address: listener,
|
|
||||||
}
|
|
||||||
dave.RPC.AddTower(addTowerReq)
|
|
||||||
|
|
||||||
// We must let Dave have an open channel before she can send a node
|
|
||||||
// announcement, so we open a channel with Carol,
|
|
||||||
ht.ConnectNodes(dave, carol)
|
|
||||||
|
|
||||||
// Before we make a channel, we'll load up Dave with some coins sent
|
|
||||||
// directly from the miner.
|
|
||||||
ht.FundCoins(btcutil.SatoshiPerBitcoin, dave)
|
|
||||||
|
|
||||||
// Send one more UTXOs if this is a neutrino backend.
|
|
||||||
if ht.IsNeutrinoBackend() {
|
|
||||||
ht.FundCoins(btcutil.SatoshiPerBitcoin, dave)
|
|
||||||
}
|
|
||||||
|
|
||||||
// In order to test Dave's response to an uncooperative channel
|
|
||||||
// closure by Carol, we'll first open up a channel between them with a
|
|
||||||
// 0.5 BTC value.
|
|
||||||
params := lntest.OpenChannelParams{
|
|
||||||
Amt: 3 * (chanAmt / 4),
|
|
||||||
PushAmt: chanAmt / 4,
|
|
||||||
CommitmentType: commitType,
|
|
||||||
Private: true,
|
|
||||||
}
|
|
||||||
chanPoint := ht.OpenChannel(dave, carol, params)
|
|
||||||
|
|
||||||
// With the channel open, we'll create a few invoices for Carol that
|
|
||||||
// Dave will pay to in order to advance the state of the channel.
|
|
||||||
carolPayReqs, _, _ := ht.CreatePayReqs(carol, paymentAmt, numInvoices)
|
|
||||||
|
|
||||||
// Next query for Carol's channel state, as we sent 0 payments, Carol
|
|
||||||
// should still see her balance as the push amount, which is 1/4 of the
|
|
||||||
// capacity.
|
|
||||||
carolChan := ht.AssertChannelLocalBalance(
|
|
||||||
carol, chanPoint, int64(chanAmt/4),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Grab Carol's current commitment height (update number), we'll later
|
|
||||||
// revert her to this state after additional updates to force him to
|
|
||||||
// broadcast this soon to be revoked state.
|
|
||||||
carolStateNumPreCopy := int(carolChan.NumUpdates)
|
|
||||||
|
|
||||||
// With the temporary file created, copy Carol's current state into the
|
|
||||||
// temporary file we created above. Later after more updates, we'll
|
|
||||||
// restore this state.
|
|
||||||
ht.BackupDB(carol)
|
|
||||||
|
|
||||||
// Reconnect the peers after the restart that was needed for the db
|
|
||||||
// backup.
|
|
||||||
ht.EnsureConnected(dave, carol)
|
|
||||||
|
|
||||||
// Once connected, give Dave some time to enable the channel again.
|
|
||||||
ht.AssertTopologyChannelOpen(dave, chanPoint)
|
|
||||||
|
|
||||||
// Finally, send payments from Dave to Carol, consuming Carol's
|
|
||||||
// remaining payment hashes.
|
|
||||||
ht.CompletePaymentRequestsNoWait(dave, carolPayReqs, chanPoint)
|
|
||||||
|
|
||||||
daveBalResp := dave.RPC.WalletBalance()
|
|
||||||
davePreSweepBalance := daveBalResp.ConfirmedBalance
|
|
||||||
|
|
||||||
// Wait until the backup has been accepted by the watchtower before
|
|
||||||
// shutting down Dave.
|
|
||||||
err := wait.NoError(func() error {
|
|
||||||
bkpStats := dave.RPC.WatchtowerStats()
|
|
||||||
if bkpStats == nil {
|
|
||||||
return errors.New("no active backup sessions")
|
|
||||||
}
|
|
||||||
if bkpStats.NumBackups == 0 {
|
|
||||||
return errors.New("no backups accepted")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}, defaultTimeout)
|
|
||||||
require.NoError(ht, err, "unable to verify backup task completed")
|
|
||||||
|
|
||||||
// Shutdown Dave to simulate going offline for an extended period of
|
|
||||||
// time. Once he's not watching, Carol will try to breach the channel.
|
|
||||||
restart := ht.SuspendNode(dave)
|
|
||||||
|
|
||||||
// Now we shutdown Carol, copying over the his temporary database state
|
|
||||||
// which has the *prior* channel state over his current most up to date
|
|
||||||
// state. With this, we essentially force Carol to travel back in time
|
|
||||||
// within the channel's history.
|
|
||||||
ht.RestartNodeAndRestoreDB(carol)
|
|
||||||
|
|
||||||
// Now query for Carol's channel state, it should show that he's at a
|
|
||||||
// state number in the past, not the *latest* state.
|
|
||||||
ht.AssertChannelCommitHeight(carol, chanPoint, carolStateNumPreCopy)
|
|
||||||
|
|
||||||
// Now force Carol to execute a *force* channel closure by unilaterally
|
|
||||||
// broadcasting his current channel state. This is actually the
|
|
||||||
// commitment transaction of a prior *revoked* state, so he'll soon
|
|
||||||
// feel the wrath of Dave's retribution.
|
|
||||||
closeUpdates, closeTxID := ht.CloseChannelAssertPending(
|
|
||||||
carol, chanPoint, true,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Finally, generate a single block, wait for the final close status
|
|
||||||
// update, then ensure that the closing transaction was included in the
|
|
||||||
// block.
|
|
||||||
block := ht.MineBlocksAndAssertNumTxes(1, 1)[0]
|
|
||||||
|
|
||||||
breachTXID := ht.WaitForChannelCloseEvent(closeUpdates)
|
|
||||||
ht.Miner.AssertTxInBlock(block, breachTXID)
|
|
||||||
|
|
||||||
// The breachTXID should match the above closeTxID.
|
|
||||||
require.EqualValues(ht, breachTXID, closeTxID)
|
|
||||||
|
|
||||||
// Query the mempool for Dave's justice transaction, this should be
|
|
||||||
// broadcast as Carol's contract breaching transaction gets confirmed
|
|
||||||
// above.
|
|
||||||
justiceTXID := ht.Miner.AssertNumTxsInMempool(1)[0]
|
|
||||||
|
|
||||||
// Query for the mempool transaction found above. Then assert that all
|
|
||||||
// the inputs of this transaction are spending outputs generated by
|
|
||||||
// Carol's breach transaction above.
|
|
||||||
justiceTx := ht.Miner.GetRawTransaction(justiceTXID)
|
|
||||||
for _, txIn := range justiceTx.MsgTx().TxIn {
|
|
||||||
require.Equal(ht, breachTXID[:], txIn.PreviousOutPoint.Hash[:],
|
|
||||||
"justice tx not spending commitment utxo")
|
|
||||||
}
|
|
||||||
|
|
||||||
willyBalResp := willy.RPC.WalletBalance()
|
|
||||||
require.Zero(ht, willyBalResp.ConfirmedBalance,
|
|
||||||
"willy should have 0 balance before mining justice transaction")
|
|
||||||
|
|
||||||
// Now mine a block, this transaction should include Dave's justice
|
|
||||||
// transaction which was just accepted into the mempool.
|
|
||||||
block = ht.MineBlocksAndAssertNumTxes(1, 1)[0]
|
|
||||||
|
|
||||||
// The block should have exactly *two* transactions, one of which is
|
|
||||||
// the justice transaction.
|
|
||||||
require.Len(ht, block.Transactions, 2, "transaction wasn't mined")
|
|
||||||
justiceSha := block.Transactions[1].TxHash()
|
|
||||||
require.Equal(ht, justiceTx.Hash()[:], justiceSha[:],
|
|
||||||
"justice tx wasn't mined")
|
|
||||||
|
|
||||||
// Ensure that Willy doesn't get any funds, as he is acting as an
|
|
||||||
// altruist watchtower.
|
|
||||||
err = wait.NoError(func() error {
|
|
||||||
willyBalResp := willy.RPC.WalletBalance()
|
|
||||||
|
|
||||||
if willyBalResp.ConfirmedBalance != 0 {
|
|
||||||
return fmt.Errorf("expected Willy to have no funds "+
|
|
||||||
"after justice transaction was mined, found %v",
|
|
||||||
willyBalResp)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}, time.Second*5)
|
|
||||||
require.NoError(ht, err, "timeout checking willy's balance")
|
|
||||||
|
|
||||||
// Before restarting Dave, shutdown Carol so Dave won't sync with her.
|
|
||||||
// Otherwise, during the restart, Dave will realize Carol is falling
|
|
||||||
// behind and return `ErrCommitSyncRemoteDataLoss`, thus force closing
|
|
||||||
// the channel. Although this force close tx will be later replaced by
|
|
||||||
// the breach tx, it will create two anchor sweeping txes for neutrino
|
|
||||||
// backend, causing the confirmed wallet balance to be zero later on
|
|
||||||
// because the utxos are used in sweeping.
|
|
||||||
ht.Shutdown(carol)
|
|
||||||
|
|
||||||
// Restart Dave, who will still think his channel with Carol is open.
|
|
||||||
// We should him to detect the breach, but realize that the funds have
|
|
||||||
// then been swept to his wallet by Willy.
|
|
||||||
require.NoError(ht, restart(), "unable to restart dave")
|
|
||||||
|
|
||||||
err = wait.NoError(func() error {
|
|
||||||
daveBalResp := dave.RPC.ChannelBalance()
|
|
||||||
if daveBalResp.LocalBalance.Sat != 0 {
|
|
||||||
return fmt.Errorf("Dave should end up with zero "+
|
|
||||||
"channel balance, instead has %d",
|
|
||||||
daveBalResp.LocalBalance.Sat)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}, defaultTimeout)
|
|
||||||
require.NoError(ht, err, "timeout checking dave's channel balance")
|
|
||||||
|
|
||||||
ht.AssertNumPendingForceClose(dave, 0)
|
|
||||||
|
|
||||||
// If this is an anchor channel, Dave would sweep the anchor.
|
|
||||||
if lntest.CommitTypeHasAnchors(commitType) {
|
|
||||||
ht.MineBlocksAndAssertNumTxes(1, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that Dave's wallet balance is increased.
|
|
||||||
err = wait.NoError(func() error {
|
|
||||||
daveBalResp := dave.RPC.WalletBalance()
|
|
||||||
|
|
||||||
if daveBalResp.ConfirmedBalance <= davePreSweepBalance {
|
|
||||||
return fmt.Errorf("Dave should have more than %d "+
|
|
||||||
"after sweep, instead has %d",
|
|
||||||
davePreSweepBalance,
|
|
||||||
daveBalResp.ConfirmedBalance)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}, defaultTimeout)
|
|
||||||
require.NoError(ht, err, "timeout checking dave's wallet balance")
|
|
||||||
|
|
||||||
// Dave should have no open channels.
|
|
||||||
ht.AssertNodeNumChannels(dave, 0)
|
|
||||||
}
|
|
||||||
+602
-78
@@ -2,24 +2,234 @@ package itest
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/funding"
|
"github.com/lightningnetwork/lnd/funding"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/wtclientrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/wtclientrpc"
|
||||||
"github.com/lightningnetwork/lnd/lntest"
|
"github.com/lightningnetwork/lnd/lntest"
|
||||||
"github.com/lightningnetwork/lnd/lntest/node"
|
"github.com/lightningnetwork/lnd/lntest/node"
|
||||||
|
"github.com/lightningnetwork/lnd/lntest/rpc"
|
||||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
// testWatchtowerSessionManagement tests that session deletion is done
|
// testWatchtower tests the behaviour of the watchtower client and server.
|
||||||
// correctly.
|
func testWatchtower(ht *lntest.HarnessTest) {
|
||||||
func testWatchtowerSessionManagement(ht *lntest.HarnessTest) {
|
ht.Run("revocation", func(t *testing.T) {
|
||||||
|
tt := ht.Subtest(t)
|
||||||
|
testRevokedCloseRetributionAltruistWatchtower(tt)
|
||||||
|
})
|
||||||
|
|
||||||
|
ht.Run("session deletion", func(t *testing.T) {
|
||||||
|
tt := ht.Subtest(t)
|
||||||
|
testTowerClientSessionDeletion(tt)
|
||||||
|
})
|
||||||
|
|
||||||
|
ht.Run("tower and session activation", func(t *testing.T) {
|
||||||
|
tt := ht.Subtest(t)
|
||||||
|
testTowerClientTowerAndSessionManagement(tt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// testTowerClientTowerAndSessionManagement tests the various control commands
|
||||||
|
// that a user has over the client's set of active towers and sessions.
|
||||||
|
func testTowerClientTowerAndSessionManagement(ht *lntest.HarnessTest) {
|
||||||
|
const (
|
||||||
|
chanAmt = funding.MaxBtcFundingAmount
|
||||||
|
externalIP = "1.2.3.4"
|
||||||
|
externalIP2 = "1.2.3.5"
|
||||||
|
sessionCloseRange = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set up Wallis the watchtower who will be used by Dave to watch over
|
||||||
|
// his channel commitment transactions.
|
||||||
|
wallisPk, wallisListener, _ := setUpNewTower(ht, "Wallis", externalIP)
|
||||||
|
|
||||||
|
// Dave will be the tower client.
|
||||||
|
daveArgs := []string{
|
||||||
|
"--wtclient.active",
|
||||||
|
fmt.Sprintf(
|
||||||
|
"--wtclient.session-close-range=%d", sessionCloseRange,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
dave := ht.NewNode("Dave", daveArgs)
|
||||||
|
|
||||||
|
addWallisReq := &wtclientrpc.AddTowerRequest{
|
||||||
|
Pubkey: wallisPk,
|
||||||
|
Address: wallisListener,
|
||||||
|
}
|
||||||
|
dave.RPC.AddTower(addWallisReq)
|
||||||
|
|
||||||
|
assertNumSessions := func(towerPk []byte, expectedNum int) {
|
||||||
|
err := wait.NoError(func() error {
|
||||||
|
info := dave.RPC.GetTowerInfo(
|
||||||
|
&wtclientrpc.GetTowerInfoRequest{
|
||||||
|
Pubkey: towerPk,
|
||||||
|
IncludeSessions: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
var numSessions uint32
|
||||||
|
for _, sessionType := range info.SessionInfo {
|
||||||
|
numSessions += sessionType.NumSessions
|
||||||
|
}
|
||||||
|
if numSessions == uint32(expectedNum) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("expected %d sessions, got %d",
|
||||||
|
expectedNum, numSessions)
|
||||||
|
}, defaultTimeout)
|
||||||
|
require.NoError(ht, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that there are a few sessions between Dave and Wallis. There
|
||||||
|
// should be one per client. There are currently 3 types of clients, so
|
||||||
|
// we expect 3 sessions.
|
||||||
|
assertNumSessions(wallisPk, 3)
|
||||||
|
|
||||||
|
// Before we make a channel, we'll load up Dave with some coins sent
|
||||||
|
// directly from the miner.
|
||||||
|
ht.FundCoins(btcutil.SatoshiPerBitcoin, dave)
|
||||||
|
|
||||||
|
// Connect Dave and Alice.
|
||||||
|
ht.ConnectNodes(dave, ht.Alice)
|
||||||
|
|
||||||
|
// Open a channel between Dave and Alice.
|
||||||
|
params := lntest.OpenChannelParams{
|
||||||
|
Amt: chanAmt,
|
||||||
|
}
|
||||||
|
chanPoint := ht.OpenChannel(dave, ht.Alice, params)
|
||||||
|
|
||||||
|
// Show that the Wallis tower is currently seen as an active session
|
||||||
|
// candidate.
|
||||||
|
info := dave.RPC.GetTowerInfo(&wtclientrpc.GetTowerInfoRequest{
|
||||||
|
Pubkey: wallisPk,
|
||||||
|
})
|
||||||
|
require.GreaterOrEqual(ht, len(info.SessionInfo), 1)
|
||||||
|
require.True(ht, info.SessionInfo[0].ActiveSessionCandidate)
|
||||||
|
|
||||||
|
// Make some back-ups and assert that they are added to a session with
|
||||||
|
// the tower.
|
||||||
|
generateBackups(ht, dave, ht.Alice, 4)
|
||||||
|
|
||||||
|
// Assert that one of the sessions now has 4 backups.
|
||||||
|
assertNumBackups(ht, dave.RPC, wallisPk, 4, false)
|
||||||
|
|
||||||
|
// Now, deactivate the tower and show that it is no longer considered
|
||||||
|
// an active session candidate.
|
||||||
|
dave.RPC.DeactivateTower(&wtclientrpc.DeactivateTowerRequest{
|
||||||
|
Pubkey: wallisPk,
|
||||||
|
})
|
||||||
|
info = dave.RPC.GetTowerInfo(&wtclientrpc.GetTowerInfoRequest{
|
||||||
|
Pubkey: wallisPk,
|
||||||
|
})
|
||||||
|
require.GreaterOrEqual(ht, len(info.SessionInfo), 1)
|
||||||
|
require.False(ht, info.SessionInfo[0].ActiveSessionCandidate)
|
||||||
|
|
||||||
|
// Back up a few more states.
|
||||||
|
generateBackups(ht, dave, ht.Alice, 4)
|
||||||
|
|
||||||
|
// These should _not_ be on the tower. Therefore, the number of
|
||||||
|
// back-ups on the tower should be the same as before.
|
||||||
|
assertNumBackups(ht, dave.RPC, wallisPk, 4, false)
|
||||||
|
|
||||||
|
// Add new tower and connect Dave to it.
|
||||||
|
wilmaPk, wilmaListener, _ := setUpNewTower(ht, "Wilma", externalIP2)
|
||||||
|
dave.RPC.AddTower(&wtclientrpc.AddTowerRequest{
|
||||||
|
Pubkey: wilmaPk,
|
||||||
|
Address: wilmaListener,
|
||||||
|
})
|
||||||
|
assertNumSessions(wilmaPk, 3)
|
||||||
|
|
||||||
|
// The updates from before should now appear on the new watchtower.
|
||||||
|
assertNumBackups(ht, dave.RPC, wilmaPk, 4, false)
|
||||||
|
|
||||||
|
// Reactivate the Wallis tower and then deactivate the Wilma one.
|
||||||
|
dave.RPC.AddTower(addWallisReq)
|
||||||
|
dave.RPC.DeactivateTower(&wtclientrpc.DeactivateTowerRequest{
|
||||||
|
Pubkey: wilmaPk,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Generate some more back-ups.
|
||||||
|
generateBackups(ht, dave, ht.Alice, 4)
|
||||||
|
|
||||||
|
// Assert that they get added to the first tower (Wallis) and that the
|
||||||
|
// number of sessions with Wallis has not changed - in other words, the
|
||||||
|
// previously used session was re-used.
|
||||||
|
assertNumBackups(ht, dave.RPC, wallisPk, 8, false)
|
||||||
|
assertNumSessions(wallisPk, 3)
|
||||||
|
|
||||||
|
findSession := func(towerPk []byte, numBackups uint32) []byte {
|
||||||
|
info := dave.RPC.GetTowerInfo(&wtclientrpc.GetTowerInfoRequest{
|
||||||
|
Pubkey: towerPk,
|
||||||
|
IncludeSessions: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, sessionType := range info.SessionInfo {
|
||||||
|
for _, session := range sessionType.Sessions {
|
||||||
|
if session.NumBackups == numBackups {
|
||||||
|
return session.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ht.Fatalf("session with %d backups not found", numBackups)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we will test the termination of a session.
|
||||||
|
// First, we need to figure out the ID of the session that has been used
|
||||||
|
// for back-ups.
|
||||||
|
sessionID := findSession(wallisPk, 8)
|
||||||
|
|
||||||
|
// Now, terminate the session.
|
||||||
|
dave.RPC.TerminateSession(&wtclientrpc.TerminateSessionRequest{
|
||||||
|
SessionId: sessionID,
|
||||||
|
})
|
||||||
|
|
||||||
|
// This should force the client to negotiate a new session. The old
|
||||||
|
// session still remains in our session list since the channel for which
|
||||||
|
// it has updates for is still open.
|
||||||
|
assertNumSessions(wallisPk, 4)
|
||||||
|
|
||||||
|
// Any new back-ups should now be backed up on a different session.
|
||||||
|
generateBackups(ht, dave, ht.Alice, 2)
|
||||||
|
assertNumBackups(ht, dave.RPC, wallisPk, 10, false)
|
||||||
|
findSession(wallisPk, 2)
|
||||||
|
|
||||||
|
// Close the channel.
|
||||||
|
ht.CloseChannelAssertPending(dave, chanPoint, false)
|
||||||
|
|
||||||
|
// Mine enough blocks to surpass the session close range buffer.
|
||||||
|
ht.MineBlocksAndAssertNumTxes(sessionCloseRange+6, 1)
|
||||||
|
|
||||||
|
// The session that was previously terminated now gets deleted since
|
||||||
|
// the channel for which it has updates has now been closed. All the
|
||||||
|
// remaining sessions are not yet closable since they are not yet
|
||||||
|
// exhausted and are all still active.
|
||||||
|
assertNumSessions(wallisPk, 3)
|
||||||
|
|
||||||
|
// For the sake of completion, we call RemoveTower here for both towers
|
||||||
|
// to show that this should never error.
|
||||||
|
dave.RPC.RemoveTower(&wtclientrpc.RemoveTowerRequest{
|
||||||
|
Pubkey: wallisPk,
|
||||||
|
})
|
||||||
|
dave.RPC.RemoveTower(&wtclientrpc.RemoveTowerRequest{
|
||||||
|
Pubkey: wilmaPk,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// testTowerClientSessionDeletion tests that sessions are correctly deleted
|
||||||
|
// when they are deemed closable.
|
||||||
|
func testTowerClientSessionDeletion(ht *lntest.HarnessTest) {
|
||||||
const (
|
const (
|
||||||
chanAmt = funding.MaxBtcFundingAmount
|
chanAmt = funding.MaxBtcFundingAmount
|
||||||
paymentAmt = 10_000
|
|
||||||
numInvoices = 5
|
numInvoices = 5
|
||||||
maxUpdates = numInvoices * 2
|
maxUpdates = numInvoices * 2
|
||||||
externalIP = "1.2.3.4"
|
externalIP = "1.2.3.4"
|
||||||
@@ -28,24 +238,7 @@ func testWatchtowerSessionManagement(ht *lntest.HarnessTest) {
|
|||||||
|
|
||||||
// Set up Wallis the watchtower who will be used by Dave to watch over
|
// Set up Wallis the watchtower who will be used by Dave to watch over
|
||||||
// his channel commitment transactions.
|
// his channel commitment transactions.
|
||||||
wallis := ht.NewNode("Wallis", []string{
|
wallisPk, listener, _ := setUpNewTower(ht, "Wallis", externalIP)
|
||||||
"--watchtower.active",
|
|
||||||
"--watchtower.externalip=" + externalIP,
|
|
||||||
})
|
|
||||||
|
|
||||||
wallisInfo := wallis.RPC.GetInfoWatchtower()
|
|
||||||
|
|
||||||
// Assert that Wallis has one listener and it is 0.0.0.0:9911 or
|
|
||||||
// [::]:9911. Since no listener is explicitly specified, one of these
|
|
||||||
// should be the default depending on whether the host supports IPv6 or
|
|
||||||
// not.
|
|
||||||
require.Len(ht, wallisInfo.Listeners, 1)
|
|
||||||
listener := wallisInfo.Listeners[0]
|
|
||||||
require.True(ht, listener == "0.0.0.0:9911" || listener == "[::]:9911")
|
|
||||||
|
|
||||||
// Assert the Wallis's URIs properly display the chosen external IP.
|
|
||||||
require.Len(ht, wallisInfo.Uris, 1)
|
|
||||||
require.Contains(ht, wallisInfo.Uris[0], externalIP)
|
|
||||||
|
|
||||||
// Dave will be the tower client.
|
// Dave will be the tower client.
|
||||||
daveArgs := []string{
|
daveArgs := []string{
|
||||||
@@ -58,7 +251,7 @@ func testWatchtowerSessionManagement(ht *lntest.HarnessTest) {
|
|||||||
dave := ht.NewNode("Dave", daveArgs)
|
dave := ht.NewNode("Dave", daveArgs)
|
||||||
|
|
||||||
addTowerReq := &wtclientrpc.AddTowerRequest{
|
addTowerReq := &wtclientrpc.AddTowerRequest{
|
||||||
Pubkey: wallisInfo.Pubkey,
|
Pubkey: wallisPk,
|
||||||
Address: listener,
|
Address: listener,
|
||||||
}
|
}
|
||||||
dave.RPC.AddTower(addTowerReq)
|
dave.RPC.AddTower(addTowerReq)
|
||||||
@@ -66,7 +259,7 @@ func testWatchtowerSessionManagement(ht *lntest.HarnessTest) {
|
|||||||
// Assert that there exists a session between Dave and Wallis.
|
// Assert that there exists a session between Dave and Wallis.
|
||||||
err := wait.NoError(func() error {
|
err := wait.NoError(func() error {
|
||||||
info := dave.RPC.GetTowerInfo(&wtclientrpc.GetTowerInfoRequest{
|
info := dave.RPC.GetTowerInfo(&wtclientrpc.GetTowerInfoRequest{
|
||||||
Pubkey: wallisInfo.Pubkey,
|
Pubkey: wallisPk,
|
||||||
IncludeSessions: true,
|
IncludeSessions: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -98,61 +291,10 @@ func testWatchtowerSessionManagement(ht *lntest.HarnessTest) {
|
|||||||
// Since there are 2 updates made for every payment and the maximum
|
// Since there are 2 updates made for every payment and the maximum
|
||||||
// number of updates per session has been set to 10, make 5 payments
|
// number of updates per session has been set to 10, make 5 payments
|
||||||
// between the pair so that the session is exhausted.
|
// between the pair so that the session is exhausted.
|
||||||
alicePayReqs, _, _ := ht.CreatePayReqs(
|
generateBackups(ht, dave, ht.Alice, maxUpdates)
|
||||||
ht.Alice, paymentAmt, numInvoices,
|
|
||||||
)
|
|
||||||
|
|
||||||
send := func(node *node.HarnessNode, payReq string) {
|
|
||||||
stream := node.RPC.SendPayment(&routerrpc.SendPaymentRequest{
|
|
||||||
PaymentRequest: payReq,
|
|
||||||
TimeoutSeconds: 60,
|
|
||||||
FeeLimitMsat: noFeeLimitMsat,
|
|
||||||
})
|
|
||||||
|
|
||||||
ht.AssertPaymentStatusFromStream(
|
|
||||||
stream, lnrpc.Payment_SUCCEEDED,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < numInvoices; i++ {
|
|
||||||
send(dave, alicePayReqs[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// assertNumBackups is a closure that asserts that Dave has a certain
|
|
||||||
// number of backups backed up to the tower. If mineOnFail is true,
|
|
||||||
// then a block will be mined each time the assertion fails.
|
|
||||||
assertNumBackups := func(expected int, mineOnFail bool) {
|
|
||||||
err = wait.NoError(func() error {
|
|
||||||
info := dave.RPC.GetTowerInfo(
|
|
||||||
&wtclientrpc.GetTowerInfoRequest{
|
|
||||||
Pubkey: wallisInfo.Pubkey,
|
|
||||||
IncludeSessions: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
var numBackups uint32
|
|
||||||
for _, sessionType := range info.SessionInfo {
|
|
||||||
for _, session := range sessionType.Sessions {
|
|
||||||
numBackups += session.NumBackups
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if numBackups == uint32(expected) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if mineOnFail {
|
|
||||||
ht.Miner.MineBlocksSlow(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("expected %d backups, got %d",
|
|
||||||
expected, numBackups)
|
|
||||||
}, defaultTimeout)
|
|
||||||
require.NoError(ht, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert that one of the sessions now has 10 backups.
|
// Assert that one of the sessions now has 10 backups.
|
||||||
assertNumBackups(10, false)
|
assertNumBackups(ht, dave.RPC, wallisPk, 10, false)
|
||||||
|
|
||||||
// Now close the channel and wait for the close transaction to appear
|
// Now close the channel and wait for the close transaction to appear
|
||||||
// in the mempool so that it is included in a block when we mine.
|
// in the mempool so that it is included in a block when we mine.
|
||||||
@@ -168,5 +310,387 @@ func testWatchtowerSessionManagement(ht *lntest.HarnessTest) {
|
|||||||
// would immediately negotiate another session after deleting the
|
// would immediately negotiate another session after deleting the
|
||||||
// exhausted one. This time we set the "mineOnFail" parameter to true to
|
// exhausted one. This time we set the "mineOnFail" parameter to true to
|
||||||
// ensure that the session deleting logic is run.
|
// ensure that the session deleting logic is run.
|
||||||
assertNumBackups(0, true)
|
assertNumBackups(ht, dave.RPC, wallisPk, 0, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// testRevokedCloseRetributionAltruistWatchtower establishes a channel between
|
||||||
|
// Carol and Dave, where Carol is using a third node Willy as her watchtower.
|
||||||
|
// After sending some payments, Dave reverts his state and force closes to
|
||||||
|
// trigger a breach. Carol is kept offline throughout the process and the test
|
||||||
|
// asserts that Willy responds by broadcasting the justice transaction on
|
||||||
|
// Carol's behalf sweeping her funds without a reward.
|
||||||
|
func testRevokedCloseRetributionAltruistWatchtower(ht *lntest.HarnessTest) {
|
||||||
|
for _, commitType := range []lnrpc.CommitmentType{
|
||||||
|
lnrpc.CommitmentType_LEGACY,
|
||||||
|
lnrpc.CommitmentType_ANCHORS,
|
||||||
|
lnrpc.CommitmentType_SIMPLE_TAPROOT,
|
||||||
|
} {
|
||||||
|
testName := fmt.Sprintf("%v", commitType.String())
|
||||||
|
ct := commitType
|
||||||
|
testFunc := func(ht *lntest.HarnessTest) {
|
||||||
|
testRevokedCloseRetributionAltruistWatchtowerCase(
|
||||||
|
ht, ct,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
success := ht.Run(testName, func(tt *testing.T) {
|
||||||
|
st := ht.Subtest(tt)
|
||||||
|
|
||||||
|
st.RunTestCase(&lntest.TestCase{
|
||||||
|
Name: testName,
|
||||||
|
TestFunc: testFunc,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if !success {
|
||||||
|
// Log failure time to help relate the lnd logs to the
|
||||||
|
// failure.
|
||||||
|
ht.Logf("Failure time: %v", time.Now().Format(
|
||||||
|
"2006-01-02 15:04:05.000",
|
||||||
|
))
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRevokedCloseRetributionAltruistWatchtowerCase(ht *lntest.HarnessTest,
|
||||||
|
commitType lnrpc.CommitmentType) {
|
||||||
|
|
||||||
|
const (
|
||||||
|
chanAmt = funding.MaxBtcFundingAmount
|
||||||
|
paymentAmt = 10000
|
||||||
|
numInvoices = 6
|
||||||
|
externalIP = "1.2.3.4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Since we'd like to test some multi-hop failure scenarios, we'll
|
||||||
|
// introduce another node into our test network: Carol.
|
||||||
|
carolArgs := lntest.NodeArgsForCommitType(commitType)
|
||||||
|
carolArgs = append(carolArgs, "--hodl.exit-settle")
|
||||||
|
|
||||||
|
carol := ht.NewNode("Carol", carolArgs)
|
||||||
|
|
||||||
|
// Set up Willy the watchtower who will protect Dave from Carol's
|
||||||
|
// breach. He will remain online in order to punish Carol on Dave's
|
||||||
|
// behalf, since the breach will happen while Dave is offline.
|
||||||
|
willyInfoPk, listener, willy := setUpNewTower(ht, "Willy", externalIP)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// protection logic automatically.
|
||||||
|
daveArgs := lntest.NodeArgsForCommitType(commitType)
|
||||||
|
daveArgs = append(daveArgs, "--nolisten", "--wtclient.active")
|
||||||
|
dave := ht.NewNode("Dave", daveArgs)
|
||||||
|
|
||||||
|
addTowerReq := &wtclientrpc.AddTowerRequest{
|
||||||
|
Pubkey: willyInfoPk,
|
||||||
|
Address: listener,
|
||||||
|
}
|
||||||
|
dave.RPC.AddTower(addTowerReq)
|
||||||
|
|
||||||
|
// We must let Dave have an open channel before she can send a node
|
||||||
|
// announcement, so we open a channel with Carol,
|
||||||
|
ht.ConnectNodes(dave, carol)
|
||||||
|
|
||||||
|
// Before we make a channel, we'll load up Dave with some coins sent
|
||||||
|
// directly from the miner.
|
||||||
|
ht.FundCoins(btcutil.SatoshiPerBitcoin, dave)
|
||||||
|
|
||||||
|
// Send one more UTXOs if this is a neutrino backend.
|
||||||
|
if ht.IsNeutrinoBackend() {
|
||||||
|
ht.FundCoins(btcutil.SatoshiPerBitcoin, dave)
|
||||||
|
}
|
||||||
|
|
||||||
|
// In order to test Dave's response to an uncooperative channel
|
||||||
|
// closure by Carol, we'll first open up a channel between them with a
|
||||||
|
// 0.5 BTC value.
|
||||||
|
params := lntest.OpenChannelParams{
|
||||||
|
Amt: 3 * (chanAmt / 4),
|
||||||
|
PushAmt: chanAmt / 4,
|
||||||
|
CommitmentType: commitType,
|
||||||
|
Private: true,
|
||||||
|
}
|
||||||
|
chanPoint := ht.OpenChannel(dave, carol, params)
|
||||||
|
|
||||||
|
// With the channel open, we'll create a few invoices for Carol that
|
||||||
|
// Dave will pay to in order to advance the state of the channel.
|
||||||
|
carolPayReqs, _, _ := ht.CreatePayReqs(carol, paymentAmt, numInvoices)
|
||||||
|
|
||||||
|
// Next query for Carol's channel state, as we sent 0 payments, Carol
|
||||||
|
// should still see her balance as the push amount, which is 1/4 of the
|
||||||
|
// capacity.
|
||||||
|
carolChan := ht.AssertChannelLocalBalance(
|
||||||
|
carol, chanPoint, int64(chanAmt/4),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Grab Carol's current commitment height (update number), we'll later
|
||||||
|
// revert her to this state after additional updates to force him to
|
||||||
|
// broadcast this soon to be revoked state.
|
||||||
|
carolStateNumPreCopy := int(carolChan.NumUpdates)
|
||||||
|
|
||||||
|
// With the temporary file created, copy Carol's current state into the
|
||||||
|
// temporary file we created above. Later after more updates, we'll
|
||||||
|
// restore this state.
|
||||||
|
ht.BackupDB(carol)
|
||||||
|
|
||||||
|
// Reconnect the peers after the restart that was needed for the db
|
||||||
|
// backup.
|
||||||
|
ht.EnsureConnected(dave, carol)
|
||||||
|
|
||||||
|
// Once connected, give Dave some time to enable the channel again.
|
||||||
|
ht.AssertTopologyChannelOpen(dave, chanPoint)
|
||||||
|
|
||||||
|
// Finally, send payments from Dave to Carol, consuming Carol's
|
||||||
|
// remaining payment hashes.
|
||||||
|
ht.CompletePaymentRequestsNoWait(dave, carolPayReqs, chanPoint)
|
||||||
|
|
||||||
|
daveBalResp := dave.RPC.WalletBalance()
|
||||||
|
davePreSweepBalance := daveBalResp.ConfirmedBalance
|
||||||
|
|
||||||
|
// Wait until the backup has been accepted by the watchtower before
|
||||||
|
// shutting down Dave.
|
||||||
|
err := wait.NoError(func() error {
|
||||||
|
bkpStats := dave.RPC.WatchtowerStats()
|
||||||
|
if bkpStats == nil {
|
||||||
|
return errors.New("no active backup sessions")
|
||||||
|
}
|
||||||
|
if bkpStats.NumBackups == 0 {
|
||||||
|
return errors.New("no backups accepted")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, defaultTimeout)
|
||||||
|
require.NoError(ht, err, "unable to verify backup task completed")
|
||||||
|
|
||||||
|
// Shutdown Dave to simulate going offline for an extended period of
|
||||||
|
// time. Once he's not watching, Carol will try to breach the channel.
|
||||||
|
restart := ht.SuspendNode(dave)
|
||||||
|
|
||||||
|
// Now we shutdown Carol, copying over the his temporary database state
|
||||||
|
// which has the *prior* channel state over his current most up to date
|
||||||
|
// state. With this, we essentially force Carol to travel back in time
|
||||||
|
// within the channel's history.
|
||||||
|
ht.RestartNodeAndRestoreDB(carol)
|
||||||
|
|
||||||
|
// Now query for Carol's channel state, it should show that he's at a
|
||||||
|
// state number in the past, not the *latest* state.
|
||||||
|
ht.AssertChannelCommitHeight(carol, chanPoint, carolStateNumPreCopy)
|
||||||
|
|
||||||
|
// Now force Carol to execute a *force* channel closure by unilaterally
|
||||||
|
// broadcasting his current channel state. This is actually the
|
||||||
|
// commitment transaction of a prior *revoked* state, so he'll soon
|
||||||
|
// feel the wrath of Dave's retribution.
|
||||||
|
closeUpdates, closeTxID := ht.CloseChannelAssertPending(
|
||||||
|
carol, chanPoint, true,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Finally, generate a single block, wait for the final close status
|
||||||
|
// update, then ensure that the closing transaction was included in the
|
||||||
|
// block.
|
||||||
|
block := ht.MineBlocksAndAssertNumTxes(1, 1)[0]
|
||||||
|
|
||||||
|
breachTXID := ht.WaitForChannelCloseEvent(closeUpdates)
|
||||||
|
ht.Miner.AssertTxInBlock(block, breachTXID)
|
||||||
|
|
||||||
|
// The breachTXID should match the above closeTxID.
|
||||||
|
require.EqualValues(ht, breachTXID, closeTxID)
|
||||||
|
|
||||||
|
// Query the mempool for Dave's justice transaction, this should be
|
||||||
|
// broadcast as Carol's contract breaching transaction gets confirmed
|
||||||
|
// above.
|
||||||
|
justiceTXID := ht.Miner.AssertNumTxsInMempool(1)[0]
|
||||||
|
|
||||||
|
// Query for the mempool transaction found above. Then assert that all
|
||||||
|
// the inputs of this transaction are spending outputs generated by
|
||||||
|
// Carol's breach transaction above.
|
||||||
|
justiceTx := ht.Miner.GetRawTransaction(justiceTXID)
|
||||||
|
for _, txIn := range justiceTx.MsgTx().TxIn {
|
||||||
|
require.Equal(ht, breachTXID[:], txIn.PreviousOutPoint.Hash[:],
|
||||||
|
"justice tx not spending commitment utxo")
|
||||||
|
}
|
||||||
|
|
||||||
|
willyBalResp := willy.WalletBalance()
|
||||||
|
require.Zero(ht, willyBalResp.ConfirmedBalance,
|
||||||
|
"willy should have 0 balance before mining justice transaction")
|
||||||
|
|
||||||
|
// Now mine a block, this transaction should include Dave's justice
|
||||||
|
// transaction which was just accepted into the mempool.
|
||||||
|
block = ht.MineBlocksAndAssertNumTxes(1, 1)[0]
|
||||||
|
|
||||||
|
// The block should have exactly *two* transactions, one of which is
|
||||||
|
// the justice transaction.
|
||||||
|
require.Len(ht, block.Transactions, 2, "transaction wasn't mined")
|
||||||
|
justiceSha := block.Transactions[1].TxHash()
|
||||||
|
require.Equal(ht, justiceTx.Hash()[:], justiceSha[:],
|
||||||
|
"justice tx wasn't mined")
|
||||||
|
|
||||||
|
// Ensure that Willy doesn't get any funds, as he is acting as an
|
||||||
|
// altruist watchtower.
|
||||||
|
err = wait.NoError(func() error {
|
||||||
|
willyBalResp := willy.WalletBalance()
|
||||||
|
|
||||||
|
if willyBalResp.ConfirmedBalance != 0 {
|
||||||
|
return fmt.Errorf("expected Willy to have no funds "+
|
||||||
|
"after justice transaction was mined, found %v",
|
||||||
|
willyBalResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, time.Second*5)
|
||||||
|
require.NoError(ht, err, "timeout checking willy's balance")
|
||||||
|
|
||||||
|
// Before restarting Dave, shutdown Carol so Dave won't sync with her.
|
||||||
|
// Otherwise, during the restart, Dave will realize Carol is falling
|
||||||
|
// behind and return `ErrCommitSyncRemoteDataLoss`, thus force closing
|
||||||
|
// the channel. Although this force close tx will be later replaced by
|
||||||
|
// the breach tx, it will create two anchor sweeping txes for neutrino
|
||||||
|
// backend, causing the confirmed wallet balance to be zero later on
|
||||||
|
// because the utxos are used in sweeping.
|
||||||
|
ht.Shutdown(carol)
|
||||||
|
|
||||||
|
// Restart Dave, who will still think his channel with Carol is open.
|
||||||
|
// We should him to detect the breach, but realize that the funds have
|
||||||
|
// then been swept to his wallet by Willy.
|
||||||
|
require.NoError(ht, restart(), "unable to restart dave")
|
||||||
|
|
||||||
|
err = wait.NoError(func() error {
|
||||||
|
daveBalResp := dave.RPC.ChannelBalance()
|
||||||
|
if daveBalResp.LocalBalance.Sat != 0 {
|
||||||
|
return fmt.Errorf("Dave should end up with zero "+
|
||||||
|
"channel balance, instead has %d",
|
||||||
|
daveBalResp.LocalBalance.Sat)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, defaultTimeout)
|
||||||
|
require.NoError(ht, err, "timeout checking dave's channel balance")
|
||||||
|
|
||||||
|
ht.AssertNumPendingForceClose(dave, 0)
|
||||||
|
|
||||||
|
// If this is an anchor channel, Dave would sweep the anchor.
|
||||||
|
if lntest.CommitTypeHasAnchors(commitType) {
|
||||||
|
ht.MineBlocksAndAssertNumTxes(1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that Dave's wallet balance is increased.
|
||||||
|
err = wait.NoError(func() error {
|
||||||
|
daveBalResp := dave.RPC.WalletBalance()
|
||||||
|
|
||||||
|
if daveBalResp.ConfirmedBalance <= davePreSweepBalance {
|
||||||
|
return fmt.Errorf("Dave should have more than %d "+
|
||||||
|
"after sweep, instead has %d",
|
||||||
|
davePreSweepBalance,
|
||||||
|
daveBalResp.ConfirmedBalance)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, defaultTimeout)
|
||||||
|
require.NoError(ht, err, "timeout checking dave's wallet balance")
|
||||||
|
|
||||||
|
// Dave should have no open channels.
|
||||||
|
ht.AssertNodeNumChannels(dave, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUpNewTower(ht *lntest.HarnessTest, name, externalIP string) ([]byte,
|
||||||
|
string, *rpc.HarnessRPC) {
|
||||||
|
|
||||||
|
port := node.NextAvailablePort()
|
||||||
|
|
||||||
|
listenAddr := fmt.Sprintf("0.0.0.0:%d", port)
|
||||||
|
|
||||||
|
// Set up the new watchtower.
|
||||||
|
tower := ht.NewNode(name, []string{
|
||||||
|
"--watchtower.active",
|
||||||
|
"--watchtower.externalip=" + externalIP,
|
||||||
|
"--watchtower.listen=" + listenAddr,
|
||||||
|
})
|
||||||
|
|
||||||
|
towerInfo := tower.RPC.GetInfoWatchtower()
|
||||||
|
|
||||||
|
require.Len(ht, towerInfo.Listeners, 1)
|
||||||
|
listener := towerInfo.Listeners[0]
|
||||||
|
require.True(
|
||||||
|
ht, listener == listenAddr ||
|
||||||
|
listener == fmt.Sprintf("[::]:%d", port),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assert the Tower's URIs properly display the chosen external IP.
|
||||||
|
require.Len(ht, towerInfo.Uris, 1)
|
||||||
|
require.Contains(ht, towerInfo.Uris[0], externalIP)
|
||||||
|
|
||||||
|
return towerInfo.Pubkey, towerInfo.Listeners[0], tower.RPC
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateBackups is a helper function that can be used to create a number of
|
||||||
|
// watchtower back-ups.
|
||||||
|
func generateBackups(ht *lntest.HarnessTest, srcNode,
|
||||||
|
dstNode *node.HarnessNode, numBackups int64) {
|
||||||
|
|
||||||
|
const paymentAmt = 10_000
|
||||||
|
|
||||||
|
require.EqualValuesf(ht, numBackups%2, 0, "the number of desired "+
|
||||||
|
"back-ups must be even")
|
||||||
|
|
||||||
|
// Two updates are made for every payment.
|
||||||
|
numPayments := int(numBackups / 2)
|
||||||
|
|
||||||
|
// Create the required number of invoices.
|
||||||
|
alicePayReqs, _, _ := ht.CreatePayReqs(
|
||||||
|
dstNode, paymentAmt, numPayments,
|
||||||
|
)
|
||||||
|
|
||||||
|
send := func(node *node.HarnessNode, payReq string) {
|
||||||
|
stream := node.RPC.SendPayment(
|
||||||
|
&routerrpc.SendPaymentRequest{
|
||||||
|
PaymentRequest: payReq,
|
||||||
|
TimeoutSeconds: 60,
|
||||||
|
FeeLimitMsat: noFeeLimitMsat,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
ht.AssertPaymentStatusFromStream(
|
||||||
|
stream, lnrpc.Payment_SUCCEEDED,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pay each invoice.
|
||||||
|
for i := 0; i < numPayments; i++ {
|
||||||
|
send(srcNode, alicePayReqs[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertNumBackups is a helper that asserts that the given node has a certain
|
||||||
|
// number of backups backed up to the tower. If mineOnFail is true, then a block
|
||||||
|
// will be mined each time the assertion fails.
|
||||||
|
func assertNumBackups(ht *lntest.HarnessTest, node *rpc.HarnessRPC,
|
||||||
|
towerPk []byte, expectedNumBackups int, mineOnFail bool) {
|
||||||
|
|
||||||
|
err := wait.NoError(func() error {
|
||||||
|
info := node.GetTowerInfo(
|
||||||
|
&wtclientrpc.GetTowerInfoRequest{
|
||||||
|
Pubkey: towerPk,
|
||||||
|
IncludeSessions: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
var numBackups uint32
|
||||||
|
for _, sessionType := range info.SessionInfo {
|
||||||
|
for _, session := range sessionType.Sessions {
|
||||||
|
numBackups += session.NumBackups
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if numBackups == uint32(expectedNumBackups) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if mineOnFail {
|
||||||
|
ht.Miner.MineBlocksSlow(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("expected %d backups, got %d",
|
||||||
|
expectedNumBackups, numBackups)
|
||||||
|
}, defaultTimeout)
|
||||||
|
require.NoError(ht, err)
|
||||||
}
|
}
|
||||||
@@ -71,6 +71,56 @@ func RegisterWatchtowerClientJSONCallbacks(registry map[string]func(ctx context.
|
|||||||
callback(string(respBytes), nil)
|
callback(string(respBytes), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registry["wtclientrpc.WatchtowerClient.DeactivateTower"] = func(ctx context.Context,
|
||||||
|
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||||
|
|
||||||
|
req := &DeactivateTowerRequest{}
|
||||||
|
err := marshaler.Unmarshal([]byte(reqJSON), req)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client := NewWatchtowerClientClient(conn)
|
||||||
|
resp, err := client.DeactivateTower(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respBytes, err := marshaler.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback(string(respBytes), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
registry["wtclientrpc.WatchtowerClient.TerminateSession"] = func(ctx context.Context,
|
||||||
|
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||||
|
|
||||||
|
req := &TerminateSessionRequest{}
|
||||||
|
err := marshaler.Unmarshal([]byte(reqJSON), req)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client := NewWatchtowerClientClient(conn)
|
||||||
|
resp, err := client.TerminateSession(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respBytes, err := marshaler.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback(string(respBytes), nil)
|
||||||
|
}
|
||||||
|
|
||||||
registry["wtclientrpc.WatchtowerClient.ListTowers"] = func(ctx context.Context,
|
registry["wtclientrpc.WatchtowerClient.ListTowers"] = func(ctx context.Context,
|
||||||
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,14 @@ var (
|
|||||||
Entity: "offchain",
|
Entity: "offchain",
|
||||||
Action: "write",
|
Action: "write",
|
||||||
}},
|
}},
|
||||||
|
"/wtclientrpc.WatchtowerClient/DeactivateTower": {{
|
||||||
|
Entity: "offchain",
|
||||||
|
Action: "write",
|
||||||
|
}},
|
||||||
|
"/wtclientrpc.WatchtowerClient/TerminateSession": {{
|
||||||
|
Entity: "offchain",
|
||||||
|
Action: "write",
|
||||||
|
}},
|
||||||
"/wtclientrpc.WatchtowerClient/ListTowers": {{
|
"/wtclientrpc.WatchtowerClient/ListTowers": {{
|
||||||
Entity: "offchain",
|
Entity: "offchain",
|
||||||
Action: "read",
|
Action: "read",
|
||||||
@@ -251,6 +259,59 @@ func (c *WatchtowerClient) RemoveTower(ctx context.Context,
|
|||||||
return &RemoveTowerResponse{}, nil
|
return &RemoveTowerResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeactivateTower sets the given tower's status to inactive so that it is not
|
||||||
|
// considered for session negotiation. Its sessions will also not be used while
|
||||||
|
// the tower is inactive.
|
||||||
|
func (c *WatchtowerClient) DeactivateTower(_ context.Context,
|
||||||
|
req *DeactivateTowerRequest) (*DeactivateTowerResponse, error) {
|
||||||
|
|
||||||
|
if err := c.isActive(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey, err := btcec.ParsePubKey(req.Pubkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.cfg.ClientMgr.DeactivateTower(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DeactivateTowerResponse{
|
||||||
|
Status: fmt.Sprintf("Successful deactivation of tower: %x",
|
||||||
|
req.Pubkey),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TerminateSession terminates the given session and marks it as terminal so
|
||||||
|
// that it is never used again.
|
||||||
|
func (c *WatchtowerClient) TerminateSession(_ context.Context,
|
||||||
|
req *TerminateSessionRequest) (*TerminateSessionResponse, error) {
|
||||||
|
|
||||||
|
if err := c.isActive(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey, err := btcec.ParsePubKey(req.SessionId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionID := wtdb.NewSessionIDFromPubKey(pubKey)
|
||||||
|
|
||||||
|
err = c.cfg.ClientMgr.TerminateSession(sessionID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TerminateSessionResponse{
|
||||||
|
Status: fmt.Sprintf("Successful termination of session: %s",
|
||||||
|
sessionID),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ListTowers returns the list of watchtowers registered with the client.
|
// ListTowers returns the list of watchtowers registered with the client.
|
||||||
func (c *WatchtowerClient) ListTowers(ctx context.Context,
|
func (c *WatchtowerClient) ListTowers(ctx context.Context,
|
||||||
req *ListTowersRequest) (*ListTowersResponse, error) {
|
req *ListTowersRequest) (*ListTowersResponse, error) {
|
||||||
@@ -489,6 +550,7 @@ func marshallTower(tower *wtclient.RegisteredTower, policyType PolicyType,
|
|||||||
for _, session := range sessions {
|
for _, session := range sessions {
|
||||||
satPerVByte := session.Policy.SweepFeeRate.FeePerVByte()
|
satPerVByte := session.Policy.SweepFeeRate.FeePerVByte()
|
||||||
rpcSessions = append(rpcSessions, &TowerSession{
|
rpcSessions = append(rpcSessions, &TowerSession{
|
||||||
|
Id: session.ID[:],
|
||||||
NumBackups: uint32(ackCounts[session.ID]),
|
NumBackups: uint32(ackCounts[session.ID]),
|
||||||
NumPendingBackups: uint32(pendingCounts[session.ID]),
|
NumPendingBackups: uint32(pendingCounts[session.ID]),
|
||||||
MaxBackups: uint32(session.Policy.MaxUpdates),
|
MaxBackups: uint32(session.Policy.MaxUpdates),
|
||||||
|
|||||||
+490
-207
@@ -264,6 +264,198 @@ func (*RemoveTowerResponse) Descriptor() ([]byte, []int) {
|
|||||||
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{3}
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{3}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeactivateTowerRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// The identifying public key of the watchtower to deactivate.
|
||||||
|
Pubkey []byte `protobuf:"bytes,1,opt,name=pubkey,proto3" json:"pubkey,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeactivateTowerRequest) Reset() {
|
||||||
|
*x = DeactivateTowerRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[4]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeactivateTowerRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*DeactivateTowerRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *DeactivateTowerRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[4]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use DeactivateTowerRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*DeactivateTowerRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeactivateTowerRequest) GetPubkey() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Pubkey
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeactivateTowerResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// A string describing the action that took place.
|
||||||
|
Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeactivateTowerResponse) Reset() {
|
||||||
|
*x = DeactivateTowerResponse{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[5]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeactivateTowerResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*DeactivateTowerResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *DeactivateTowerResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[5]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use DeactivateTowerResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*DeactivateTowerResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeactivateTowerResponse) GetStatus() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Status
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type TerminateSessionRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// The ID of the session that should be terminated.
|
||||||
|
SessionId []byte `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *TerminateSessionRequest) Reset() {
|
||||||
|
*x = TerminateSessionRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[6]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *TerminateSessionRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*TerminateSessionRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *TerminateSessionRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[6]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use TerminateSessionRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*TerminateSessionRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *TerminateSessionRequest) GetSessionId() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.SessionId
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TerminateSessionResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// A string describing the action that took place.
|
||||||
|
Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *TerminateSessionResponse) Reset() {
|
||||||
|
*x = TerminateSessionResponse{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[7]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *TerminateSessionResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*TerminateSessionResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *TerminateSessionResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[7]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use TerminateSessionResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*TerminateSessionResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{7}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *TerminateSessionResponse) GetStatus() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Status
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type GetTowerInfoRequest struct {
|
type GetTowerInfoRequest struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -281,7 +473,7 @@ type GetTowerInfoRequest struct {
|
|||||||
func (x *GetTowerInfoRequest) Reset() {
|
func (x *GetTowerInfoRequest) Reset() {
|
||||||
*x = GetTowerInfoRequest{}
|
*x = GetTowerInfoRequest{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[4]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[8]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -294,7 +486,7 @@ func (x *GetTowerInfoRequest) String() string {
|
|||||||
func (*GetTowerInfoRequest) ProtoMessage() {}
|
func (*GetTowerInfoRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *GetTowerInfoRequest) ProtoReflect() protoreflect.Message {
|
func (x *GetTowerInfoRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[4]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[8]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -307,7 +499,7 @@ func (x *GetTowerInfoRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use GetTowerInfoRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use GetTowerInfoRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*GetTowerInfoRequest) Descriptor() ([]byte, []int) {
|
func (*GetTowerInfoRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{4}
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{8}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *GetTowerInfoRequest) GetPubkey() []byte {
|
func (x *GetTowerInfoRequest) GetPubkey() []byte {
|
||||||
@@ -353,12 +545,14 @@ type TowerSession struct {
|
|||||||
// The fee rate, in satoshis per vbyte, that will be used by the watchtower for
|
// The fee rate, in satoshis per vbyte, that will be used by the watchtower for
|
||||||
// the justice transaction in the event of a channel breach.
|
// the justice transaction in the event of a channel breach.
|
||||||
SweepSatPerVbyte uint32 `protobuf:"varint,5,opt,name=sweep_sat_per_vbyte,json=sweepSatPerVbyte,proto3" json:"sweep_sat_per_vbyte,omitempty"`
|
SweepSatPerVbyte uint32 `protobuf:"varint,5,opt,name=sweep_sat_per_vbyte,json=sweepSatPerVbyte,proto3" json:"sweep_sat_per_vbyte,omitempty"`
|
||||||
|
// The ID of the session.
|
||||||
|
Id []byte `protobuf:"bytes,6,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *TowerSession) Reset() {
|
func (x *TowerSession) Reset() {
|
||||||
*x = TowerSession{}
|
*x = TowerSession{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[5]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[9]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -371,7 +565,7 @@ func (x *TowerSession) String() string {
|
|||||||
func (*TowerSession) ProtoMessage() {}
|
func (*TowerSession) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *TowerSession) ProtoReflect() protoreflect.Message {
|
func (x *TowerSession) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[5]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[9]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -384,7 +578,7 @@ func (x *TowerSession) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use TowerSession.ProtoReflect.Descriptor instead.
|
// Deprecated: Use TowerSession.ProtoReflect.Descriptor instead.
|
||||||
func (*TowerSession) Descriptor() ([]byte, []int) {
|
func (*TowerSession) Descriptor() ([]byte, []int) {
|
||||||
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{5}
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{9}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *TowerSession) GetNumBackups() uint32 {
|
func (x *TowerSession) GetNumBackups() uint32 {
|
||||||
@@ -423,6 +617,13 @@ func (x *TowerSession) GetSweepSatPerVbyte() uint32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *TowerSession) GetId() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Id
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Tower struct {
|
type Tower struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -457,7 +658,7 @@ type Tower struct {
|
|||||||
func (x *Tower) Reset() {
|
func (x *Tower) Reset() {
|
||||||
*x = Tower{}
|
*x = Tower{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[6]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[10]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -470,7 +671,7 @@ func (x *Tower) String() string {
|
|||||||
func (*Tower) ProtoMessage() {}
|
func (*Tower) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Tower) ProtoReflect() protoreflect.Message {
|
func (x *Tower) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[6]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[10]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -483,7 +684,7 @@ func (x *Tower) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use Tower.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Tower.ProtoReflect.Descriptor instead.
|
||||||
func (*Tower) Descriptor() ([]byte, []int) {
|
func (*Tower) Descriptor() ([]byte, []int) {
|
||||||
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{6}
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{10}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Tower) GetPubkey() []byte {
|
func (x *Tower) GetPubkey() []byte {
|
||||||
@@ -549,7 +750,7 @@ type TowerSessionInfo struct {
|
|||||||
func (x *TowerSessionInfo) Reset() {
|
func (x *TowerSessionInfo) Reset() {
|
||||||
*x = TowerSessionInfo{}
|
*x = TowerSessionInfo{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[7]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[11]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -562,7 +763,7 @@ func (x *TowerSessionInfo) String() string {
|
|||||||
func (*TowerSessionInfo) ProtoMessage() {}
|
func (*TowerSessionInfo) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *TowerSessionInfo) ProtoReflect() protoreflect.Message {
|
func (x *TowerSessionInfo) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[7]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[11]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -575,7 +776,7 @@ func (x *TowerSessionInfo) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use TowerSessionInfo.ProtoReflect.Descriptor instead.
|
// Deprecated: Use TowerSessionInfo.ProtoReflect.Descriptor instead.
|
||||||
func (*TowerSessionInfo) Descriptor() ([]byte, []int) {
|
func (*TowerSessionInfo) Descriptor() ([]byte, []int) {
|
||||||
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{7}
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{11}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *TowerSessionInfo) GetActiveSessionCandidate() bool {
|
func (x *TowerSessionInfo) GetActiveSessionCandidate() bool {
|
||||||
@@ -621,7 +822,7 @@ type ListTowersRequest struct {
|
|||||||
func (x *ListTowersRequest) Reset() {
|
func (x *ListTowersRequest) Reset() {
|
||||||
*x = ListTowersRequest{}
|
*x = ListTowersRequest{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[8]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[12]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -634,7 +835,7 @@ func (x *ListTowersRequest) String() string {
|
|||||||
func (*ListTowersRequest) ProtoMessage() {}
|
func (*ListTowersRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *ListTowersRequest) ProtoReflect() protoreflect.Message {
|
func (x *ListTowersRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[8]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[12]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -647,7 +848,7 @@ func (x *ListTowersRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use ListTowersRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ListTowersRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*ListTowersRequest) Descriptor() ([]byte, []int) {
|
func (*ListTowersRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{8}
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{12}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ListTowersRequest) GetIncludeSessions() bool {
|
func (x *ListTowersRequest) GetIncludeSessions() bool {
|
||||||
@@ -676,7 +877,7 @@ type ListTowersResponse struct {
|
|||||||
func (x *ListTowersResponse) Reset() {
|
func (x *ListTowersResponse) Reset() {
|
||||||
*x = ListTowersResponse{}
|
*x = ListTowersResponse{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[9]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[13]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -689,7 +890,7 @@ func (x *ListTowersResponse) String() string {
|
|||||||
func (*ListTowersResponse) ProtoMessage() {}
|
func (*ListTowersResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *ListTowersResponse) ProtoReflect() protoreflect.Message {
|
func (x *ListTowersResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[9]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[13]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -702,7 +903,7 @@ func (x *ListTowersResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use ListTowersResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ListTowersResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*ListTowersResponse) Descriptor() ([]byte, []int) {
|
func (*ListTowersResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{9}
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{13}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ListTowersResponse) GetTowers() []*Tower {
|
func (x *ListTowersResponse) GetTowers() []*Tower {
|
||||||
@@ -721,7 +922,7 @@ type StatsRequest struct {
|
|||||||
func (x *StatsRequest) Reset() {
|
func (x *StatsRequest) Reset() {
|
||||||
*x = StatsRequest{}
|
*x = StatsRequest{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[10]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[14]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -734,7 +935,7 @@ func (x *StatsRequest) String() string {
|
|||||||
func (*StatsRequest) ProtoMessage() {}
|
func (*StatsRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *StatsRequest) ProtoReflect() protoreflect.Message {
|
func (x *StatsRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[10]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[14]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -747,7 +948,7 @@ func (x *StatsRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use StatsRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use StatsRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*StatsRequest) Descriptor() ([]byte, []int) {
|
func (*StatsRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{10}
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{14}
|
||||||
}
|
}
|
||||||
|
|
||||||
type StatsResponse struct {
|
type StatsResponse struct {
|
||||||
@@ -773,7 +974,7 @@ type StatsResponse struct {
|
|||||||
func (x *StatsResponse) Reset() {
|
func (x *StatsResponse) Reset() {
|
||||||
*x = StatsResponse{}
|
*x = StatsResponse{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[11]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[15]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -786,7 +987,7 @@ func (x *StatsResponse) String() string {
|
|||||||
func (*StatsResponse) ProtoMessage() {}
|
func (*StatsResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *StatsResponse) ProtoReflect() protoreflect.Message {
|
func (x *StatsResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[11]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[15]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -799,7 +1000,7 @@ func (x *StatsResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use StatsResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use StatsResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*StatsResponse) Descriptor() ([]byte, []int) {
|
func (*StatsResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{11}
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{15}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *StatsResponse) GetNumBackups() uint32 {
|
func (x *StatsResponse) GetNumBackups() uint32 {
|
||||||
@@ -849,7 +1050,7 @@ type PolicyRequest struct {
|
|||||||
func (x *PolicyRequest) Reset() {
|
func (x *PolicyRequest) Reset() {
|
||||||
*x = PolicyRequest{}
|
*x = PolicyRequest{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[12]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[16]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -862,7 +1063,7 @@ func (x *PolicyRequest) String() string {
|
|||||||
func (*PolicyRequest) ProtoMessage() {}
|
func (*PolicyRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *PolicyRequest) ProtoReflect() protoreflect.Message {
|
func (x *PolicyRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[12]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[16]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -875,7 +1076,7 @@ func (x *PolicyRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use PolicyRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use PolicyRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*PolicyRequest) Descriptor() ([]byte, []int) {
|
func (*PolicyRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{12}
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{16}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PolicyRequest) GetPolicyType() PolicyType {
|
func (x *PolicyRequest) GetPolicyType() PolicyType {
|
||||||
@@ -907,7 +1108,7 @@ type PolicyResponse struct {
|
|||||||
func (x *PolicyResponse) Reset() {
|
func (x *PolicyResponse) Reset() {
|
||||||
*x = PolicyResponse{}
|
*x = PolicyResponse{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[13]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[17]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -920,7 +1121,7 @@ func (x *PolicyResponse) String() string {
|
|||||||
func (*PolicyResponse) ProtoMessage() {}
|
func (*PolicyResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *PolicyResponse) ProtoReflect() protoreflect.Message {
|
func (x *PolicyResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_wtclientrpc_wtclient_proto_msgTypes[13]
|
mi := &file_wtclientrpc_wtclient_proto_msgTypes[17]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -933,7 +1134,7 @@ func (x *PolicyResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use PolicyResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use PolicyResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*PolicyResponse) Descriptor() ([]byte, []int) {
|
func (*PolicyResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{13}
|
return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{17}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PolicyResponse) GetMaxUpdates() uint32 {
|
func (x *PolicyResponse) GetMaxUpdates() uint32 {
|
||||||
@@ -975,141 +1176,167 @@ var file_wtclientrpc_wtclient_proto_rawDesc = []byte{
|
|||||||
0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65,
|
0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65,
|
||||||
0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||||
0x65, 0x22, 0x96, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x49, 0x6e,
|
0x65, 0x22, 0x30, 0x0a, 0x16, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x54,
|
||||||
0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62,
|
0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70,
|
||||||
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65,
|
0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62,
|
||||||
0x79, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x73,
|
0x6b, 0x65, 0x79, 0x22, 0x31, 0x0a, 0x17, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
|
||||||
0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x63,
|
0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16,
|
||||||
0x6c, 0x75, 0x64, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x1a,
|
0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
|
||||||
0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x65, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65,
|
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x38, 0x0a, 0x17, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e,
|
||||||
0x64, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
|
0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
0x52, 0x18, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x45, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74,
|
0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18,
|
||||||
0x65, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xe0, 0x01, 0x0a, 0x0c, 0x54,
|
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64,
|
||||||
0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6e,
|
0x22, 0x32, 0x0a, 0x18, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73,
|
||||||
0x75, 0x6d, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
|
0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06,
|
||||||
0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x13,
|
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74,
|
||||||
0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x61, 0x63, 0x6b,
|
0x61, 0x74, 0x75, 0x73, 0x22, 0x96, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x77, 0x65,
|
||||||
0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x6e, 0x75, 0x6d, 0x50, 0x65,
|
0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06,
|
||||||
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1f, 0x0a, 0x0b,
|
0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75,
|
||||||
0x6d, 0x61, 0x78, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
|
0x62, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f,
|
||||||
0x0d, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2f, 0x0a,
|
0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f,
|
||||||
0x12, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x62,
|
0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12,
|
||||||
0x79, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0f, 0x73,
|
0x3c, 0x0a, 0x1a, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x65, 0x78, 0x68, 0x61, 0x75,
|
||||||
0x77, 0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x12, 0x2d,
|
0x73, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20,
|
||||||
0x0a, 0x13, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f,
|
0x01, 0x28, 0x08, 0x52, 0x18, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x45, 0x78, 0x68, 0x61,
|
||||||
0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x73, 0x77, 0x65,
|
0x75, 0x73, 0x74, 0x65, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xf0, 0x01,
|
||||||
0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x22, 0x9f, 0x02,
|
0x0a, 0x0c, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f,
|
||||||
0x0a, 0x05, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65,
|
0x0a, 0x0b, 0x6e, 0x75, 0x6d, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20,
|
||||||
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12,
|
0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12,
|
||||||
0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,
|
0x2e, 0x0a, 0x13, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x62,
|
||||||
0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x3c, 0x0a,
|
0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x6e, 0x75,
|
||||||
0x18, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
|
0x6d, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12,
|
||||||
0x63, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x42,
|
0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x03,
|
||||||
0x02, 0x18, 0x01, 0x52, 0x16, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69,
|
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73,
|
||||||
0x6f, 0x6e, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0c, 0x6e,
|
0x12, 0x2f, 0x0a, 0x12, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65,
|
||||||
0x75, 0x6d, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
|
0x72, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01,
|
||||||
0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
|
0x52, 0x0f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x42, 0x79, 0x74,
|
||||||
0x6e, 0x73, 0x12, 0x39, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05,
|
0x65, 0x12, 0x2d, 0x0a, 0x13, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70,
|
||||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72,
|
0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10,
|
||||||
0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x42,
|
0x73, 0x77, 0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65,
|
||||||
0x02, 0x18, 0x01, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x40, 0x0a,
|
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64,
|
||||||
0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20,
|
0x22, 0x9f, 0x02, 0x0a, 0x05, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75,
|
||||||
0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70,
|
0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b,
|
||||||
0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e,
|
0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18,
|
||||||
0x66, 0x6f, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x22,
|
0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73,
|
||||||
0xe0, 0x01, 0x0a, 0x10, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
|
0x12, 0x3c, 0x0a, 0x18, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69,
|
||||||
0x49, 0x6e, 0x66, 0x6f, 0x12, 0x38, 0x0a, 0x18, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x73,
|
0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01,
|
||||||
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65,
|
0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x16, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, 0x65,
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, 0x65,
|
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x12, 0x25,
|
||||||
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x12, 0x21,
|
0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04,
|
||||||
0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02,
|
0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73,
|
||||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
|
0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x39, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
|
||||||
0x73, 0x12, 0x35, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20,
|
0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65,
|
||||||
0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70,
|
0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69,
|
||||||
0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08,
|
0x6f, 0x6e, 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
|
||||||
0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69,
|
0x12, 0x40, 0x0a, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f,
|
||||||
0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e,
|
0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||||
0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69,
|
0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
|
||||||
0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79,
|
0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e,
|
||||||
0x70, 0x65, 0x22, 0x7c, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73,
|
0x66, 0x6f, 0x22, 0xe0, 0x01, 0x0a, 0x10, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73,
|
||||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75,
|
0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x38, 0x0a, 0x18, 0x61, 0x63, 0x74, 0x69, 0x76,
|
||||||
0x64, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x64, 0x69, 0x64,
|
||||||
0x08, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
|
0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x61, 0x63, 0x74, 0x69, 0x76,
|
||||||
0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x1a, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x65, 0x78,
|
0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74,
|
||||||
0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
|
0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x45,
|
0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73,
|
||||||
0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
|
0x69, 0x6f, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
|
||||||
0x22, 0x40, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x52, 0x65,
|
0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x73,
|
0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
|
||||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
0x6e, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x70,
|
||||||
0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x06, 0x74, 0x6f, 0x77, 0x65,
|
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e,
|
||||||
0x72, 0x73, 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
|
0x32, 0x17, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50,
|
||||||
0x73, 0x74, 0x22, 0xf8, 0x01, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
|
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63,
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x75, 0x6d, 0x5f, 0x62, 0x61, 0x63, 0x6b,
|
0x79, 0x54, 0x79, 0x70, 0x65, 0x22, 0x7c, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77,
|
||||||
0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x42, 0x61,
|
0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e,
|
||||||
0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x65, 0x6e,
|
0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01,
|
||||||
0x64, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01,
|
0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x53, 0x65, 0x73,
|
||||||
0x28, 0x0d, 0x52, 0x11, 0x6e, 0x75, 0x6d, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x61,
|
0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x1a, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65,
|
||||||
0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x75, 0x6d, 0x5f, 0x66, 0x61, 0x69,
|
0x5f, 0x65, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69,
|
||||||
0x6c, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
|
0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x65, 0x78, 0x63, 0x6c, 0x75,
|
||||||
0x0d, 0x52, 0x10, 0x6e, 0x75, 0x6d, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x42, 0x61, 0x63, 0x6b,
|
0x64, 0x65, 0x45, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69,
|
||||||
0x75, 0x70, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69,
|
0x6f, 0x6e, 0x73, 0x22, 0x40, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72,
|
||||||
0x6f, 0x6e, 0x73, 0x5f, 0x61, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01,
|
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x74, 0x6f, 0x77,
|
||||||
0x28, 0x0d, 0x52, 0x13, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x41,
|
0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x77, 0x74, 0x63, 0x6c,
|
||||||
0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x6e, 0x75, 0x6d, 0x5f, 0x73,
|
0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x06, 0x74,
|
||||||
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x65, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65,
|
0x6f, 0x77, 0x65, 0x72, 0x73, 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
|
||||||
0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73,
|
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xf8, 0x01, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52,
|
||||||
0x69, 0x6f, 0x6e, 0x73, 0x45, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64, 0x22, 0x49, 0x0a,
|
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x75, 0x6d, 0x5f, 0x62,
|
||||||
0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38,
|
0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75,
|
||||||
0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20,
|
0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x6e, 0x75, 0x6d, 0x5f,
|
||||||
0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70,
|
0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18,
|
||||||
0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x70, 0x6f,
|
0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x6e, 0x75, 0x6d, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e,
|
||||||
0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x22, 0x91, 0x01, 0x0a, 0x0e, 0x50, 0x6f, 0x6c,
|
0x67, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x75, 0x6d, 0x5f,
|
||||||
0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d,
|
0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x03,
|
||||||
0x61, 0x78, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
|
0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6e, 0x75, 0x6d, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x42,
|
||||||
0x52, 0x0a, 0x6d, 0x61, 0x78, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x12,
|
0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65,
|
||||||
0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x62, 0x79,
|
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x61, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18,
|
||||||
0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0f, 0x73, 0x77,
|
0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
|
||||||
0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x12, 0x2d, 0x0a,
|
0x6e, 0x73, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x6e, 0x75,
|
||||||
0x13, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76,
|
0x6d, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x65, 0x78, 0x68, 0x61, 0x75,
|
||||||
0x62, 0x79, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x73, 0x77, 0x65, 0x65,
|
0x73, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x6e, 0x75, 0x6d, 0x53,
|
||||||
0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x2a, 0x31, 0x0a, 0x0a,
|
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64,
|
||||||
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x45,
|
0x22, 0x49, 0x0a, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
0x47, 0x41, 0x43, 0x59, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52,
|
0x74, 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65,
|
||||||
0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x02, 0x32,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||||
0xc5, 0x03, 0x0a, 0x10, 0x57, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x43, 0x6c,
|
0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52,
|
||||||
0x69, 0x65, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72,
|
0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x22, 0x91, 0x01, 0x0a, 0x0e,
|
||||||
0x12, 0x1c, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41,
|
0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f,
|
||||||
0x64, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d,
|
0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20,
|
||||||
0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64,
|
0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12,
|
||||||
0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a,
|
0x2f, 0x0a, 0x12, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72,
|
||||||
0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x77,
|
0x5f, 0x62, 0x79, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52,
|
||||||
0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76,
|
0x0f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65,
|
||||||
0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e,
|
0x12, 0x2d, 0x0a, 0x13, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65,
|
||||||
0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f,
|
0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x73,
|
||||||
0x76, 0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
0x77, 0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x2a,
|
||||||
0x4d, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e,
|
0x31, 0x0a, 0x0a, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a,
|
||||||
0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74,
|
0x06, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43,
|
||||||
0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e,
|
0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54,
|
||||||
0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74,
|
0x10, 0x02, 0x32, 0x84, 0x05, 0x0a, 0x10, 0x57, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65,
|
||||||
0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44,
|
0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x54, 0x6f,
|
||||||
0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20,
|
0x77, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70,
|
||||||
0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74,
|
0x63, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
0x54, 0x6f, 0x77, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
0x74, 0x1a, 0x1d, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e,
|
||||||
0x1a, 0x12, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54,
|
0x41, 0x64, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
0x6f, 0x77, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x19, 0x2e,
|
0x12, 0x50, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x12,
|
||||||
0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74,
|
0x1f, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65,
|
||||||
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69,
|
0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
|
0x1a, 0x20, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52,
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1a,
|
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||||
0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c,
|
0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0f, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65,
|
||||||
0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x77, 0x74, 0x63,
|
0x54, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x23, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
||||||
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52,
|
0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x54, 0x6f,
|
||||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75,
|
0x77, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x77, 0x74, 0x63,
|
||||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e,
|
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76,
|
||||||
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63,
|
0x61, 0x74, 0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
0x2f, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72,
|
0x12, 0x5f, 0x0a, 0x10, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73,
|
||||||
0x6f, 0x74, 0x6f, 0x33,
|
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72,
|
||||||
|
0x70, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73,
|
||||||
|
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x77, 0x74, 0x63,
|
||||||
|
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61,
|
||||||
|
0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||||
|
0x65, 0x12, 0x4d, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x12,
|
||||||
|
0x1e, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69,
|
||||||
|
0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
|
0x1f, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69,
|
||||||
|
0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
|
0x12, 0x44, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f,
|
||||||
|
0x12, 0x20, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47,
|
||||||
|
0x65, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
|
0x73, 0x74, 0x1a, 0x12, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63,
|
||||||
|
0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12,
|
||||||
|
0x19, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74,
|
||||||
|
0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x77, 0x74, 0x63,
|
||||||
|
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
|
||||||
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
|
||||||
|
0x12, 0x1a, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50,
|
||||||
|
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x77,
|
||||||
|
0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63,
|
||||||
|
0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74,
|
||||||
|
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e,
|
||||||
|
0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72,
|
||||||
|
0x70, 0x63, 0x2f, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -1125,45 +1352,53 @@ func file_wtclientrpc_wtclient_proto_rawDescGZIP() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var file_wtclientrpc_wtclient_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
var file_wtclientrpc_wtclient_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
var file_wtclientrpc_wtclient_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
|
var file_wtclientrpc_wtclient_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
|
||||||
var file_wtclientrpc_wtclient_proto_goTypes = []interface{}{
|
var file_wtclientrpc_wtclient_proto_goTypes = []interface{}{
|
||||||
(PolicyType)(0), // 0: wtclientrpc.PolicyType
|
(PolicyType)(0), // 0: wtclientrpc.PolicyType
|
||||||
(*AddTowerRequest)(nil), // 1: wtclientrpc.AddTowerRequest
|
(*AddTowerRequest)(nil), // 1: wtclientrpc.AddTowerRequest
|
||||||
(*AddTowerResponse)(nil), // 2: wtclientrpc.AddTowerResponse
|
(*AddTowerResponse)(nil), // 2: wtclientrpc.AddTowerResponse
|
||||||
(*RemoveTowerRequest)(nil), // 3: wtclientrpc.RemoveTowerRequest
|
(*RemoveTowerRequest)(nil), // 3: wtclientrpc.RemoveTowerRequest
|
||||||
(*RemoveTowerResponse)(nil), // 4: wtclientrpc.RemoveTowerResponse
|
(*RemoveTowerResponse)(nil), // 4: wtclientrpc.RemoveTowerResponse
|
||||||
(*GetTowerInfoRequest)(nil), // 5: wtclientrpc.GetTowerInfoRequest
|
(*DeactivateTowerRequest)(nil), // 5: wtclientrpc.DeactivateTowerRequest
|
||||||
(*TowerSession)(nil), // 6: wtclientrpc.TowerSession
|
(*DeactivateTowerResponse)(nil), // 6: wtclientrpc.DeactivateTowerResponse
|
||||||
(*Tower)(nil), // 7: wtclientrpc.Tower
|
(*TerminateSessionRequest)(nil), // 7: wtclientrpc.TerminateSessionRequest
|
||||||
(*TowerSessionInfo)(nil), // 8: wtclientrpc.TowerSessionInfo
|
(*TerminateSessionResponse)(nil), // 8: wtclientrpc.TerminateSessionResponse
|
||||||
(*ListTowersRequest)(nil), // 9: wtclientrpc.ListTowersRequest
|
(*GetTowerInfoRequest)(nil), // 9: wtclientrpc.GetTowerInfoRequest
|
||||||
(*ListTowersResponse)(nil), // 10: wtclientrpc.ListTowersResponse
|
(*TowerSession)(nil), // 10: wtclientrpc.TowerSession
|
||||||
(*StatsRequest)(nil), // 11: wtclientrpc.StatsRequest
|
(*Tower)(nil), // 11: wtclientrpc.Tower
|
||||||
(*StatsResponse)(nil), // 12: wtclientrpc.StatsResponse
|
(*TowerSessionInfo)(nil), // 12: wtclientrpc.TowerSessionInfo
|
||||||
(*PolicyRequest)(nil), // 13: wtclientrpc.PolicyRequest
|
(*ListTowersRequest)(nil), // 13: wtclientrpc.ListTowersRequest
|
||||||
(*PolicyResponse)(nil), // 14: wtclientrpc.PolicyResponse
|
(*ListTowersResponse)(nil), // 14: wtclientrpc.ListTowersResponse
|
||||||
|
(*StatsRequest)(nil), // 15: wtclientrpc.StatsRequest
|
||||||
|
(*StatsResponse)(nil), // 16: wtclientrpc.StatsResponse
|
||||||
|
(*PolicyRequest)(nil), // 17: wtclientrpc.PolicyRequest
|
||||||
|
(*PolicyResponse)(nil), // 18: wtclientrpc.PolicyResponse
|
||||||
}
|
}
|
||||||
var file_wtclientrpc_wtclient_proto_depIdxs = []int32{
|
var file_wtclientrpc_wtclient_proto_depIdxs = []int32{
|
||||||
6, // 0: wtclientrpc.Tower.sessions:type_name -> wtclientrpc.TowerSession
|
10, // 0: wtclientrpc.Tower.sessions:type_name -> wtclientrpc.TowerSession
|
||||||
8, // 1: wtclientrpc.Tower.session_info:type_name -> wtclientrpc.TowerSessionInfo
|
12, // 1: wtclientrpc.Tower.session_info:type_name -> wtclientrpc.TowerSessionInfo
|
||||||
6, // 2: wtclientrpc.TowerSessionInfo.sessions:type_name -> wtclientrpc.TowerSession
|
10, // 2: wtclientrpc.TowerSessionInfo.sessions:type_name -> wtclientrpc.TowerSession
|
||||||
0, // 3: wtclientrpc.TowerSessionInfo.policy_type:type_name -> wtclientrpc.PolicyType
|
0, // 3: wtclientrpc.TowerSessionInfo.policy_type:type_name -> wtclientrpc.PolicyType
|
||||||
7, // 4: wtclientrpc.ListTowersResponse.towers:type_name -> wtclientrpc.Tower
|
11, // 4: wtclientrpc.ListTowersResponse.towers:type_name -> wtclientrpc.Tower
|
||||||
0, // 5: wtclientrpc.PolicyRequest.policy_type:type_name -> wtclientrpc.PolicyType
|
0, // 5: wtclientrpc.PolicyRequest.policy_type:type_name -> wtclientrpc.PolicyType
|
||||||
1, // 6: wtclientrpc.WatchtowerClient.AddTower:input_type -> wtclientrpc.AddTowerRequest
|
1, // 6: wtclientrpc.WatchtowerClient.AddTower:input_type -> wtclientrpc.AddTowerRequest
|
||||||
3, // 7: wtclientrpc.WatchtowerClient.RemoveTower:input_type -> wtclientrpc.RemoveTowerRequest
|
3, // 7: wtclientrpc.WatchtowerClient.RemoveTower:input_type -> wtclientrpc.RemoveTowerRequest
|
||||||
9, // 8: wtclientrpc.WatchtowerClient.ListTowers:input_type -> wtclientrpc.ListTowersRequest
|
5, // 8: wtclientrpc.WatchtowerClient.DeactivateTower:input_type -> wtclientrpc.DeactivateTowerRequest
|
||||||
5, // 9: wtclientrpc.WatchtowerClient.GetTowerInfo:input_type -> wtclientrpc.GetTowerInfoRequest
|
7, // 9: wtclientrpc.WatchtowerClient.TerminateSession:input_type -> wtclientrpc.TerminateSessionRequest
|
||||||
11, // 10: wtclientrpc.WatchtowerClient.Stats:input_type -> wtclientrpc.StatsRequest
|
13, // 10: wtclientrpc.WatchtowerClient.ListTowers:input_type -> wtclientrpc.ListTowersRequest
|
||||||
13, // 11: wtclientrpc.WatchtowerClient.Policy:input_type -> wtclientrpc.PolicyRequest
|
9, // 11: wtclientrpc.WatchtowerClient.GetTowerInfo:input_type -> wtclientrpc.GetTowerInfoRequest
|
||||||
2, // 12: wtclientrpc.WatchtowerClient.AddTower:output_type -> wtclientrpc.AddTowerResponse
|
15, // 12: wtclientrpc.WatchtowerClient.Stats:input_type -> wtclientrpc.StatsRequest
|
||||||
4, // 13: wtclientrpc.WatchtowerClient.RemoveTower:output_type -> wtclientrpc.RemoveTowerResponse
|
17, // 13: wtclientrpc.WatchtowerClient.Policy:input_type -> wtclientrpc.PolicyRequest
|
||||||
10, // 14: wtclientrpc.WatchtowerClient.ListTowers:output_type -> wtclientrpc.ListTowersResponse
|
2, // 14: wtclientrpc.WatchtowerClient.AddTower:output_type -> wtclientrpc.AddTowerResponse
|
||||||
7, // 15: wtclientrpc.WatchtowerClient.GetTowerInfo:output_type -> wtclientrpc.Tower
|
4, // 15: wtclientrpc.WatchtowerClient.RemoveTower:output_type -> wtclientrpc.RemoveTowerResponse
|
||||||
12, // 16: wtclientrpc.WatchtowerClient.Stats:output_type -> wtclientrpc.StatsResponse
|
6, // 16: wtclientrpc.WatchtowerClient.DeactivateTower:output_type -> wtclientrpc.DeactivateTowerResponse
|
||||||
14, // 17: wtclientrpc.WatchtowerClient.Policy:output_type -> wtclientrpc.PolicyResponse
|
8, // 17: wtclientrpc.WatchtowerClient.TerminateSession:output_type -> wtclientrpc.TerminateSessionResponse
|
||||||
12, // [12:18] is the sub-list for method output_type
|
14, // 18: wtclientrpc.WatchtowerClient.ListTowers:output_type -> wtclientrpc.ListTowersResponse
|
||||||
6, // [6:12] is the sub-list for method input_type
|
11, // 19: wtclientrpc.WatchtowerClient.GetTowerInfo:output_type -> wtclientrpc.Tower
|
||||||
|
16, // 20: wtclientrpc.WatchtowerClient.Stats:output_type -> wtclientrpc.StatsResponse
|
||||||
|
18, // 21: wtclientrpc.WatchtowerClient.Policy:output_type -> wtclientrpc.PolicyResponse
|
||||||
|
14, // [14:22] is the sub-list for method output_type
|
||||||
|
6, // [6:14] is the sub-list for method input_type
|
||||||
6, // [6:6] is the sub-list for extension type_name
|
6, // [6:6] is the sub-list for extension type_name
|
||||||
6, // [6:6] is the sub-list for extension extendee
|
6, // [6:6] is the sub-list for extension extendee
|
||||||
0, // [0:6] is the sub-list for field type_name
|
0, // [0:6] is the sub-list for field type_name
|
||||||
@@ -1224,7 +1459,7 @@ func file_wtclientrpc_wtclient_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_wtclientrpc_wtclient_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
file_wtclientrpc_wtclient_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*GetTowerInfoRequest); i {
|
switch v := v.(*DeactivateTowerRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1236,7 +1471,7 @@ func file_wtclientrpc_wtclient_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_wtclientrpc_wtclient_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
file_wtclientrpc_wtclient_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*TowerSession); i {
|
switch v := v.(*DeactivateTowerResponse); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1248,7 +1483,7 @@ func file_wtclientrpc_wtclient_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_wtclientrpc_wtclient_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
file_wtclientrpc_wtclient_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Tower); i {
|
switch v := v.(*TerminateSessionRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1260,7 +1495,7 @@ func file_wtclientrpc_wtclient_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_wtclientrpc_wtclient_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
|
file_wtclientrpc_wtclient_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*TowerSessionInfo); i {
|
switch v := v.(*TerminateSessionResponse); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1272,7 +1507,7 @@ func file_wtclientrpc_wtclient_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_wtclientrpc_wtclient_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
file_wtclientrpc_wtclient_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*ListTowersRequest); i {
|
switch v := v.(*GetTowerInfoRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1284,7 +1519,7 @@ func file_wtclientrpc_wtclient_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_wtclientrpc_wtclient_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
file_wtclientrpc_wtclient_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*ListTowersResponse); i {
|
switch v := v.(*TowerSession); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1296,7 +1531,7 @@ func file_wtclientrpc_wtclient_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_wtclientrpc_wtclient_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
file_wtclientrpc_wtclient_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*StatsRequest); i {
|
switch v := v.(*Tower); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1308,7 +1543,7 @@ func file_wtclientrpc_wtclient_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_wtclientrpc_wtclient_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
file_wtclientrpc_wtclient_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*StatsResponse); i {
|
switch v := v.(*TowerSessionInfo); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1320,7 +1555,7 @@ func file_wtclientrpc_wtclient_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_wtclientrpc_wtclient_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
|
file_wtclientrpc_wtclient_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*PolicyRequest); i {
|
switch v := v.(*ListTowersRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1332,6 +1567,54 @@ func file_wtclientrpc_wtclient_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_wtclientrpc_wtclient_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
|
file_wtclientrpc_wtclient_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ListTowersResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_wtclientrpc_wtclient_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*StatsRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_wtclientrpc_wtclient_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*StatsResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_wtclientrpc_wtclient_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*PolicyRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_wtclientrpc_wtclient_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*PolicyResponse); i {
|
switch v := v.(*PolicyResponse); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -1350,7 +1633,7 @@ func file_wtclientrpc_wtclient_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_wtclientrpc_wtclient_proto_rawDesc,
|
RawDescriptor: file_wtclientrpc_wtclient_proto_rawDesc,
|
||||||
NumEnums: 1,
|
NumEnums: 1,
|
||||||
NumMessages: 14,
|
NumMessages: 18,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -135,6 +135,110 @@ func local_request_WatchtowerClient_RemoveTower_0(ctx context.Context, marshaler
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func request_WatchtowerClient_DeactivateTower_0(ctx context.Context, marshaler runtime.Marshaler, client WatchtowerClientClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq DeactivateTowerRequest
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
var (
|
||||||
|
val string
|
||||||
|
ok bool
|
||||||
|
err error
|
||||||
|
_ = err
|
||||||
|
)
|
||||||
|
|
||||||
|
val, ok = pathParams["pubkey"]
|
||||||
|
if !ok {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pubkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
protoReq.Pubkey, err = runtime.Bytes(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pubkey", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := client.DeactivateTower(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func local_request_WatchtowerClient_DeactivateTower_0(ctx context.Context, marshaler runtime.Marshaler, server WatchtowerClientServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq DeactivateTowerRequest
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
var (
|
||||||
|
val string
|
||||||
|
ok bool
|
||||||
|
err error
|
||||||
|
_ = err
|
||||||
|
)
|
||||||
|
|
||||||
|
val, ok = pathParams["pubkey"]
|
||||||
|
if !ok {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pubkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
protoReq.Pubkey, err = runtime.Bytes(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pubkey", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := server.DeactivateTower(ctx, &protoReq)
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func request_WatchtowerClient_TerminateSession_0(ctx context.Context, marshaler runtime.Marshaler, client WatchtowerClientClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq TerminateSessionRequest
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
var (
|
||||||
|
val string
|
||||||
|
ok bool
|
||||||
|
err error
|
||||||
|
_ = err
|
||||||
|
)
|
||||||
|
|
||||||
|
val, ok = pathParams["session_id"]
|
||||||
|
if !ok {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "session_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
protoReq.SessionId, err = runtime.Bytes(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "session_id", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := client.TerminateSession(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func local_request_WatchtowerClient_TerminateSession_0(ctx context.Context, marshaler runtime.Marshaler, server WatchtowerClientServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq TerminateSessionRequest
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
var (
|
||||||
|
val string
|
||||||
|
ok bool
|
||||||
|
err error
|
||||||
|
_ = err
|
||||||
|
)
|
||||||
|
|
||||||
|
val, ok = pathParams["session_id"]
|
||||||
|
if !ok {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "session_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
protoReq.SessionId, err = runtime.Bytes(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "session_id", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := server.TerminateSession(ctx, &protoReq)
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
filter_WatchtowerClient_ListTowers_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
|
filter_WatchtowerClient_ListTowers_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
|
||||||
)
|
)
|
||||||
@@ -347,6 +451,52 @@ func RegisterWatchtowerClientHandlerServer(ctx context.Context, mux *runtime.Ser
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_WatchtowerClient_DeactivateTower_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
|
defer cancel()
|
||||||
|
var stream runtime.ServerTransportStream
|
||||||
|
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||||
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
|
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wtclientrpc.WatchtowerClient/DeactivateTower", runtime.WithHTTPPathPattern("/v2/watchtower/client/tower/deactivate/{pubkey}"))
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := local_request_WatchtowerClient_DeactivateTower_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||||
|
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||||
|
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_WatchtowerClient_DeactivateTower_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_WatchtowerClient_TerminateSession_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
|
defer cancel()
|
||||||
|
var stream runtime.ServerTransportStream
|
||||||
|
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||||
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
|
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/wtclientrpc.WatchtowerClient/TerminateSession", runtime.WithHTTPPathPattern("/v2/watchtower/client/sessions/terminate/{session_id}"))
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := local_request_WatchtowerClient_TerminateSession_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||||
|
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||||
|
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_WatchtowerClient_TerminateSession_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
mux.Handle("GET", pattern_WatchtowerClient_ListTowers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
mux.Handle("GET", pattern_WatchtowerClient_ListTowers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -520,6 +670,46 @@ func RegisterWatchtowerClientHandlerClient(ctx context.Context, mux *runtime.Ser
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_WatchtowerClient_DeactivateTower_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
|
defer cancel()
|
||||||
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
|
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wtclientrpc.WatchtowerClient/DeactivateTower", runtime.WithHTTPPathPattern("/v2/watchtower/client/tower/deactivate/{pubkey}"))
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := request_WatchtowerClient_DeactivateTower_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||||
|
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_WatchtowerClient_DeactivateTower_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_WatchtowerClient_TerminateSession_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
|
defer cancel()
|
||||||
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
|
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/wtclientrpc.WatchtowerClient/TerminateSession", runtime.WithHTTPPathPattern("/v2/watchtower/client/sessions/terminate/{session_id}"))
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := request_WatchtowerClient_TerminateSession_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||||
|
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_WatchtowerClient_TerminateSession_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
mux.Handle("GET", pattern_WatchtowerClient_ListTowers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
mux.Handle("GET", pattern_WatchtowerClient_ListTowers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -608,6 +798,10 @@ var (
|
|||||||
|
|
||||||
pattern_WatchtowerClient_RemoveTower_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v2", "watchtower", "client", "pubkey"}, ""))
|
pattern_WatchtowerClient_RemoveTower_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v2", "watchtower", "client", "pubkey"}, ""))
|
||||||
|
|
||||||
|
pattern_WatchtowerClient_DeactivateTower_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"v2", "watchtower", "client", "tower", "deactivate", "pubkey"}, ""))
|
||||||
|
|
||||||
|
pattern_WatchtowerClient_TerminateSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"v2", "watchtower", "client", "sessions", "terminate", "session_id"}, ""))
|
||||||
|
|
||||||
pattern_WatchtowerClient_ListTowers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "watchtower", "client"}, ""))
|
pattern_WatchtowerClient_ListTowers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "watchtower", "client"}, ""))
|
||||||
|
|
||||||
pattern_WatchtowerClient_GetTowerInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v2", "watchtower", "client", "info", "pubkey"}, ""))
|
pattern_WatchtowerClient_GetTowerInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v2", "watchtower", "client", "info", "pubkey"}, ""))
|
||||||
@@ -622,6 +816,10 @@ var (
|
|||||||
|
|
||||||
forward_WatchtowerClient_RemoveTower_0 = runtime.ForwardResponseMessage
|
forward_WatchtowerClient_RemoveTower_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
|
forward_WatchtowerClient_DeactivateTower_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
|
forward_WatchtowerClient_TerminateSession_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
forward_WatchtowerClient_ListTowers_0 = runtime.ForwardResponseMessage
|
forward_WatchtowerClient_ListTowers_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
forward_WatchtowerClient_GetTowerInfo_0 = runtime.ForwardResponseMessage
|
forward_WatchtowerClient_GetTowerInfo_0 = runtime.ForwardResponseMessage
|
||||||
|
|||||||
@@ -41,6 +41,21 @@ service WatchtowerClient {
|
|||||||
*/
|
*/
|
||||||
rpc RemoveTower (RemoveTowerRequest) returns (RemoveTowerResponse);
|
rpc RemoveTower (RemoveTowerRequest) returns (RemoveTowerResponse);
|
||||||
|
|
||||||
|
/* lncli: `wtclient deactivate`
|
||||||
|
DeactivateTower sets the given tower's status to inactive so that it
|
||||||
|
is not considered for session negotiation. Its sessions will also not
|
||||||
|
be used while the tower is inactive.
|
||||||
|
*/
|
||||||
|
rpc DeactivateTower (DeactivateTowerRequest)
|
||||||
|
returns (DeactivateTowerResponse);
|
||||||
|
|
||||||
|
/* lncli: `wtclient session terminate`
|
||||||
|
Terminate terminates the given session and marks it as terminal so that
|
||||||
|
it is not used for backups anymore.
|
||||||
|
*/
|
||||||
|
rpc TerminateSession (TerminateSessionRequest)
|
||||||
|
returns (TerminateSessionResponse);
|
||||||
|
|
||||||
/* lncli: `wtclient towers`
|
/* lncli: `wtclient towers`
|
||||||
ListTowers returns the list of watchtowers registered with the client.
|
ListTowers returns the list of watchtowers registered with the client.
|
||||||
*/
|
*/
|
||||||
@@ -88,6 +103,26 @@ message RemoveTowerRequest {
|
|||||||
message RemoveTowerResponse {
|
message RemoveTowerResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message DeactivateTowerRequest {
|
||||||
|
// The identifying public key of the watchtower to deactivate.
|
||||||
|
bytes pubkey = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeactivateTowerResponse {
|
||||||
|
// A string describing the action that took place.
|
||||||
|
string status = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TerminateSessionRequest {
|
||||||
|
// The ID of the session that should be terminated.
|
||||||
|
bytes session_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TerminateSessionResponse {
|
||||||
|
// A string describing the action that took place.
|
||||||
|
string status = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message GetTowerInfoRequest {
|
message GetTowerInfoRequest {
|
||||||
// The identifying public key of the watchtower to retrieve information for.
|
// The identifying public key of the watchtower to retrieve information for.
|
||||||
bytes pubkey = 1;
|
bytes pubkey = 1;
|
||||||
@@ -128,6 +163,11 @@ message TowerSession {
|
|||||||
the justice transaction in the event of a channel breach.
|
the justice transaction in the event of a channel breach.
|
||||||
*/
|
*/
|
||||||
uint32 sweep_sat_per_vbyte = 5;
|
uint32 sweep_sat_per_vbyte = 5;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The ID of the session.
|
||||||
|
*/
|
||||||
|
bytes id = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Tower {
|
message Tower {
|
||||||
|
|||||||
@@ -171,6 +171,39 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/v2/watchtower/client/sessions/terminate/{session_id}": {
|
||||||
|
"post": {
|
||||||
|
"summary": "lncli: `wtclient session terminate`\nTerminate terminates the given session and marks it as terminal so that\nit is not used for backups anymore.",
|
||||||
|
"operationId": "WatchtowerClient_TerminateSession",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A successful response.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/wtclientrpcTerminateSessionResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"description": "An unexpected error response.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/rpcStatus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "session_id",
|
||||||
|
"description": "The ID of the session that should be terminated.",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"type": "string",
|
||||||
|
"format": "byte"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"WatchtowerClient"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/v2/watchtower/client/stats": {
|
"/v2/watchtower/client/stats": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "lncli: `wtclient stats`\nStats returns the in-memory statistics of the client since startup.",
|
"summary": "lncli: `wtclient stats`\nStats returns the in-memory statistics of the client since startup.",
|
||||||
@@ -194,6 +227,39 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/v2/watchtower/client/tower/deactivate/{pubkey}": {
|
||||||
|
"post": {
|
||||||
|
"summary": "lncli: `wtclient deactivate`\nDeactivateTower sets the given tower's status to inactive so that it\nis not considered for session negotiation. Its sessions will also not\nbe used while the tower is inactive.",
|
||||||
|
"operationId": "WatchtowerClient_DeactivateTower",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A successful response.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/wtclientrpcDeactivateTowerResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"description": "An unexpected error response.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/rpcStatus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "pubkey",
|
||||||
|
"description": "The identifying public key of the watchtower to deactivate.",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"type": "string",
|
||||||
|
"format": "byte"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"WatchtowerClient"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/v2/watchtower/client/{pubkey}": {
|
"/v2/watchtower/client/{pubkey}": {
|
||||||
"delete": {
|
"delete": {
|
||||||
"summary": "lncli: `wtclient remove`\nRemoveTower removes a watchtower from being considered for future session\nnegotiations and from being used for any subsequent backups until it's added\nagain. If an address is provided, then this RPC only serves as a way of\nremoving the address from the watchtower instead.",
|
"summary": "lncli: `wtclient remove`\nRemoveTower removes a watchtower from being considered for future session\nnegotiations and from being used for any subsequent backups until it's added\nagain. If an address is provided, then this RPC only serves as a way of\nremoving the address from the watchtower instead.",
|
||||||
@@ -283,6 +349,15 @@
|
|||||||
"wtclientrpcAddTowerResponse": {
|
"wtclientrpcAddTowerResponse": {
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"wtclientrpcDeactivateTowerResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A string describing the action that took place."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"wtclientrpcListTowersResponse": {
|
"wtclientrpcListTowersResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -358,6 +433,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"wtclientrpcTerminateSessionResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A string describing the action that took place."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"wtclientrpcTower": {
|
"wtclientrpcTower": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -425,6 +509,11 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64",
|
"format": "int64",
|
||||||
"description": "The fee rate, in satoshis per vbyte, that will be used by the watchtower for\nthe justice transaction in the event of a channel breach."
|
"description": "The fee rate, in satoshis per vbyte, that will be used by the watchtower for\nthe justice transaction in the event of a channel breach."
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "byte",
|
||||||
|
"description": "The ID of the session."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ http:
|
|||||||
body: "*"
|
body: "*"
|
||||||
- selector: wtclientrpc.WatchtowerClient.RemoveTower
|
- selector: wtclientrpc.WatchtowerClient.RemoveTower
|
||||||
delete: "/v2/watchtower/client/{pubkey}"
|
delete: "/v2/watchtower/client/{pubkey}"
|
||||||
|
- selector: wtclientrpc.WatchtowerClient.DeactivateTower
|
||||||
|
post: "/v2/watchtower/client/tower/deactivate/{pubkey}"
|
||||||
|
- selector: wtclientrpc.WatchtowerClient.TerminateSession
|
||||||
|
post: "/v2/watchtower/client/sessions/terminate/{session_id}"
|
||||||
- selector: wtclientrpc.WatchtowerClient.ListTowers
|
- selector: wtclientrpc.WatchtowerClient.ListTowers
|
||||||
get: "/v2/watchtower/client"
|
get: "/v2/watchtower/client"
|
||||||
- selector: wtclientrpc.WatchtowerClient.GetTowerInfo
|
- selector: wtclientrpc.WatchtowerClient.GetTowerInfo
|
||||||
|
|||||||
@@ -30,6 +30,15 @@ type WatchtowerClientClient interface {
|
|||||||
// again. If an address is provided, then this RPC only serves as a way of
|
// again. If an address is provided, then this RPC only serves as a way of
|
||||||
// removing the address from the watchtower instead.
|
// removing the address from the watchtower instead.
|
||||||
RemoveTower(ctx context.Context, in *RemoveTowerRequest, opts ...grpc.CallOption) (*RemoveTowerResponse, error)
|
RemoveTower(ctx context.Context, in *RemoveTowerRequest, opts ...grpc.CallOption) (*RemoveTowerResponse, error)
|
||||||
|
// lncli: `wtclient deactivate`
|
||||||
|
// DeactivateTower sets the given tower's status to inactive so that it
|
||||||
|
// is not considered for session negotiation. Its sessions will also not
|
||||||
|
// be used while the tower is inactive.
|
||||||
|
DeactivateTower(ctx context.Context, in *DeactivateTowerRequest, opts ...grpc.CallOption) (*DeactivateTowerResponse, error)
|
||||||
|
// lncli: `wtclient session terminate`
|
||||||
|
// Terminate terminates the given session and marks it as terminal so that
|
||||||
|
// it is not used for backups anymore.
|
||||||
|
TerminateSession(ctx context.Context, in *TerminateSessionRequest, opts ...grpc.CallOption) (*TerminateSessionResponse, error)
|
||||||
// lncli: `wtclient towers`
|
// lncli: `wtclient towers`
|
||||||
// ListTowers returns the list of watchtowers registered with the client.
|
// ListTowers returns the list of watchtowers registered with the client.
|
||||||
ListTowers(ctx context.Context, in *ListTowersRequest, opts ...grpc.CallOption) (*ListTowersResponse, error)
|
ListTowers(ctx context.Context, in *ListTowersRequest, opts ...grpc.CallOption) (*ListTowersResponse, error)
|
||||||
@@ -70,6 +79,24 @@ func (c *watchtowerClientClient) RemoveTower(ctx context.Context, in *RemoveTowe
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *watchtowerClientClient) DeactivateTower(ctx context.Context, in *DeactivateTowerRequest, opts ...grpc.CallOption) (*DeactivateTowerResponse, error) {
|
||||||
|
out := new(DeactivateTowerResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/wtclientrpc.WatchtowerClient/DeactivateTower", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *watchtowerClientClient) TerminateSession(ctx context.Context, in *TerminateSessionRequest, opts ...grpc.CallOption) (*TerminateSessionResponse, error) {
|
||||||
|
out := new(TerminateSessionResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/wtclientrpc.WatchtowerClient/TerminateSession", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *watchtowerClientClient) ListTowers(ctx context.Context, in *ListTowersRequest, opts ...grpc.CallOption) (*ListTowersResponse, error) {
|
func (c *watchtowerClientClient) ListTowers(ctx context.Context, in *ListTowersRequest, opts ...grpc.CallOption) (*ListTowersResponse, error) {
|
||||||
out := new(ListTowersResponse)
|
out := new(ListTowersResponse)
|
||||||
err := c.cc.Invoke(ctx, "/wtclientrpc.WatchtowerClient/ListTowers", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/wtclientrpc.WatchtowerClient/ListTowers", in, out, opts...)
|
||||||
@@ -122,6 +149,15 @@ type WatchtowerClientServer interface {
|
|||||||
// again. If an address is provided, then this RPC only serves as a way of
|
// again. If an address is provided, then this RPC only serves as a way of
|
||||||
// removing the address from the watchtower instead.
|
// removing the address from the watchtower instead.
|
||||||
RemoveTower(context.Context, *RemoveTowerRequest) (*RemoveTowerResponse, error)
|
RemoveTower(context.Context, *RemoveTowerRequest) (*RemoveTowerResponse, error)
|
||||||
|
// lncli: `wtclient deactivate`
|
||||||
|
// DeactivateTower sets the given tower's status to inactive so that it
|
||||||
|
// is not considered for session negotiation. Its sessions will also not
|
||||||
|
// be used while the tower is inactive.
|
||||||
|
DeactivateTower(context.Context, *DeactivateTowerRequest) (*DeactivateTowerResponse, error)
|
||||||
|
// lncli: `wtclient session terminate`
|
||||||
|
// Terminate terminates the given session and marks it as terminal so that
|
||||||
|
// it is not used for backups anymore.
|
||||||
|
TerminateSession(context.Context, *TerminateSessionRequest) (*TerminateSessionResponse, error)
|
||||||
// lncli: `wtclient towers`
|
// lncli: `wtclient towers`
|
||||||
// ListTowers returns the list of watchtowers registered with the client.
|
// ListTowers returns the list of watchtowers registered with the client.
|
||||||
ListTowers(context.Context, *ListTowersRequest) (*ListTowersResponse, error)
|
ListTowers(context.Context, *ListTowersRequest) (*ListTowersResponse, error)
|
||||||
@@ -147,6 +183,12 @@ func (UnimplementedWatchtowerClientServer) AddTower(context.Context, *AddTowerRe
|
|||||||
func (UnimplementedWatchtowerClientServer) RemoveTower(context.Context, *RemoveTowerRequest) (*RemoveTowerResponse, error) {
|
func (UnimplementedWatchtowerClientServer) RemoveTower(context.Context, *RemoveTowerRequest) (*RemoveTowerResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method RemoveTower not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method RemoveTower not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedWatchtowerClientServer) DeactivateTower(context.Context, *DeactivateTowerRequest) (*DeactivateTowerResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method DeactivateTower not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedWatchtowerClientServer) TerminateSession(context.Context, *TerminateSessionRequest) (*TerminateSessionResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method TerminateSession not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedWatchtowerClientServer) ListTowers(context.Context, *ListTowersRequest) (*ListTowersResponse, error) {
|
func (UnimplementedWatchtowerClientServer) ListTowers(context.Context, *ListTowersRequest) (*ListTowersResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method ListTowers not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method ListTowers not implemented")
|
||||||
}
|
}
|
||||||
@@ -208,6 +250,42 @@ func _WatchtowerClient_RemoveTower_Handler(srv interface{}, ctx context.Context,
|
|||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _WatchtowerClient_DeactivateTower_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(DeactivateTowerRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(WatchtowerClientServer).DeactivateTower(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/wtclientrpc.WatchtowerClient/DeactivateTower",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(WatchtowerClientServer).DeactivateTower(ctx, req.(*DeactivateTowerRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _WatchtowerClient_TerminateSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(TerminateSessionRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(WatchtowerClientServer).TerminateSession(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/wtclientrpc.WatchtowerClient/TerminateSession",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(WatchtowerClientServer).TerminateSession(ctx, req.(*TerminateSessionRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
func _WatchtowerClient_ListTowers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _WatchtowerClient_ListTowers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(ListTowersRequest)
|
in := new(ListTowersRequest)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
@@ -295,6 +373,14 @@ var WatchtowerClient_ServiceDesc = grpc.ServiceDesc{
|
|||||||
MethodName: "RemoveTower",
|
MethodName: "RemoveTower",
|
||||||
Handler: _WatchtowerClient_RemoveTower_Handler,
|
Handler: _WatchtowerClient_RemoveTower_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "DeactivateTower",
|
||||||
|
Handler: _WatchtowerClient_DeactivateTower_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "TerminateSession",
|
||||||
|
Handler: _WatchtowerClient_TerminateSession_Handler,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
MethodName: "ListTowers",
|
MethodName: "ListTowers",
|
||||||
Handler: _WatchtowerClient_ListTowers_Handler,
|
Handler: _WatchtowerClient_ListTowers_Handler,
|
||||||
|
|||||||
@@ -52,6 +52,38 @@ func (h *HarnessRPC) AddTower(
|
|||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeactivateTower makes an RPC call to the WatchtowerClient of the given node
|
||||||
|
// and asserts.
|
||||||
|
func (h *HarnessRPC) DeactivateTower(req *wtclientrpc.DeactivateTowerRequest) {
|
||||||
|
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, err := h.WatchtowerClient.DeactivateTower(ctxt, req)
|
||||||
|
h.NoError(err, "DeactivateTower")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TerminateSession makes an RPC call to the WatchtowerClient of the given node
|
||||||
|
// and asserts.
|
||||||
|
func (h *HarnessRPC) TerminateSession(
|
||||||
|
req *wtclientrpc.TerminateSessionRequest) {
|
||||||
|
|
||||||
|
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, err := h.WatchtowerClient.TerminateSession(ctxt, req)
|
||||||
|
h.NoError(err, "TerminateSession")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveTower makes an RPC call to the WatchtowerClient of the given node
|
||||||
|
// and asserts.
|
||||||
|
func (h *HarnessRPC) RemoveTower(req *wtclientrpc.RemoveTowerRequest) {
|
||||||
|
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, err := h.WatchtowerClient.RemoveTower(ctxt, req)
|
||||||
|
h.NoError(err, "RemoveTower")
|
||||||
|
}
|
||||||
|
|
||||||
// WatchtowerStats makes a RPC call to the WatchtowerClient of the given node
|
// WatchtowerStats makes a RPC call to the WatchtowerClient of the given node
|
||||||
// and asserts.
|
// and asserts.
|
||||||
func (h *HarnessRPC) WatchtowerStats() *wtclientrpc.StatsResponse {
|
func (h *HarnessRPC) WatchtowerStats() *wtclientrpc.StatsResponse {
|
||||||
|
|||||||
+189
-17
@@ -132,6 +132,35 @@ type staleTowerMsg struct {
|
|||||||
errChan chan error
|
errChan chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deactivateTowerMsg is an internal message we'll use within the TowerClient
|
||||||
|
// to signal that a tower should be marked as inactive.
|
||||||
|
type deactivateTowerMsg struct {
|
||||||
|
// id is the unique database identifier for the tower.
|
||||||
|
id wtdb.TowerID
|
||||||
|
|
||||||
|
// pubKey is the identifying public key of the watchtower.
|
||||||
|
pubKey *btcec.PublicKey
|
||||||
|
|
||||||
|
// errChan is the channel through which we'll send a response back to
|
||||||
|
// the caller when handling their request.
|
||||||
|
//
|
||||||
|
// NOTE: This channel must be buffered.
|
||||||
|
errChan chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
// terminateSessMsg is an internal message we'll use within the TowerClient to
|
||||||
|
// signal that a session should be terminated.
|
||||||
|
type terminateSessMsg struct {
|
||||||
|
// id is the session identifier.
|
||||||
|
id wtdb.SessionID
|
||||||
|
|
||||||
|
// errChan is the channel through which we'll send a response back to
|
||||||
|
// the caller when handling their request.
|
||||||
|
//
|
||||||
|
// NOTE: This channel must be buffered.
|
||||||
|
errChan chan error
|
||||||
|
}
|
||||||
|
|
||||||
// clientCfg holds the configuration values required by a client.
|
// clientCfg holds the configuration values required by a client.
|
||||||
type clientCfg struct {
|
type clientCfg struct {
|
||||||
*Config
|
*Config
|
||||||
@@ -165,8 +194,10 @@ type client struct {
|
|||||||
statTicker *time.Ticker
|
statTicker *time.Ticker
|
||||||
stats *clientStats
|
stats *clientStats
|
||||||
|
|
||||||
newTowers chan *newTowerMsg
|
newTowers chan *newTowerMsg
|
||||||
staleTowers chan *staleTowerMsg
|
staleTowers chan *staleTowerMsg
|
||||||
|
deactivateTowers chan *deactivateTowerMsg
|
||||||
|
terminateSessions chan *terminateSessMsg
|
||||||
|
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
@@ -192,15 +223,17 @@ func newClient(cfg *clientCfg) (*client, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c := &client{
|
c := &client{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
log: plog,
|
log: plog,
|
||||||
pipeline: queue,
|
pipeline: queue,
|
||||||
activeSessions: newSessionQueueSet(),
|
activeSessions: newSessionQueueSet(),
|
||||||
statTicker: time.NewTicker(DefaultStatInterval),
|
statTicker: time.NewTicker(DefaultStatInterval),
|
||||||
stats: new(clientStats),
|
stats: new(clientStats),
|
||||||
newTowers: make(chan *newTowerMsg),
|
newTowers: make(chan *newTowerMsg),
|
||||||
staleTowers: make(chan *staleTowerMsg),
|
staleTowers: make(chan *staleTowerMsg),
|
||||||
quit: make(chan struct{}),
|
deactivateTowers: make(chan *deactivateTowerMsg),
|
||||||
|
terminateSessions: make(chan *terminateSessMsg),
|
||||||
|
quit: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
candidateTowers := newTowerListIterator()
|
candidateTowers := newTowerListIterator()
|
||||||
@@ -261,7 +294,10 @@ func getTowerAndSessionCandidates(db DB, keyRing ECDHKeyRing,
|
|||||||
opts ...wtdb.ClientSessionListOption) (
|
opts ...wtdb.ClientSessionListOption) (
|
||||||
map[wtdb.SessionID]*ClientSession, error) {
|
map[wtdb.SessionID]*ClientSession, error) {
|
||||||
|
|
||||||
towers, err := db.ListTowers()
|
// Fetch all active towers from the DB.
|
||||||
|
towers, err := db.ListTowers(func(tower *wtdb.Tower) bool {
|
||||||
|
return tower.Status == wtdb.TowerStatusActive
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -288,9 +324,9 @@ func getTowerAndSessionCandidates(db DB, keyRing ECDHKeyRing,
|
|||||||
|
|
||||||
// Add the session to the set of candidate sessions.
|
// Add the session to the set of candidate sessions.
|
||||||
candidateSessions[s.ID] = cs
|
candidateSessions[s.ID] = cs
|
||||||
|
|
||||||
perActiveTower(tower)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
perActiveTower(tower)
|
||||||
}
|
}
|
||||||
|
|
||||||
return candidateSessions, nil
|
return candidateSessions, nil
|
||||||
@@ -511,8 +547,8 @@ func (c *client) nextSessionQueue() (*sessionQueue, error) {
|
|||||||
|
|
||||||
// stopAndRemoveSession stops the session with the given ID and removes it from
|
// stopAndRemoveSession stops the session with the given ID and removes it from
|
||||||
// the in-memory active sessions set.
|
// the in-memory active sessions set.
|
||||||
func (c *client) stopAndRemoveSession(id wtdb.SessionID) error {
|
func (c *client) stopAndRemoveSession(id wtdb.SessionID, final bool) error {
|
||||||
return c.activeSessions.StopAndRemove(id)
|
return c.activeSessions.StopAndRemove(id, final)
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteSessionFromTower dials the tower that we created the session with and
|
// deleteSessionFromTower dials the tower that we created the session with and
|
||||||
@@ -691,6 +727,16 @@ func (c *client) backupDispatcher() {
|
|||||||
case msg := <-c.staleTowers:
|
case msg := <-c.staleTowers:
|
||||||
msg.errChan <- c.handleStaleTower(msg)
|
msg.errChan <- c.handleStaleTower(msg)
|
||||||
|
|
||||||
|
// A tower has been requested to be de-activated. We'll
|
||||||
|
// only allow this if the tower is not currently being
|
||||||
|
// used for session negotiation.
|
||||||
|
case msg := <-c.deactivateTowers:
|
||||||
|
msg.errChan <- c.handleDeactivateTower(msg)
|
||||||
|
|
||||||
|
// A request has come through to terminate a session.
|
||||||
|
case msg := <-c.terminateSessions:
|
||||||
|
msg.errChan <- c.handleTerminateSession(msg)
|
||||||
|
|
||||||
case <-c.quit:
|
case <-c.quit:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -776,6 +822,14 @@ func (c *client) backupDispatcher() {
|
|||||||
case msg := <-c.staleTowers:
|
case msg := <-c.staleTowers:
|
||||||
msg.errChan <- c.handleStaleTower(msg)
|
msg.errChan <- c.handleStaleTower(msg)
|
||||||
|
|
||||||
|
// A tower has been requested to be de-activated.
|
||||||
|
case msg := <-c.deactivateTowers:
|
||||||
|
msg.errChan <- c.handleDeactivateTower(msg)
|
||||||
|
|
||||||
|
// A request has come through to terminate a session.
|
||||||
|
case msg := <-c.terminateSessions:
|
||||||
|
msg.errChan <- c.handleTerminateSession(msg)
|
||||||
|
|
||||||
case <-c.quit:
|
case <-c.quit:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1043,6 +1097,124 @@ func (c *client) initActiveQueue(s *ClientSession,
|
|||||||
return sq
|
return sq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// terminateSession sets the given session's status to CSessionTerminal meaning
|
||||||
|
// that it will not be used again.
|
||||||
|
func (c *client) terminateSession(id wtdb.SessionID) error {
|
||||||
|
errChan := make(chan error, 1)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case c.terminateSessions <- &terminateSessMsg{
|
||||||
|
id: id,
|
||||||
|
errChan: errChan,
|
||||||
|
}:
|
||||||
|
case <-c.pipeline.quit:
|
||||||
|
return ErrClientExiting
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-errChan:
|
||||||
|
return err
|
||||||
|
case <-c.pipeline.quit:
|
||||||
|
return ErrClientExiting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleTerminateSession handles a request to terminate a session. It will
|
||||||
|
// first shut down the session if it is part of the active session set, then
|
||||||
|
// it will ensure that the active session queue is set reset if it is using the
|
||||||
|
// session in question. Finally, the session's status in the DB will be updated.
|
||||||
|
func (c *client) handleTerminateSession(msg *terminateSessMsg) error {
|
||||||
|
id := msg.id
|
||||||
|
|
||||||
|
delete(c.candidateSessions, id)
|
||||||
|
|
||||||
|
err := c.activeSessions.StopAndRemove(id, true)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not stop session %s: %w", id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If our active session queue corresponds to the session being
|
||||||
|
// terminated, then we'll proceed to negotiate a new one.
|
||||||
|
if c.sessionQueue != nil {
|
||||||
|
if bytes.Equal(c.sessionQueue.ID()[:], id[:]) {
|
||||||
|
c.sessionQueue = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// deactivateTower sends a tower deactivation request to the backupDispatcher
|
||||||
|
// where it will be handled synchronously. The request should result in all the
|
||||||
|
// sessions that we have with the given tower being shutdown and removed from
|
||||||
|
// our in-memory set of active sessions.
|
||||||
|
func (c *client) deactivateTower(id wtdb.TowerID,
|
||||||
|
pubKey *btcec.PublicKey) error {
|
||||||
|
|
||||||
|
errChan := make(chan error, 1)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case c.deactivateTowers <- &deactivateTowerMsg{
|
||||||
|
id: id,
|
||||||
|
pubKey: pubKey,
|
||||||
|
errChan: errChan,
|
||||||
|
}:
|
||||||
|
case <-c.pipeline.quit:
|
||||||
|
return ErrClientExiting
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-errChan:
|
||||||
|
return err
|
||||||
|
case <-c.pipeline.quit:
|
||||||
|
return ErrClientExiting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleDeactivateTower handles a request to deactivate a tower. We will remove
|
||||||
|
// it from the in-memory candidate set, and we will also stop any active
|
||||||
|
// sessions we have with this tower.
|
||||||
|
func (c *client) handleDeactivateTower(msg *deactivateTowerMsg) error {
|
||||||
|
// Remove the tower from our in-memory candidate set so that it is not
|
||||||
|
// used for any new session negotiations.
|
||||||
|
err := c.candidateTowers.RemoveCandidate(msg.id, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey := msg.pubKey.SerializeCompressed()
|
||||||
|
sessions, err := c.cfg.DB.ListClientSessions(&msg.id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to retrieve sessions for tower %x: "+
|
||||||
|
"%v", pubKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over all the sessions we have for this tower and remove them
|
||||||
|
// from our candidate set and also from our set of started, active
|
||||||
|
// sessions.
|
||||||
|
for sessionID := range sessions {
|
||||||
|
delete(c.candidateSessions, sessionID)
|
||||||
|
|
||||||
|
err = c.activeSessions.StopAndRemove(sessionID, false)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not stop session %s: %w",
|
||||||
|
sessionID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If our active session queue corresponds to the stale tower, we'll
|
||||||
|
// proceed to negotiate a new one.
|
||||||
|
if c.sessionQueue != nil {
|
||||||
|
towerKey := c.sessionQueue.tower.IdentityKey
|
||||||
|
|
||||||
|
if bytes.Equal(pubKey, towerKey.SerializeCompressed()) {
|
||||||
|
c.sessionQueue = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// addTower adds a new watchtower reachable at the given address and considers
|
// addTower adds a new watchtower reachable at the given address and considers
|
||||||
// it for new sessions. If the watchtower already exists, then any new addresses
|
// it for new sessions. If the watchtower already exists, then any new addresses
|
||||||
// included will be considered when dialing it for session negotiations and
|
// included will be considered when dialing it for session negotiations and
|
||||||
@@ -1149,7 +1321,7 @@ func (c *client) handleStaleTower(msg *staleTowerMsg) error {
|
|||||||
|
|
||||||
// Shutdown the session so that any pending updates are
|
// Shutdown the session so that any pending updates are
|
||||||
// replayed back onto the main task pipeline.
|
// replayed back onto the main task pipeline.
|
||||||
err = c.activeSessions.StopAndRemove(sessionID)
|
err = c.activeSessions.StopAndRemove(sessionID, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.log.Errorf("could not stop session %s: %w", sessionID,
|
c.log.Errorf("could not stop session %s: %w", sessionID,
|
||||||
err)
|
err)
|
||||||
|
|||||||
@@ -1034,15 +1034,19 @@ func (s *serverHarness) waitForUpdates(hints []blob.BreachHint,
|
|||||||
// Closure to assert the server's matches are consistent with the hint
|
// Closure to assert the server's matches are consistent with the hint
|
||||||
// set.
|
// set.
|
||||||
serverHasHints := func(matches []wtdb.Match) bool {
|
serverHasHints := func(matches []wtdb.Match) bool {
|
||||||
if len(hintSet) != len(matches) {
|
// De-dup the server matches since it might very well have
|
||||||
|
// multiple matches for a hint if that update was backed up on
|
||||||
|
// more than one session.
|
||||||
|
matchHints := make(map[blob.BreachHint]struct{})
|
||||||
|
for _, match := range matches {
|
||||||
|
matchHints[match.Hint] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hintSet) != len(matchHints) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, match := range matches {
|
require.EqualValues(s.t, hintSet, matchHints)
|
||||||
_, ok := hintSet[match.Hint]
|
|
||||||
require.Truef(s.t, ok, "match %v in db is not in "+
|
|
||||||
"hint set", match.Hint)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -2688,6 +2692,205 @@ var clientTests = []clientTest{
|
|||||||
require.NoError(h.t, err)
|
require.NoError(h.t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "de-activate a tower",
|
||||||
|
cfg: harnessCfg{
|
||||||
|
localBalance: localBalance,
|
||||||
|
remoteBalance: remoteBalance,
|
||||||
|
policy: wtpolicy.Policy{
|
||||||
|
TxPolicy: defaultTxPolicy,
|
||||||
|
MaxUpdates: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fn: func(h *testHarness) {
|
||||||
|
const (
|
||||||
|
numUpdates = 10
|
||||||
|
chanIDInt = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// Advance the channel with a few updates.
|
||||||
|
hints := h.advanceChannelN(chanIDInt, numUpdates)
|
||||||
|
|
||||||
|
// Backup a few these updates and wait for them to
|
||||||
|
// arrive at the server.
|
||||||
|
h.backupStates(chanIDInt, 0, numUpdates/2, nil)
|
||||||
|
h.server.waitForUpdates(hints[:numUpdates/2], waitTime)
|
||||||
|
|
||||||
|
// Lookup the tower and assert that it currently is
|
||||||
|
// seen as an active session candidate.
|
||||||
|
resp, err := h.clientMgr.LookupTower(
|
||||||
|
h.server.addr.IdentityKey,
|
||||||
|
)
|
||||||
|
require.NoError(h.t, err)
|
||||||
|
tower, ok := resp[blob.TypeAltruistTaprootCommit]
|
||||||
|
require.True(h.t, ok)
|
||||||
|
require.True(h.t, tower.ActiveSessionCandidate)
|
||||||
|
|
||||||
|
// Deactivate the tower.
|
||||||
|
err = h.clientMgr.DeactivateTower(
|
||||||
|
h.server.addr.IdentityKey,
|
||||||
|
)
|
||||||
|
require.NoError(h.t, err)
|
||||||
|
|
||||||
|
// Assert that it is no longer seen as an active
|
||||||
|
// session candidate.
|
||||||
|
resp, err = h.clientMgr.LookupTower(
|
||||||
|
h.server.addr.IdentityKey,
|
||||||
|
)
|
||||||
|
require.NoError(h.t, err)
|
||||||
|
tower, ok = resp[blob.TypeAltruistTaprootCommit]
|
||||||
|
require.True(h.t, ok)
|
||||||
|
require.False(h.t, tower.ActiveSessionCandidate)
|
||||||
|
|
||||||
|
// Add a new tower.
|
||||||
|
server2 := newServerHarness(
|
||||||
|
h.t, h.net, towerAddr2Str, nil,
|
||||||
|
)
|
||||||
|
server2.start()
|
||||||
|
h.addTower(server2.addr)
|
||||||
|
|
||||||
|
// Backup a few more states and assert that they appear
|
||||||
|
// on the second tower server.
|
||||||
|
h.backupStates(
|
||||||
|
chanIDInt, numUpdates/2, numUpdates-1, nil,
|
||||||
|
)
|
||||||
|
server2.waitForUpdates(
|
||||||
|
hints[numUpdates/2:numUpdates-1], waitTime,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reactivate the first tower.
|
||||||
|
err = h.clientMgr.AddTower(h.server.addr)
|
||||||
|
require.NoError(h.t, err)
|
||||||
|
|
||||||
|
// Deactivate the second tower.
|
||||||
|
err = h.clientMgr.DeactivateTower(
|
||||||
|
server2.addr.IdentityKey,
|
||||||
|
)
|
||||||
|
require.NoError(h.t, err)
|
||||||
|
|
||||||
|
// Backup the last backup and assert that it appears
|
||||||
|
// on the first tower.
|
||||||
|
h.backupStates(chanIDInt, numUpdates-1, numUpdates, nil)
|
||||||
|
h.server.waitForUpdates(hints[numUpdates-1:], waitTime)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "terminate session",
|
||||||
|
cfg: harnessCfg{
|
||||||
|
localBalance: localBalance,
|
||||||
|
remoteBalance: remoteBalance,
|
||||||
|
policy: wtpolicy.Policy{
|
||||||
|
TxPolicy: defaultTxPolicy,
|
||||||
|
MaxUpdates: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fn: func(h *testHarness) {
|
||||||
|
const (
|
||||||
|
numUpdates = 10
|
||||||
|
chanIDInt = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// Advance the channel with a few updates.
|
||||||
|
hints := h.advanceChannelN(chanIDInt, numUpdates)
|
||||||
|
|
||||||
|
// Backup one of these updates and wait for it to
|
||||||
|
// arrive at the server.
|
||||||
|
h.backupStates(chanIDInt, 0, 1, nil)
|
||||||
|
h.server.waitForUpdates(hints[:1], waitTime)
|
||||||
|
|
||||||
|
// Now, restart the server in a state where it will not
|
||||||
|
// ack updates. This will allow us to wait for an update
|
||||||
|
// to be un-acked and persisted.
|
||||||
|
h.server.restart(func(cfg *wtserver.Config) {
|
||||||
|
cfg.NoAckUpdates = true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Backup another update. These should remain in the
|
||||||
|
// client as un-acked.
|
||||||
|
h.backupStates(chanIDInt, 1, 2, nil)
|
||||||
|
|
||||||
|
// Wait for the update to be persisted.
|
||||||
|
fetchUnacked := h.clientDB.FetchSessionCommittedUpdates
|
||||||
|
var sessID wtdb.SessionID
|
||||||
|
err := wait.Predicate(func() bool {
|
||||||
|
sessions, err := h.clientDB.ListClientSessions(
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
require.NoError(h.t, err)
|
||||||
|
|
||||||
|
var updates []wtdb.CommittedUpdate
|
||||||
|
for id := range sessions {
|
||||||
|
sessID = id
|
||||||
|
updates, err = fetchUnacked(&id)
|
||||||
|
require.NoError(h.t, err)
|
||||||
|
|
||||||
|
return len(updates) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}, waitTime)
|
||||||
|
require.NoError(h.t, err)
|
||||||
|
|
||||||
|
// Now try to terminate the session by directly calling
|
||||||
|
// the DB terminate method. This is expected to fail
|
||||||
|
// since the session still has un-acked updates.
|
||||||
|
err = h.clientDB.TerminateSession(sessID)
|
||||||
|
require.ErrorIs(
|
||||||
|
h.t, err, wtdb.ErrSessionHasUnackedUpdates,
|
||||||
|
)
|
||||||
|
|
||||||
|
// If we try to terminate the session through the client
|
||||||
|
// interface though, it should succeed since the client
|
||||||
|
// will handle the un-acked updates of the session.
|
||||||
|
err = h.clientMgr.TerminateSession(sessID)
|
||||||
|
require.NoError(h.t, err)
|
||||||
|
|
||||||
|
// Fetch the session from the DB and assert that it is
|
||||||
|
// in the terminal state and that it is not exhausted.
|
||||||
|
sess, err := h.clientDB.GetClientSession(sessID)
|
||||||
|
require.NoError(h.t, err)
|
||||||
|
|
||||||
|
require.Equal(h.t, wtdb.CSessionTerminal, sess.Status)
|
||||||
|
require.NotEqual(
|
||||||
|
h.t, sess.Policy.MaxUpdates, sess.SeqNum,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Restart the server and allow it to ack updates again.
|
||||||
|
h.server.restart(func(cfg *wtserver.Config) {
|
||||||
|
cfg.NoAckUpdates = false
|
||||||
|
})
|
||||||
|
|
||||||
|
// Wait for the update from before to appear on the
|
||||||
|
// server. The server will actually have this back-up
|
||||||
|
// stored twice now since it would have stored it for
|
||||||
|
// the first session even though it did not send an ACK
|
||||||
|
// for it.
|
||||||
|
h.server.waitForUpdates(hints[1:2], waitTime)
|
||||||
|
|
||||||
|
// Now we want to assert that this update was definitely
|
||||||
|
// not sent on the terminated session but was instead
|
||||||
|
// sent in a new session.
|
||||||
|
var (
|
||||||
|
updateCounts = make(map[wtdb.SessionID]uint16)
|
||||||
|
totalUpdates uint16
|
||||||
|
)
|
||||||
|
sessions, err := h.clientDB.ListClientSessions(nil,
|
||||||
|
wtdb.WithPerNumAckedUpdates(
|
||||||
|
func(s *wtdb.ClientSession,
|
||||||
|
_ lnwire.ChannelID,
|
||||||
|
num uint16) {
|
||||||
|
|
||||||
|
updateCounts[s.ID] += num
|
||||||
|
totalUpdates += num
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
require.NoError(h.t, err)
|
||||||
|
require.Len(h.t, sessions, 2)
|
||||||
|
require.EqualValues(h.t, 1, updateCounts[sessID])
|
||||||
|
require.EqualValues(h.t, 2, totalUpdates)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestClient executes the client test suite, asserting the ability to backup
|
// TestClient executes the client test suite, asserting the ability to backup
|
||||||
|
|||||||
@@ -26,14 +26,17 @@ type DB interface {
|
|||||||
// RemoveTower modifies a tower's record within the database. If an
|
// RemoveTower modifies a tower's record within the database. If an
|
||||||
// address is provided, then _only_ the address record should be removed
|
// address is provided, then _only_ the address record should be removed
|
||||||
// from the tower's persisted state. Otherwise, we'll attempt to mark
|
// from the tower's persisted state. Otherwise, we'll attempt to mark
|
||||||
// the tower as inactive by marking all of its sessions inactive. If any
|
// the tower as inactive. If any of its sessions have unacked updates,
|
||||||
// of its sessions has unacked updates, then ErrTowerUnackedUpdates is
|
// then ErrTowerUnackedUpdates is returned. If the tower doesn't have
|
||||||
// returned. If the tower doesn't have any sessions at all, it'll be
|
// any sessions at all, it'll be completely removed from the database.
|
||||||
// completely removed from the database.
|
|
||||||
//
|
//
|
||||||
// NOTE: An error is not returned if the tower doesn't exist.
|
// NOTE: An error is not returned if the tower doesn't exist.
|
||||||
RemoveTower(*btcec.PublicKey, net.Addr) error
|
RemoveTower(*btcec.PublicKey, net.Addr) error
|
||||||
|
|
||||||
|
// TerminateSession sets the given session's status to CSessionTerminal
|
||||||
|
// meaning that it will not be usable again.
|
||||||
|
TerminateSession(id wtdb.SessionID) error
|
||||||
|
|
||||||
// LoadTower retrieves a tower by its public key.
|
// LoadTower retrieves a tower by its public key.
|
||||||
LoadTower(*btcec.PublicKey) (*wtdb.Tower, error)
|
LoadTower(*btcec.PublicKey) (*wtdb.Tower, error)
|
||||||
|
|
||||||
@@ -41,8 +44,9 @@ type DB interface {
|
|||||||
LoadTowerByID(wtdb.TowerID) (*wtdb.Tower, error)
|
LoadTowerByID(wtdb.TowerID) (*wtdb.Tower, error)
|
||||||
|
|
||||||
// ListTowers retrieves the list of towers available within the
|
// ListTowers retrieves the list of towers available within the
|
||||||
// database.
|
// database. The filter function may be set in order to filter out the
|
||||||
ListTowers() ([]*wtdb.Tower, error)
|
// towers to be returned.
|
||||||
|
ListTowers(filter wtdb.TowerFilterFn) ([]*wtdb.Tower, error)
|
||||||
|
|
||||||
// NextSessionKeyIndex reserves a new session key derivation index for a
|
// NextSessionKeyIndex reserves a new session key derivation index for a
|
||||||
// particular tower id and blob type. The index is reserved for that
|
// particular tower id and blob type. The index is reserved for that
|
||||||
@@ -136,9 +140,14 @@ type DB interface {
|
|||||||
// space.
|
// space.
|
||||||
GetDBQueue(namespace []byte) wtdb.Queue[*wtdb.BackupID]
|
GetDBQueue(namespace []byte) wtdb.Queue[*wtdb.BackupID]
|
||||||
|
|
||||||
// DeleteCommittedUpdate deletes the committed update belonging to the
|
// DeleteCommittedUpdates deletes all the committed updates belonging to
|
||||||
// given session and with the given sequence number from the db.
|
// the given session from the db.
|
||||||
DeleteCommittedUpdate(id *wtdb.SessionID, seqNum uint16) error
|
DeleteCommittedUpdates(id *wtdb.SessionID) error
|
||||||
|
|
||||||
|
// DeactivateTower sets the given tower's status to inactive. This means
|
||||||
|
// that this tower's sessions won't be loaded and used for backups.
|
||||||
|
// CreateTower can be used to reactivate the tower again.
|
||||||
|
DeactivateTower(pubKey *btcec.PublicKey) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthDialer connects to a remote node using an authenticated transport, such
|
// AuthDialer connects to a remote node using an authenticated transport, such
|
||||||
|
|||||||
@@ -38,6 +38,15 @@ type ClientManager interface {
|
|||||||
// instead.
|
// instead.
|
||||||
RemoveTower(*btcec.PublicKey, net.Addr) error
|
RemoveTower(*btcec.PublicKey, net.Addr) error
|
||||||
|
|
||||||
|
// DeactivateTower sets the given tower's status to inactive so that it
|
||||||
|
// is not considered for session negotiation. Its sessions will also not
|
||||||
|
// be used while the tower is inactive.
|
||||||
|
DeactivateTower(pubKey *btcec.PublicKey) error
|
||||||
|
|
||||||
|
// TerminateSession sets the given session's status to CSessionTerminal
|
||||||
|
// meaning that it will not be used again.
|
||||||
|
TerminateSession(id wtdb.SessionID) error
|
||||||
|
|
||||||
// Stats returns the in-memory statistics of the client since startup.
|
// Stats returns the in-memory statistics of the client since startup.
|
||||||
Stats() ClientStats
|
Stats() ClientStats
|
||||||
|
|
||||||
@@ -431,6 +440,73 @@ func (m *Manager) RemoveTower(key *btcec.PublicKey, addr net.Addr) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TerminateSession sets the given session's status to CSessionTerminal meaning
|
||||||
|
// that it will not be used again.
|
||||||
|
func (m *Manager) TerminateSession(id wtdb.SessionID) error {
|
||||||
|
m.clientsMu.Lock()
|
||||||
|
defer m.clientsMu.Unlock()
|
||||||
|
|
||||||
|
for _, client := range m.clients {
|
||||||
|
err := client.terminateSession(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, mark the session as terminated in the DB.
|
||||||
|
return m.cfg.DB.TerminateSession(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeactivateTower sets the given tower's status to inactive so that it is not
|
||||||
|
// considered for session negotiation. Its sessions will also not be used while
|
||||||
|
// the tower is inactive.
|
||||||
|
func (m *Manager) DeactivateTower(key *btcec.PublicKey) error {
|
||||||
|
// We'll load the tower in order to retrieve its ID within the database.
|
||||||
|
tower, err := m.cfg.DB.LoadTower(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.clientsMu.Lock()
|
||||||
|
defer m.clientsMu.Unlock()
|
||||||
|
|
||||||
|
for _, client := range m.clients {
|
||||||
|
err := client.deactivateTower(tower.ID, tower.IdentityKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, mark the tower as inactive in the DB.
|
||||||
|
err = m.cfg.DB.DeactivateTower(key)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Could not deactivate the tower. Re-activating. %v",
|
||||||
|
err)
|
||||||
|
|
||||||
|
// If the persisted state update fails, re-add the address to
|
||||||
|
// our client's in-memory state.
|
||||||
|
tower, newTowerErr := NewTowerFromDBTower(tower)
|
||||||
|
if newTowerErr != nil {
|
||||||
|
log.Errorf("Could not create new in-memory tower: %v",
|
||||||
|
newTowerErr)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, client := range m.clients {
|
||||||
|
addTowerErr := client.addTower(tower)
|
||||||
|
if addTowerErr != nil {
|
||||||
|
log.Errorf("Could not re-add tower: %v",
|
||||||
|
addTowerErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Stats returns the in-memory statistics of the clients managed by the Manager
|
// Stats returns the in-memory statistics of the clients managed by the Manager
|
||||||
// since startup.
|
// since startup.
|
||||||
func (m *Manager) Stats() ClientStats {
|
func (m *Manager) Stats() ClientStats {
|
||||||
@@ -455,7 +531,7 @@ func (m *Manager) Stats() ClientStats {
|
|||||||
func (m *Manager) RegisteredTowers(opts ...wtdb.ClientSessionListOption) (
|
func (m *Manager) RegisteredTowers(opts ...wtdb.ClientSessionListOption) (
|
||||||
map[blob.Type][]*RegisteredTower, error) {
|
map[blob.Type][]*RegisteredTower, error) {
|
||||||
|
|
||||||
towers, err := m.cfg.DB.ListTowers()
|
towers, err := m.cfg.DB.ListTowers(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -850,7 +926,7 @@ func (m *Manager) handleClosableSessions(
|
|||||||
// Stop the session and remove it from the
|
// Stop the session and remove it from the
|
||||||
// in-memory set.
|
// in-memory set.
|
||||||
err = client.stopAndRemoveSession(
|
err = client.stopAndRemoveSession(
|
||||||
item.sessionID,
|
item.sessionID, true,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("could not remove "+
|
log.Errorf("could not remove "+
|
||||||
|
|||||||
@@ -211,14 +211,13 @@ func (q *sessionQueue) Stop(final bool) error {
|
|||||||
update.BackupID, err)
|
update.BackupID, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = q.cfg.DB.DeleteCommittedUpdate(
|
if final {
|
||||||
q.ID(), update.SeqNum,
|
err = q.cfg.DB.DeleteCommittedUpdates(q.ID())
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("could not delete committed "+
|
log.Errorf("could not delete committed "+
|
||||||
"update %d for session %s",
|
"updates for session %s", q.ID())
|
||||||
update.SeqNum, q.ID())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -766,7 +765,7 @@ func (s *sessionQueueSet) AddAndStart(sessionQueue *sessionQueue) {
|
|||||||
|
|
||||||
// StopAndRemove stops the given session queue and removes it from the
|
// StopAndRemove stops the given session queue and removes it from the
|
||||||
// sessionQueueSet.
|
// sessionQueueSet.
|
||||||
func (s *sessionQueueSet) StopAndRemove(id wtdb.SessionID) error {
|
func (s *sessionQueueSet) StopAndRemove(id wtdb.SessionID, final bool) error {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
@@ -777,7 +776,7 @@ func (s *sessionQueueSet) StopAndRemove(id wtdb.SessionID) error {
|
|||||||
|
|
||||||
delete(s.queues, id)
|
delete(s.queues, id)
|
||||||
|
|
||||||
return queue.Stop(true)
|
return queue.Stop(final)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get fetches and returns the sessionQueue with the given ID.
|
// Get fetches and returns the sessionQueue with the given ID.
|
||||||
|
|||||||
+211
-89
@@ -196,9 +196,9 @@ var (
|
|||||||
// session has updates for channels that are still open.
|
// session has updates for channels that are still open.
|
||||||
errSessionHasOpenChannels = errors.New("session has open channels")
|
errSessionHasOpenChannels = errors.New("session has open channels")
|
||||||
|
|
||||||
// errSessionHasUnackedUpdates is an error used to indicate that a
|
// ErrSessionHasUnackedUpdates is an error used to indicate that a
|
||||||
// session has un-acked updates.
|
// session has un-acked updates.
|
||||||
errSessionHasUnackedUpdates = errors.New("session has un-acked updates")
|
ErrSessionHasUnackedUpdates = errors.New("session has un-acked updates")
|
||||||
|
|
||||||
// errChannelHasMoreSessions is an error used to indicate that a channel
|
// errChannelHasMoreSessions is an error used to indicate that a channel
|
||||||
// has updates in other non-closed sessions.
|
// has updates in other non-closed sessions.
|
||||||
@@ -387,41 +387,13 @@ func (c *ClientDB) CreateTower(lnAddr *lnwire.NetAddress) (*Tower, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set its status to active.
|
||||||
|
tower.Status = TowerStatusActive
|
||||||
|
|
||||||
// Add the new address to the existing tower. If the
|
// Add the new address to the existing tower. If the
|
||||||
// address is a duplicate, this will result in no
|
// address is a duplicate, this will result in no
|
||||||
// change.
|
// change.
|
||||||
tower.AddAddress(lnAddr.Address)
|
tower.AddAddress(lnAddr.Address)
|
||||||
|
|
||||||
// If there are any client sessions that correspond to
|
|
||||||
// this tower, we'll mark them as active to ensure we
|
|
||||||
// load them upon restarts.
|
|
||||||
towerSessIndex := towerToSessionIndex.NestedReadBucket(
|
|
||||||
tower.ID.Bytes(),
|
|
||||||
)
|
|
||||||
if towerSessIndex == nil {
|
|
||||||
return ErrTowerNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
sessions := tx.ReadWriteBucket(cSessionBkt)
|
|
||||||
if sessions == nil {
|
|
||||||
return ErrUninitializedDB
|
|
||||||
}
|
|
||||||
|
|
||||||
err = towerSessIndex.ForEach(func(k, _ []byte) error {
|
|
||||||
session, err := getClientSessionBody(
|
|
||||||
sessions, k,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return markSessionStatus(
|
|
||||||
sessions, session, CSessionActive,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// No such tower exists, create a new tower id for our
|
// No such tower exists, create a new tower id for our
|
||||||
// new tower. The error is unhandled since NextSequence
|
// new tower. The error is unhandled since NextSequence
|
||||||
@@ -432,6 +404,7 @@ func (c *ClientDB) CreateTower(lnAddr *lnwire.NetAddress) (*Tower, error) {
|
|||||||
ID: TowerID(towerID),
|
ID: TowerID(towerID),
|
||||||
IdentityKey: lnAddr.IdentityKey,
|
IdentityKey: lnAddr.IdentityKey,
|
||||||
Addresses: []net.Addr{lnAddr.Address},
|
Addresses: []net.Addr{lnAddr.Address},
|
||||||
|
Status: TowerStatusActive,
|
||||||
}
|
}
|
||||||
|
|
||||||
towerIDBytes = tower.ID.Bytes()
|
towerIDBytes = tower.ID.Bytes()
|
||||||
@@ -465,10 +438,10 @@ func (c *ClientDB) CreateTower(lnAddr *lnwire.NetAddress) (*Tower, error) {
|
|||||||
|
|
||||||
// RemoveTower modifies a tower's record within the database. If an address is
|
// RemoveTower modifies a tower's record within the database. If an address is
|
||||||
// provided, then _only_ the address record should be removed from the tower's
|
// provided, then _only_ the address record should be removed from the tower's
|
||||||
// persisted state. Otherwise, we'll attempt to mark the tower as inactive by
|
// persisted state. Otherwise, we'll attempt to mark the tower as inactive. If
|
||||||
// marking all of its sessions inactive. If any of its sessions has unacked
|
// any of its sessions has unacked updates, then ErrTowerUnackedUpdates is
|
||||||
// updates, then ErrTowerUnackedUpdates is returned. If the tower doesn't have
|
// returned. If the tower doesn't have any sessions at all, it'll be completely
|
||||||
// any sessions at all, it'll be completely removed from the database.
|
// removed from the database.
|
||||||
//
|
//
|
||||||
// NOTE: An error is not returned if the tower doesn't exist.
|
// NOTE: An error is not returned if the tower doesn't exist.
|
||||||
func (c *ClientDB) RemoveTower(pubKey *btcec.PublicKey, addr net.Addr) error {
|
func (c *ClientDB) RemoveTower(pubKey *btcec.PublicKey, addr net.Addr) error {
|
||||||
@@ -503,14 +476,14 @@ func (c *ClientDB) RemoveTower(pubKey *btcec.PublicKey, addr net.Addr) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tower, err := getTower(towers, towerIDBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// If an address is provided, then we should _only_ remove the
|
// If an address is provided, then we should _only_ remove the
|
||||||
// address record from the database.
|
// address record from the database.
|
||||||
if addr != nil {
|
if addr != nil {
|
||||||
tower, err := getTower(towers, towerIDBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Towers should always have at least one address saved.
|
// Towers should always have at least one address saved.
|
||||||
tower.RemoveAddress(addr)
|
tower.RemoveAddress(addr)
|
||||||
if len(tower.Addresses) == 0 {
|
if len(tower.Addresses) == 0 {
|
||||||
@@ -560,25 +533,76 @@ func (c *ClientDB) RemoveTower(pubKey *btcec.PublicKey, addr net.Addr) error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll mark its sessions as inactive as long as they don't
|
// Otherwise, we mark the tower as inactive.
|
||||||
// have any pending updates to ensure we don't load them upon
|
tower.Status = TowerStatusInactive
|
||||||
// restarts.
|
err = putTower(towers, tower)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll do a check to ensure that the tower's sessions don't
|
||||||
|
// have any pending back-ups.
|
||||||
for _, session := range towerSessions {
|
for _, session := range towerSessions {
|
||||||
if committedUpdateCount[session.ID] > 0 {
|
if committedUpdateCount[session.ID] > 0 {
|
||||||
return ErrTowerUnackedUpdates
|
return ErrTowerUnackedUpdates
|
||||||
}
|
}
|
||||||
err := markSessionStatus(
|
|
||||||
sessions, session, CSessionInactive,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}, func() {})
|
}, func() {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeactivateTower sets the given tower's status to inactive. This means that
|
||||||
|
// this tower's sessions won't be loaded and used for backups. CreateTower can
|
||||||
|
// be used to reactivate the tower again.
|
||||||
|
func (c *ClientDB) DeactivateTower(pubKey *btcec.PublicKey) error {
|
||||||
|
return kvdb.Update(c.db, func(tx kvdb.RwTx) error {
|
||||||
|
towers := tx.ReadWriteBucket(cTowerBkt)
|
||||||
|
if towers == nil {
|
||||||
|
return ErrUninitializedDB
|
||||||
|
}
|
||||||
|
|
||||||
|
towerIndex := tx.ReadWriteBucket(cTowerIndexBkt)
|
||||||
|
if towerIndex == nil {
|
||||||
|
return ErrUninitializedDB
|
||||||
|
}
|
||||||
|
|
||||||
|
towersToSessionsIndex := tx.ReadWriteBucket(
|
||||||
|
cTowerToSessionIndexBkt,
|
||||||
|
)
|
||||||
|
if towersToSessionsIndex == nil {
|
||||||
|
return ErrUninitializedDB
|
||||||
|
}
|
||||||
|
|
||||||
|
chanIDIndexBkt := tx.ReadBucket(cChanIDIndexBkt)
|
||||||
|
if chanIDIndexBkt == nil {
|
||||||
|
return ErrUninitializedDB
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKeyBytes := pubKey.SerializeCompressed()
|
||||||
|
towerIDBytes := towerIndex.Get(pubKeyBytes)
|
||||||
|
if towerIDBytes == nil {
|
||||||
|
return ErrTowerNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
tower, err := getTower(towers, towerIDBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the tower already has the desired status, then we can exit
|
||||||
|
// here.
|
||||||
|
if tower.Status == TowerStatusInactive {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we update the status and re-store the tower.
|
||||||
|
tower.Status = TowerStatusInactive
|
||||||
|
|
||||||
|
return putTower(towers, tower)
|
||||||
|
}, func() {})
|
||||||
|
}
|
||||||
|
|
||||||
// LoadTowerByID retrieves a tower by its tower ID.
|
// LoadTowerByID retrieves a tower by its tower ID.
|
||||||
func (c *ClientDB) LoadTowerByID(towerID TowerID) (*Tower, error) {
|
func (c *ClientDB) LoadTowerByID(towerID TowerID) (*Tower, error) {
|
||||||
var tower *Tower
|
var tower *Tower
|
||||||
@@ -632,8 +656,14 @@ func (c *ClientDB) LoadTower(pubKey *btcec.PublicKey) (*Tower, error) {
|
|||||||
return tower, nil
|
return tower, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListTowers retrieves the list of towers available within the database.
|
// TowerFilterFn is the signature of a call-back function that can be used to
|
||||||
func (c *ClientDB) ListTowers() ([]*Tower, error) {
|
// skip certain towers in the ListTowers method.
|
||||||
|
type TowerFilterFn func(*Tower) bool
|
||||||
|
|
||||||
|
// ListTowers retrieves the list of towers available within the database that
|
||||||
|
// have a status matching the given status. The filter function may be set in
|
||||||
|
// order to filter out the towers to be returned.
|
||||||
|
func (c *ClientDB) ListTowers(filter TowerFilterFn) ([]*Tower, error) {
|
||||||
var towers []*Tower
|
var towers []*Tower
|
||||||
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
|
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
|
||||||
towerBucket := tx.ReadBucket(cTowerBkt)
|
towerBucket := tx.ReadBucket(cTowerBkt)
|
||||||
@@ -646,7 +676,13 @@ func (c *ClientDB) ListTowers() ([]*Tower, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if filter != nil && !filter(tower) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
towers = append(towers, tower)
|
towers = append(towers, tower)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}, func() {
|
}, func() {
|
||||||
@@ -1787,9 +1823,10 @@ func (c *ClientDB) MarkChannelClosed(chanID lnwire.ChannelID,
|
|||||||
|
|
||||||
// isSessionClosable returns true if a session is considered closable. A session
|
// isSessionClosable returns true if a session is considered closable. A session
|
||||||
// is considered closable only if all the following points are true:
|
// is considered closable only if all the following points are true:
|
||||||
// 1) It has no un-acked updates.
|
// 1. It has no un-acked updates.
|
||||||
// 2) It is exhausted (ie it can't accept any more updates)
|
// 2. It is exhausted (ie it can't accept any more updates) OR it has been
|
||||||
// 3) All the channels that it has acked updates for are closed.
|
// marked as terminal.
|
||||||
|
// 3. All the channels that it has acked updates for are closed.
|
||||||
func isSessionClosable(sessionsBkt, chanDetailsBkt, chanIDIndexBkt kvdb.RBucket,
|
func isSessionClosable(sessionsBkt, chanDetailsBkt, chanIDIndexBkt kvdb.RBucket,
|
||||||
id *SessionID) (bool, error) {
|
id *SessionID) (bool, error) {
|
||||||
|
|
||||||
@@ -1798,22 +1835,21 @@ func isSessionClosable(sessionsBkt, chanDetailsBkt, chanIDIndexBkt kvdb.RBucket,
|
|||||||
return false, ErrSessionNotFound
|
return false, ErrSessionNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since the DeleteCommittedUpdates method deletes the cSessionCommits
|
||||||
|
// bucket in one go, it is possible for the session to be closable even
|
||||||
|
// if this bucket no longer exists.
|
||||||
commitsBkt := sessBkt.NestedReadBucket(cSessionCommits)
|
commitsBkt := sessBkt.NestedReadBucket(cSessionCommits)
|
||||||
if commitsBkt == nil {
|
if commitsBkt != nil {
|
||||||
// If the session has no cSessionCommits bucket then we can be
|
// If the session has any un-acked updates, then it is not yet
|
||||||
// sure that no updates have ever been committed to the session
|
// closable.
|
||||||
// and so it is not yet exhausted.
|
err := commitsBkt.ForEach(func(_, _ []byte) error {
|
||||||
return false, nil
|
return ErrSessionHasUnackedUpdates
|
||||||
}
|
})
|
||||||
|
if errors.Is(err, ErrSessionHasUnackedUpdates) {
|
||||||
// If the session has any un-acked updates, then it is not yet closable.
|
return false, nil
|
||||||
err := commitsBkt.ForEach(func(_, _ []byte) error {
|
} else if err != nil {
|
||||||
return errSessionHasUnackedUpdates
|
return false, err
|
||||||
})
|
}
|
||||||
if errors.Is(err, errSessionHasUnackedUpdates) {
|
|
||||||
return false, nil
|
|
||||||
} else if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
session, err := getClientSessionBody(sessionsBkt, id[:])
|
session, err := getClientSessionBody(sessionsBkt, id[:])
|
||||||
@@ -1821,10 +1857,14 @@ func isSessionClosable(sessionsBkt, chanDetailsBkt, chanIDIndexBkt kvdb.RBucket,
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isTerminal := session.Status == CSessionTerminal
|
||||||
|
|
||||||
// We have already checked that the session has no more committed
|
// We have already checked that the session has no more committed
|
||||||
// updates. So now we can check if the session is exhausted.
|
// updates. So now we can check if the session is exhausted or has a
|
||||||
if session.SeqNum < session.Policy.MaxUpdates {
|
// terminal state.
|
||||||
// If the session is not yet exhausted, it is not yet closable.
|
if !isTerminal && session.SeqNum < session.Policy.MaxUpdates {
|
||||||
|
// If the session is not yet exhausted, and it is not yet in a
|
||||||
|
// terminal state then it is not yet closable.
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1844,11 +1884,16 @@ func isSessionClosable(sessionsBkt, chanDetailsBkt, chanIDIndexBkt kvdb.RBucket,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the session has no acked-updates, then something is wrong since
|
|
||||||
// the above check ensures that this session has been exhausted meaning
|
|
||||||
// that it should have MaxUpdates acked updates.
|
|
||||||
ackedRangeBkt := sessBkt.NestedReadBucket(cSessionAckRangeIndex)
|
ackedRangeBkt := sessBkt.NestedReadBucket(cSessionAckRangeIndex)
|
||||||
if ackedRangeBkt == nil {
|
if ackedRangeBkt == nil {
|
||||||
|
if isTerminal {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the session has no acked-updates, and it is not in a
|
||||||
|
// terminal state then something is wrong since the above check
|
||||||
|
// ensures that this session has been exhausted meaning that it
|
||||||
|
// should have MaxUpdates acked updates.
|
||||||
return false, fmt.Errorf("no acked-updates found for "+
|
return false, fmt.Errorf("no acked-updates found for "+
|
||||||
"exhausted session %s", id)
|
"exhausted session %s", id)
|
||||||
}
|
}
|
||||||
@@ -1996,7 +2041,6 @@ func (c *ClientDB) CommitUpdate(id *SessionID,
|
|||||||
lastApplied = session.TowerLastApplied
|
lastApplied = session.TowerLastApplied
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}, func() {
|
}, func() {
|
||||||
lastApplied = 0
|
lastApplied = 0
|
||||||
})
|
})
|
||||||
@@ -2215,9 +2259,56 @@ func (c *ClientDB) GetDBQueue(namespace []byte) Queue[*BackupID] {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCommittedUpdate deletes the committed update with the given sequence
|
// TerminateSession sets the given session's status to CSessionTerminal meaning
|
||||||
// number from the given session.
|
// that it will not be usable again. An error will be returned if the given
|
||||||
func (c *ClientDB) DeleteCommittedUpdate(id *SessionID, seqNum uint16) error {
|
// session still has un-acked updates that should be attended to.
|
||||||
|
func (c *ClientDB) TerminateSession(id SessionID) error {
|
||||||
|
return kvdb.Update(c.db, func(tx kvdb.RwTx) error {
|
||||||
|
sessions := tx.ReadWriteBucket(cSessionBkt)
|
||||||
|
if sessions == nil {
|
||||||
|
return ErrUninitializedDB
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionsBkt := tx.ReadBucket(cSessionBkt)
|
||||||
|
if sessionsBkt == nil {
|
||||||
|
return ErrUninitializedDB
|
||||||
|
}
|
||||||
|
|
||||||
|
chanIDIndexBkt := tx.ReadBucket(cChanIDIndexBkt)
|
||||||
|
if chanIDIndexBkt == nil {
|
||||||
|
return ErrUninitializedDB
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect any un-acked updates for this session.
|
||||||
|
committedUpdateCount := make(map[SessionID]uint16)
|
||||||
|
perCommittedUpdate := func(s *ClientSession,
|
||||||
|
_ *CommittedUpdate) {
|
||||||
|
|
||||||
|
committedUpdateCount[s.ID]++
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err := c.getClientSession(
|
||||||
|
sessionsBkt, chanIDIndexBkt, id[:],
|
||||||
|
WithPerCommittedUpdate(perCommittedUpdate),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are any un-acked updates for this session then
|
||||||
|
// we don't allow the change of status as these updates must
|
||||||
|
// first be dealt with somehow.
|
||||||
|
if committedUpdateCount[id] > 0 {
|
||||||
|
return ErrSessionHasUnackedUpdates
|
||||||
|
}
|
||||||
|
|
||||||
|
return markSessionStatus(sessions, session, CSessionTerminal)
|
||||||
|
}, func() {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCommittedUpdates deletes all the committed updates for the given
|
||||||
|
// session.
|
||||||
|
func (c *ClientDB) DeleteCommittedUpdates(id *SessionID) error {
|
||||||
return kvdb.Update(c.db, func(tx kvdb.RwTx) error {
|
return kvdb.Update(c.db, func(tx kvdb.RwTx) error {
|
||||||
sessions := tx.ReadWriteBucket(cSessionBkt)
|
sessions := tx.ReadWriteBucket(cSessionBkt)
|
||||||
if sessions == nil {
|
if sessions == nil {
|
||||||
@@ -2231,23 +2322,54 @@ func (c *ClientDB) DeleteCommittedUpdate(id *SessionID, seqNum uint16) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the commits sub-bucket doesn't exist, there can't possibly
|
// If the commits sub-bucket doesn't exist, there can't possibly
|
||||||
// be a corresponding update to remove.
|
// be corresponding updates to remove.
|
||||||
sessionCommits := sessionBkt.NestedReadWriteBucket(
|
sessionCommits := sessionBkt.NestedReadWriteBucket(
|
||||||
cSessionCommits,
|
cSessionCommits,
|
||||||
)
|
)
|
||||||
if sessionCommits == nil {
|
if sessionCommits == nil {
|
||||||
return ErrCommittedUpdateNotFound
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var seqNumBuf [2]byte
|
// errFoundUpdates is an error we will use to exit early from
|
||||||
byteOrder.PutUint16(seqNumBuf[:], seqNum)
|
// the ForEach loop. The return of this error means that at
|
||||||
|
// least one committed update exists.
|
||||||
|
var errFoundUpdates = fmt.Errorf("found committed updates")
|
||||||
|
err := sessionCommits.ForEach(func(k, v []byte) error {
|
||||||
|
return errFoundUpdates
|
||||||
|
})
|
||||||
|
switch {
|
||||||
|
// If the errFoundUpdates signal error was returned then there
|
||||||
|
// are some updates that need to be deleted.
|
||||||
|
case errors.Is(err, errFoundUpdates):
|
||||||
|
|
||||||
if sessionCommits.Get(seqNumBuf[:]) == nil {
|
// If no error is returned then the ForEach call back was never
|
||||||
return ErrCommittedUpdateNotFound
|
// entered meaning that there are no un-acked committed updates.
|
||||||
|
// So we can exit now as there is nothing left to do.
|
||||||
|
case err == nil:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// If an expected error is returned, return that error.
|
||||||
|
default:
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the corresponding committed update.
|
session, err := getClientSessionBody(sessions, id[:])
|
||||||
return sessionCommits.Delete(seqNumBuf[:])
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once we delete a committed update from the session, the
|
||||||
|
// SeqNum of the session will be incorrect and so the session
|
||||||
|
// should be marked as terminal.
|
||||||
|
session.Status = CSessionTerminal
|
||||||
|
err = putClientSessionBody(sessionBkt, session)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all the committed updates in one go by deleting the
|
||||||
|
// session commits bucket.
|
||||||
|
return sessionBkt.DeleteNestedBucket(cSessionCommits)
|
||||||
}, func() {})
|
}, func() {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,26 @@ func (h *clientDBHarness) createTower(lnAddr *lnwire.NetAddress,
|
|||||||
return tower
|
return tower
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *clientDBHarness) deactivateTower(pubKey *btcec.PublicKey,
|
||||||
|
expErr error) {
|
||||||
|
|
||||||
|
h.t.Helper()
|
||||||
|
|
||||||
|
err := h.db.DeactivateTower(pubKey)
|
||||||
|
require.ErrorIs(h.t, err, expErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *clientDBHarness) listTowers(filterFn wtdb.TowerFilterFn,
|
||||||
|
expErr error) []*wtdb.Tower {
|
||||||
|
|
||||||
|
h.t.Helper()
|
||||||
|
|
||||||
|
towers, err := h.db.ListTowers(filterFn)
|
||||||
|
require.ErrorIs(h.t, err, expErr)
|
||||||
|
|
||||||
|
return towers
|
||||||
|
}
|
||||||
|
|
||||||
func (h *clientDBHarness) removeTower(pubKey *btcec.PublicKey, addr net.Addr,
|
func (h *clientDBHarness) removeTower(pubKey *btcec.PublicKey, addr net.Addr,
|
||||||
hasSessions bool, expErr error) {
|
hasSessions bool, expErr error) {
|
||||||
|
|
||||||
@@ -125,12 +145,9 @@ func (h *clientDBHarness) removeTower(pubKey *btcec.PublicKey, addr net.Addr,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, session := range h.listSessions(&tower.ID) {
|
require.EqualValues(
|
||||||
require.Equal(h.t, wtdb.CSessionInactive,
|
h.t, wtdb.TowerStatusInactive, tower.Status,
|
||||||
session.Status, "expected status for session "+
|
)
|
||||||
"%v to be %v, got %v", session.ID,
|
|
||||||
wtdb.CSessionInactive, session.Status)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +173,24 @@ func (h *clientDBHarness) loadTowerByID(id wtdb.TowerID,
|
|||||||
return tower
|
return tower
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *clientDBHarness) terminateSession(id wtdb.SessionID, expErr error) {
|
||||||
|
h.t.Helper()
|
||||||
|
|
||||||
|
err := h.db.TerminateSession(id)
|
||||||
|
require.ErrorIs(h.t, err, expErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *clientDBHarness) getClientSession(id wtdb.SessionID,
|
||||||
|
expErr error) *wtdb.ClientSession {
|
||||||
|
|
||||||
|
h.t.Helper()
|
||||||
|
|
||||||
|
session, err := h.db.GetClientSession(id)
|
||||||
|
require.ErrorIs(h.t, err, expErr)
|
||||||
|
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
|
||||||
func (h *clientDBHarness) fetchChanInfos() wtdb.ChannelInfos {
|
func (h *clientDBHarness) fetchChanInfos() wtdb.ChannelInfos {
|
||||||
h.t.Helper()
|
h.t.Helper()
|
||||||
|
|
||||||
@@ -194,12 +229,12 @@ func (h *clientDBHarness) ackUpdate(id *wtdb.SessionID, seqNum uint16,
|
|||||||
require.ErrorIs(h.t, err, expErr)
|
require.ErrorIs(h.t, err, expErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *clientDBHarness) deleteCommittedUpdate(id *wtdb.SessionID,
|
func (h *clientDBHarness) deleteCommittedUpdates(id *wtdb.SessionID,
|
||||||
seqNum uint16, expErr error) {
|
expErr error) {
|
||||||
|
|
||||||
h.t.Helper()
|
h.t.Helper()
|
||||||
|
|
||||||
err := h.db.DeleteCommittedUpdate(id, seqNum)
|
err := h.db.DeleteCommittedUpdates(id)
|
||||||
require.ErrorIs(h.t, err, expErr)
|
require.ErrorIs(h.t, err, expErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,20 +566,126 @@ func testRemoveTower(h *clientDBHarness) {
|
|||||||
h.commitUpdate(&session.ID, update, nil)
|
h.commitUpdate(&session.ID, update, nil)
|
||||||
|
|
||||||
// We should not be able to fully remove it from the database since
|
// We should not be able to fully remove it from the database since
|
||||||
// there's a session and it has unacked updates.
|
// there's a session, and it has unacked updates.
|
||||||
h.removeTower(pk, nil, true, wtdb.ErrTowerUnackedUpdates)
|
h.removeTower(pk, nil, true, wtdb.ErrTowerUnackedUpdates)
|
||||||
|
|
||||||
// Removing the tower after all sessions no longer have unacked updates
|
// Removing the tower after all sessions no longer have unacked updates
|
||||||
// should result in the sessions becoming inactive.
|
// should succeed.
|
||||||
h.ackUpdate(&session.ID, 1, 1, nil)
|
h.ackUpdate(&session.ID, 1, 1, nil)
|
||||||
h.removeTower(pk, nil, true, nil)
|
h.removeTower(pk, nil, true, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// Creating the tower again should mark all of the sessions active once
|
func testTerminateSession(h *clientDBHarness) {
|
||||||
// again.
|
const blobType = blob.TypeAltruistCommit
|
||||||
h.createTower(&lnwire.NetAddress{
|
|
||||||
|
tower := h.newTower()
|
||||||
|
|
||||||
|
// Create a new session that the updates in this will be tied to.
|
||||||
|
session := &wtdb.ClientSession{
|
||||||
|
ClientSessionBody: wtdb.ClientSessionBody{
|
||||||
|
TowerID: tower.ID,
|
||||||
|
Policy: wtpolicy.Policy{
|
||||||
|
TxPolicy: wtpolicy.TxPolicy{
|
||||||
|
BlobType: blobType,
|
||||||
|
},
|
||||||
|
MaxUpdates: 100,
|
||||||
|
},
|
||||||
|
RewardPkScript: []byte{0x01, 0x02, 0x03},
|
||||||
|
},
|
||||||
|
ID: wtdb.SessionID([33]byte{0x03}),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserve a session key and insert the client session.
|
||||||
|
session.KeyIndex = h.nextKeyIndex(session.TowerID, blobType, false)
|
||||||
|
h.insertSession(session, nil)
|
||||||
|
|
||||||
|
// Commit to a random update at seqnum 1.
|
||||||
|
update1 := randCommittedUpdate(h.t, 1)
|
||||||
|
h.registerChan(update1.BackupID.ChanID, nil, nil)
|
||||||
|
lastApplied := h.commitUpdate(&session.ID, update1, nil)
|
||||||
|
require.Zero(h.t, lastApplied)
|
||||||
|
|
||||||
|
// Terminating the session now should fail since the session has an
|
||||||
|
// un-acked update.
|
||||||
|
h.terminateSession(session.ID, wtdb.ErrSessionHasUnackedUpdates)
|
||||||
|
|
||||||
|
// Fetch the session and assert that the status is still active.
|
||||||
|
sess := h.getClientSession(session.ID, nil)
|
||||||
|
require.Equal(h.t, wtdb.CSessionActive, sess.Status)
|
||||||
|
|
||||||
|
// Delete the update.
|
||||||
|
h.deleteCommittedUpdates(&session.ID, nil)
|
||||||
|
|
||||||
|
// Terminating the session now should succeed.
|
||||||
|
h.terminateSession(session.ID, nil)
|
||||||
|
|
||||||
|
// Fetch the session again and assert that its status is now Terminal.
|
||||||
|
sess = h.getClientSession(session.ID, nil)
|
||||||
|
require.Equal(h.t, wtdb.CSessionTerminal, sess.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// testTowerStatusChange tests that the Tower status is updated accordingly
|
||||||
|
// given a variety of commands.
|
||||||
|
func testTowerStatusChange(h *clientDBHarness) {
|
||||||
|
// Create a new tower.
|
||||||
|
pk, err := randPubKey()
|
||||||
|
require.NoError(h.t, err)
|
||||||
|
|
||||||
|
towerAddr := &lnwire.NetAddress{
|
||||||
IdentityKey: pk,
|
IdentityKey: pk,
|
||||||
Address: addr1,
|
Address: &net.TCPAddr{
|
||||||
}, nil)
|
IP: []byte{0x01, 0x00, 0x00, 0x00}, Port: 9911,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tower := h.createTower(towerAddr, nil)
|
||||||
|
|
||||||
|
// Add a new session.
|
||||||
|
session := h.randSession(h.t, tower.ID, 100)
|
||||||
|
h.insertSession(session, nil)
|
||||||
|
|
||||||
|
// assertTowerStatus is a helper function that will assert that the
|
||||||
|
// tower's status is as expected.
|
||||||
|
assertTowerStatus := func(status wtdb.TowerStatus) {
|
||||||
|
activeFilter := func(tower *wtdb.Tower) bool {
|
||||||
|
return tower.Status == status
|
||||||
|
}
|
||||||
|
|
||||||
|
towers := h.listTowers(activeFilter, nil)
|
||||||
|
require.Len(h.t, towers, 1)
|
||||||
|
require.EqualValues(h.t, towers[0].Status, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertSessionStatus is a helper that will assert that the session's
|
||||||
|
// status is as expected
|
||||||
|
assertSessionStatus := func(status wtdb.CSessionStatus) {
|
||||||
|
sessions := h.listSessions(&tower.ID)
|
||||||
|
require.Len(h.t, sessions, 1)
|
||||||
|
for _, sess := range sessions {
|
||||||
|
require.EqualValues(h.t, sess.Status, status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initially, the tower and session should be active.
|
||||||
|
assertTowerStatus(wtdb.TowerStatusActive)
|
||||||
|
assertSessionStatus(wtdb.CSessionActive)
|
||||||
|
|
||||||
|
// Removing the tower should change its status but its session
|
||||||
|
// status should remain active.
|
||||||
|
h.removeTower(tower.IdentityKey, nil, true, nil)
|
||||||
|
assertTowerStatus(wtdb.TowerStatusInactive)
|
||||||
|
assertSessionStatus(wtdb.CSessionActive)
|
||||||
|
|
||||||
|
// Re-adding the tower in some way should re-active it and its session.
|
||||||
|
h.createTower(towerAddr, nil)
|
||||||
|
assertTowerStatus(wtdb.TowerStatusActive)
|
||||||
|
assertSessionStatus(wtdb.CSessionActive)
|
||||||
|
|
||||||
|
// Deactivating the tower should change its status but its session
|
||||||
|
// status should remain active.
|
||||||
|
h.deactivateTower(tower.IdentityKey, nil)
|
||||||
|
assertTowerStatus(wtdb.TowerStatusInactive)
|
||||||
|
assertSessionStatus(wtdb.CSessionActive)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testChanSummaries tests the process of a registering a channel and its
|
// testChanSummaries tests the process of a registering a channel and its
|
||||||
@@ -660,18 +801,7 @@ func testCommitUpdate(h *clientDBHarness) {
|
|||||||
|
|
||||||
// We will now also test that the DeleteCommittedUpdates method also
|
// We will now also test that the DeleteCommittedUpdates method also
|
||||||
// works.
|
// works.
|
||||||
// First, try to delete a committed update that does not exist.
|
h.deleteCommittedUpdates(&session.ID, nil)
|
||||||
h.deleteCommittedUpdate(
|
|
||||||
&session.ID, update4.SeqNum, wtdb.ErrCommittedUpdateNotFound,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Now delete an existing committed update and ensure that it succeeds.
|
|
||||||
h.deleteCommittedUpdate(&session.ID, update1.SeqNum, nil)
|
|
||||||
h.assertUpdates(session.ID, []wtdb.CommittedUpdate{
|
|
||||||
*update2,
|
|
||||||
}, nil)
|
|
||||||
|
|
||||||
h.deleteCommittedUpdate(&session.ID, update2.SeqNum, nil)
|
|
||||||
h.assertUpdates(session.ID, []wtdb.CommittedUpdate{}, nil)
|
h.assertUpdates(session.ID, []wtdb.CommittedUpdate{}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -929,6 +1059,44 @@ func testMarkChannelClosed(h *clientDBHarness) {
|
|||||||
// Assert that we now can delete the session.
|
// Assert that we now can delete the session.
|
||||||
h.deleteSession(session1.ID, nil)
|
h.deleteSession(session1.ID, nil)
|
||||||
require.Empty(h.t, h.listClosableSessions(nil))
|
require.Empty(h.t, h.listClosableSessions(nil))
|
||||||
|
|
||||||
|
// We also want to test that a session can be deleted if it has been
|
||||||
|
// marked as terminal.
|
||||||
|
|
||||||
|
// Create session2 with MaxUpdates set to 5.
|
||||||
|
session2 := h.randSession(h.t, tower.ID, 5)
|
||||||
|
h.insertSession(session2, nil)
|
||||||
|
|
||||||
|
// Create and register channel 7.
|
||||||
|
chanID7 := randChannelID(h.t)
|
||||||
|
h.registerChan(chanID7, nil, nil)
|
||||||
|
|
||||||
|
// Add two updates for channel 7 in session 2. Ack one of them so that
|
||||||
|
// the mapping from this channel to this session is created but don't
|
||||||
|
// ack the other since we want to delete the committed update later.
|
||||||
|
update = randCommittedUpdateForChannel(h.t, chanID7, 1)
|
||||||
|
h.commitUpdate(&session2.ID, update, nil)
|
||||||
|
h.ackUpdate(&session2.ID, 1, 1, nil)
|
||||||
|
|
||||||
|
update = randCommittedUpdateForChannel(h.t, chanID7, 2)
|
||||||
|
h.commitUpdate(&session2.ID, update, nil)
|
||||||
|
|
||||||
|
// Check that attempting to delete the session will fail since it is not
|
||||||
|
// yet considered closable.
|
||||||
|
h.deleteSession(session2.ID, wtdb.ErrSessionNotClosable)
|
||||||
|
|
||||||
|
// Now delete the added committed updates. This should put the
|
||||||
|
// session in the terminal state after which we should be able to
|
||||||
|
// delete the session.
|
||||||
|
h.deleteCommittedUpdates(&session2.ID, nil)
|
||||||
|
|
||||||
|
// Marking channel 7 as closed will now return session 2 since it has
|
||||||
|
// been marked as terminal.
|
||||||
|
sl = h.markChannelClosed(chanID7, 1, nil)
|
||||||
|
require.ElementsMatch(h.t, sl, []wtdb.SessionID{session2.ID})
|
||||||
|
|
||||||
|
// We should now be able to delete the session.
|
||||||
|
h.deleteSession(session2.ID, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testAckUpdate asserts the behavior of AckUpdate.
|
// testAckUpdate asserts the behavior of AckUpdate.
|
||||||
@@ -1153,6 +1321,14 @@ func TestClientDB(t *testing.T) {
|
|||||||
name: "max commitment heights",
|
name: "max commitment heights",
|
||||||
run: testMaxCommitmentHeights,
|
run: testMaxCommitmentHeights,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "test tower status change",
|
||||||
|
run: testTowerStatusChange,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "terminate session",
|
||||||
|
run: testTerminateSession,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, database := range dbs {
|
for _, database := range dbs {
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ const (
|
|||||||
// used for backups.
|
// used for backups.
|
||||||
CSessionActive CSessionStatus = 0
|
CSessionActive CSessionStatus = 0
|
||||||
|
|
||||||
// CSessionInactive indicates that the ClientSession is inactive and
|
// CSessionTerminal indicates that the ClientSession is in a terminal
|
||||||
// cannot be used for backups.
|
// state and cannot be used for backups.
|
||||||
CSessionInactive CSessionStatus = 1
|
CSessionTerminal CSessionStatus = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClientSession encapsulates a SessionInfo returned from a successful
|
// ClientSession encapsulates a SessionInfo returned from a successful
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ func TestCodec(tt *testing.T) {
|
|||||||
obj := wtdb.Tower{
|
obj := wtdb.Tower{
|
||||||
IdentityKey: pk,
|
IdentityKey: pk,
|
||||||
Addresses: addrs,
|
Addresses: addrs,
|
||||||
|
Status: wtdb.TowerStatus(r.Uint32()),
|
||||||
}
|
}
|
||||||
|
|
||||||
v[0] = reflect.ValueOf(obj)
|
v[0] = reflect.ValueOf(obj)
|
||||||
|
|||||||
@@ -7,6 +7,26 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TowerStatus represents the state of the tower as set by the tower client.
|
||||||
|
type TowerStatus uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TowerStatusActive is the default state of the tower, and it indicates
|
||||||
|
// that this tower should be used to attempt session creation.
|
||||||
|
TowerStatusActive TowerStatus = 0
|
||||||
|
|
||||||
|
// TowerStatusInactive indicates that the tower should not be used to
|
||||||
|
// attempt session creation.
|
||||||
|
TowerStatusInactive TowerStatus = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TowerStatusTLVType is the TLV type number that will be used to store
|
||||||
|
// the tower's status.
|
||||||
|
TowerStatusTLVType = tlv.Type(0)
|
||||||
)
|
)
|
||||||
|
|
||||||
// TowerID is a unique 64-bit identifier allocated to each unique watchtower.
|
// TowerID is a unique 64-bit identifier allocated to each unique watchtower.
|
||||||
@@ -41,6 +61,9 @@ type Tower struct {
|
|||||||
|
|
||||||
// Addresses is a list of possible addresses to reach the tower.
|
// Addresses is a list of possible addresses to reach the tower.
|
||||||
Addresses []net.Addr
|
Addresses []net.Addr
|
||||||
|
|
||||||
|
// Status is the status of this tower as set by the client.
|
||||||
|
Status TowerStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddAddress adds the given address to the tower's in-memory list of addresses.
|
// AddAddress adds the given address to the tower's in-memory list of addresses.
|
||||||
@@ -88,17 +111,56 @@ func (t *Tower) String() string {
|
|||||||
// Encode writes the Tower to the passed io.Writer. The TowerID is not
|
// Encode writes the Tower to the passed io.Writer. The TowerID is not
|
||||||
// serialized, since it acts as the key.
|
// serialized, since it acts as the key.
|
||||||
func (t *Tower) Encode(w io.Writer) error {
|
func (t *Tower) Encode(w io.Writer) error {
|
||||||
return WriteElements(w,
|
err := WriteElements(w,
|
||||||
t.IdentityKey,
|
t.IdentityKey,
|
||||||
t.Addresses,
|
t.Addresses,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
status := uint8(t.Status)
|
||||||
|
tlvRecords := []tlv.Record{
|
||||||
|
tlv.MakePrimitiveRecord(TowerStatusTLVType, &status),
|
||||||
|
}
|
||||||
|
|
||||||
|
tlvStream, err := tlv.NewStream(tlvRecords...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlvStream.Encode(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode reads a Tower from the passed io.Reader. The TowerID is meant to be
|
// Decode reads a Tower from the passed io.Reader. The TowerID is meant to be
|
||||||
// decoded from the key.
|
// decoded from the key.
|
||||||
func (t *Tower) Decode(r io.Reader) error {
|
func (t *Tower) Decode(r io.Reader) error {
|
||||||
return ReadElements(r,
|
err := ReadElements(r,
|
||||||
&t.IdentityKey,
|
&t.IdentityKey,
|
||||||
&t.Addresses,
|
&t.Addresses,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var status uint8
|
||||||
|
tlvRecords := []tlv.Record{
|
||||||
|
tlv.MakePrimitiveRecord(TowerStatusTLVType, &status),
|
||||||
|
}
|
||||||
|
|
||||||
|
tlvStream, err := tlv.NewStream(tlvRecords...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
typeMap, err := tlvStream.DecodeWithParsedTypes(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := typeMap[TowerStatusTLVType]; ok {
|
||||||
|
t.Status = TowerStatus(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user