diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 429b09171..c025f0d2d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ env: # /dev.Dockerfile # /make/builder.Dockerfile # /.github/workflows/release.yml - GO_VERSION: 1.21.0 + GO_VERSION: 1.22.3 jobs: ######################## @@ -225,6 +225,7 @@ jobs: if: matrix.unit_type == 'btcd unit-cover' with: path-to-profile: coverage.txt + flag-name: 'unit' parallel: true @@ -241,13 +242,13 @@ jobs: matrix: include: - name: btcd - args: backend=btcd + args: backend=btcd cover=1 - name: bitcoind - args: backend=bitcoind + args: backend=bitcoind cover=1 - name: bitcoind-notxindex args: backend="bitcoind notxindex" - name: bitcoind-rpcpolling - args: backend="bitcoind rpcpolling" + args: backend="bitcoind rpcpolling" cover=1 - name: bitcoind-etcd args: backend=bitcoind dbbackend=etcd - name: bitcoind-postgres @@ -259,7 +260,7 @@ jobs: - name: bitcoind-sqlite-nativesql args: backend=bitcoind dbbackend=sqlite nativesql=true - name: neutrino - args: backend=neutrino + args: backend=neutrino cover=1 steps: - name: git checkout uses: actions/checkout@v3 @@ -276,6 +277,14 @@ jobs: - name: run ${{ matrix.name }} run: make itest-parallel ${{ matrix.args }} + - name: Send coverage + if: ${{ contains(matrix.args, 'cover=1') }} + uses: shogo82148/actions-goveralls@v1 + with: + path-to-profile: coverage.txt + flag-name: 'itest-${{ matrix.name }}' + parallel: true + - name: Zip log files on failure if: ${{ failure() }} timeout-minutes: 5 # timeout after 5 minute @@ -401,3 +410,13 @@ jobs: - name: release notes check run: scripts/check-release-notes.sh + + # Notify about the completion of all coverage collecting jobs. + finish: + if: ${{ always() }} + needs: [unit-test, ubuntu-integration-test] + runs-on: ubuntu-latest + steps: + - uses: shogo82148/actions-goveralls@v1 + with: + parallel-finished: true diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e565611c5..4c4cc5d60 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -16,7 +16,7 @@ env: # /dev.Dockerfile # /make/builder.Dockerfile # /.github/workflows/main.yml - GO_VERSION: 1.21.0 + GO_VERSION: 1.22.3 jobs: main: diff --git a/.gitignore b/.gitignore index 16d33c142..a44a7b8f5 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ itest/.minerlogs itest/lnd-itest itest/btcd-itest itest/.logs-* +itest/cover cmd/cmd *.key diff --git a/Dockerfile b/Dockerfile index c35ed81bf..d6e8a82c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ # /make/builder.Dockerfile # /.github/workflows/main.yml # /.github/workflows/release.yml -FROM golang:1.21.0-alpine as builder +FROM golang:1.22.3-alpine as builder # Force Go to use the cgo based DNS resolver. This is required to ensure DNS # queries required to connect to linked containers succeed. diff --git a/Makefile b/Makefile index 41fabbfcc..5ea087892 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,7 @@ DOCKER_TOOLS = docker run \ --rm \ -v $(shell bash -c "go env GOCACHE || (mkdir -p /tmp/go-cache; echo /tmp/go-cache)"):/tmp/build/.cache \ -v $(shell bash -c "go env GOMODCACHE || (mkdir -p /tmp/go-modcache; echo /tmp/go-modcache)"):/tmp/build/.modcache \ + -v $(shell bash -c "mkdir -p /tmp/go-lint-cache; echo /tmp/go-lint-cache"):/root/.cache/golangci-lint \ -v $$(pwd):/build lnd-tools GREEN := "\\033[0;32m" @@ -110,7 +111,7 @@ build: build-itest: @$(call print, "Building itest btcd and lnd.") CGO_ENABLED=0 $(GOBUILD) -tags="integration" -o itest/btcd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(BTCD_PKG) - CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" -o itest/lnd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(PKG)/cmd/lnd + CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" $(ITEST_COVERAGE) -o itest/lnd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(PKG)/cmd/lnd @$(call print, "Building itest binary for ${backend} backend.") CGO_ENABLED=0 $(GOTEST) -v ./itest -tags="$(DEV_TAGS) $(RPC_TAGS) integration $(backend)" -c -o itest/itest.test$(EXEC_SUFFIX) @@ -135,8 +136,13 @@ manpages: @$(call print, "Generating man pages lncli.1 and lnd.1.") ./scripts/gen_man_pages.sh $(DESTDIR) $(PREFIX) -#? install: Build and install lnd and lncli binaries, place them in $GOPATH/bin, generate and install man pages -install: install-binaries manpages +#? install: Build and install lnd and lncli binaries and place them in $GOPATH/bin. +install: install-binaries + +#? install-all: Performs all the same tasks as the install command along with generating and +# installing the man pages for the lnd and lncli binaries. This command is useful in an +# environment where a user has root access and so has write access to the man page directory. +install-all: install manpages #? release-install: Build and install lnd and lncli release binaries, place them in $GOPATH/bin release-install: @@ -197,6 +203,7 @@ itest-only: db-instance @$(call print, "Running integration tests with ${backend} backend.") rm -rf itest/*.log itest/.logs-*; date EXEC_SUFFIX=$(EXEC_SUFFIX) scripts/itest_part.sh 0 1 $(TEST_FLAGS) $(ITEST_FLAGS) + $(COLLECT_ITEST_COVERAGE) #? itest: Build and run integration tests itest: build-itest itest-only @@ -209,6 +216,7 @@ itest-parallel: build-itest db-instance @$(call print, "Running tests") rm -rf itest/*.log itest/.logs-*; date EXEC_SUFFIX=$(EXEC_SUFFIX) scripts/itest_parallel.sh $(ITEST_PARALLELISM) $(NUM_ITEST_TRANCHES) $(TEST_FLAGS) $(ITEST_FLAGS) + $(COLLECT_ITEST_COVERAGE) #? itest-clean: Kill all running itest processes itest-clean: diff --git a/aezeed/bench_test.go b/aezeed/bench_test.go index c54c17944..3fa0edb21 100644 --- a/aezeed/bench_test.go +++ b/aezeed/bench_test.go @@ -11,7 +11,7 @@ var ( seed *CipherSeed ) -// BenchmarkFrommnemonic benchmarks the process of converting a cipher seed +// BenchmarkTomnemonic benchmarks the process of converting a cipher seed // (given the salt), to an enciphered mnemonic. func BenchmarkTomnemonic(b *testing.B) { scryptN = 32768 diff --git a/autopilot/agent_test.go b/autopilot/agent_test.go index 758401868..39e86906e 100644 --- a/autopilot/agent_test.go +++ b/autopilot/agent_test.go @@ -254,7 +254,7 @@ func respondMoreChans(t *testing.T, testCtx *testContext, resp moreChansResp) { } } -// respondMoreChans consumes the nodeScoresArgs element and responds to the +// respondNodeScores consumes the nodeScoresArgs element and responds to the // agent with the given node scores. func respondNodeScores(t *testing.T, testCtx *testContext, resp map[NodeID]*NodeScore) { @@ -468,7 +468,7 @@ func TestAgentChannelCloseSignal(t *testing.T) { } } -// TestAgentBalanceUpdateIncrease ensures that once the agent receives an +// TestAgentBalanceUpdate ensures that once the agent receives an // outside signal concerning a balance update, then it will re-query the // heuristic to determine its next action. func TestAgentBalanceUpdate(t *testing.T) { diff --git a/autopilot/choice_test.go b/autopilot/choice_test.go index bde48b0da..c08d87a35 100644 --- a/autopilot/choice_test.go +++ b/autopilot/choice_test.go @@ -21,7 +21,7 @@ func TestWeightedChoiceEmptyMap(t *testing.T) { } } -// singeNonZero is a type used to generate float64 slices with one non-zero +// singleNonZero is a type used to generate float64 slices with one non-zero // element. type singleNonZero []float64 diff --git a/build/version.go b/build/version.go index 77d62d22c..d3beca8a0 100644 --- a/build/version.go +++ b/build/version.go @@ -40,14 +40,14 @@ const ( AppMajor uint = 0 // AppMinor defines the minor version of this binary. - AppMinor uint = 17 + AppMinor uint = 18 // AppPatch defines the application patch for this binary. - AppPatch uint = 99 + AppPatch uint = 00 // AppPreRelease MUST only contain characters from semanticAlphabet per // the semantic versioning spec. - AppPreRelease = "beta" + AppPreRelease = "beta.rc3" ) func init() { diff --git a/cert/selfsigned.go b/cert/selfsigned.go index c956afb00..80fe42106 100644 --- a/cert/selfsigned.go +++ b/cert/selfsigned.go @@ -9,7 +9,6 @@ import ( "crypto/x509/pkix" "encoding/pem" "fmt" - "io/ioutil" "math/big" "net" "os" @@ -295,14 +294,14 @@ func GenCertPair(org string, tlsExtraIPs, tlsExtraDomains []string, func WriteCertPair(certFile, keyFile string, certBytes, keyBytes []byte) error { // Write cert and key files. if certFile != "" { - err := ioutil.WriteFile(certFile, certBytes, 0644) + err := os.WriteFile(certFile, certBytes, 0644) if err != nil { return err } } if keyFile != "" { - err := ioutil.WriteFile(keyFile, keyBytes, 0600) + err := os.WriteFile(keyFile, keyBytes, 0600) if err != nil { os.Remove(certFile) return err diff --git a/cert/selfsigned_test.go b/cert/selfsigned_test.go index 614225a5a..ca55c2772 100644 --- a/cert/selfsigned_test.go +++ b/cert/selfsigned_test.go @@ -1,7 +1,7 @@ package cert_test import ( - "io/ioutil" + "os" "path/filepath" "testing" "time" @@ -42,10 +42,10 @@ func TestIsOutdatedCert(t *testing.T) { // number of IPs and domains. for numIPs := 1; numIPs <= len(extraIPs); numIPs++ { for numDomains := 1; numDomains <= len(extraDomains); numDomains++ { - certBytes, err := ioutil.ReadFile(certPath) + certBytes, err := os.ReadFile(certPath) require.NoError(t, err) - keyBytes, err := ioutil.ReadFile(keyPath) + keyBytes, err := os.ReadFile(keyPath) require.NoError(t, err) _, parsedCert, err := cert.LoadCertFromBytes( @@ -98,10 +98,10 @@ func TestIsOutdatedPermutation(t *testing.T) { err = cert.WriteCertPair(certPath, keyPath, certBytes, keyBytes) require.NoError(t, err) - certBytes, err = ioutil.ReadFile(certPath) + certBytes, err = os.ReadFile(certPath) require.NoError(t, err) - keyBytes, err = ioutil.ReadFile(keyPath) + keyBytes, err = os.ReadFile(keyPath) require.NoError(t, err) _, parsedCert, err := cert.LoadCertFromBytes(certBytes, keyBytes) @@ -171,10 +171,10 @@ func TestTLSDisableAutofill(t *testing.T) { require.NoError(t, err) // Read certs from disk. - certBytes, err = ioutil.ReadFile(certPath) + certBytes, err = os.ReadFile(certPath) require.NoError(t, err) - keyBytes, err = ioutil.ReadFile(keyPath) + keyBytes, err = os.ReadFile(keyPath) require.NoError(t, err) // Load the certificate. @@ -230,10 +230,10 @@ func TestTLSConfig(t *testing.T) { err = cert.WriteCertPair(certPath, keyPath, certBytes, keyBytes) require.NoError(t, err) - certBytes, err = ioutil.ReadFile(certPath) + certBytes, err = os.ReadFile(certPath) require.NoError(t, err) - keyBytes, err = ioutil.ReadFile(keyPath) + keyBytes, err = os.ReadFile(keyPath) require.NoError(t, err) // Load the certificate. diff --git a/cert/tls.go b/cert/tls.go index 4e5d7c81e..6f60ae198 100644 --- a/cert/tls.go +++ b/cert/tls.go @@ -3,7 +3,7 @@ package cert import ( "crypto/tls" "crypto/x509" - "io/ioutil" + "os" "sync" ) @@ -31,11 +31,11 @@ var ( func GetCertBytesFromPath(certPath, keyPath string) (certBytes, keyBytes []byte, err error) { - certBytes, err = ioutil.ReadFile(certPath) + certBytes, err = os.ReadFile(certPath) if err != nil { return nil, nil, err } - keyBytes, err = ioutil.ReadFile(keyPath) + keyBytes, err = os.ReadFile(keyPath) if err != nil { return nil, nil, err } diff --git a/chainntnfs/txnotifier.go b/chainntnfs/txnotifier.go index f6cd0c1b8..be471da3c 100644 --- a/chainntnfs/txnotifier.go +++ b/chainntnfs/txnotifier.go @@ -772,8 +772,8 @@ func (n *TxNotifier) CancelConf(confRequest ConfRequest, confID uint64) { return } - Log.Infof("Canceling confirmation notification: conf_id=%d, %v", confID, - confRequest) + Log.Debugf("Canceling confirmation notification: conf_id=%d, %v", + confID, confRequest) // We'll close all the notification channels to let the client know // their cancel request has been fulfilled. @@ -925,7 +925,7 @@ func (n *TxNotifier) dispatchConfDetails( // we'll dispatch a confirmation notification to the caller. confHeight := details.BlockHeight + ntfn.NumConfirmations - 1 if confHeight <= n.currentHeight { - Log.Infof("Dispatching %v confirmation notification for %v", + Log.Debugf("Dispatching %v confirmation notification for %v", ntfn.NumConfirmations, ntfn.ConfRequest) // We'll send a 0 value to the Updates channel, @@ -1058,7 +1058,7 @@ func (n *TxNotifier) RegisterSpend(outpoint *wire.OutPoint, pkScript []byte, n.Lock() defer n.Unlock() - Log.Infof("New spend subscription: spend_id=%d, %v, height_hint=%d", + Log.Debugf("New spend subscription: spend_id=%d, %v, height_hint=%d", ntfn.SpendID, ntfn.SpendRequest, startHeight) // Keep track of the notification request so that we can properly @@ -1136,7 +1136,7 @@ func (n *TxNotifier) RegisterSpend(outpoint *wire.OutPoint, pkScript []byte, // notifications don't also attempt a historical dispatch. spendSet.rescanStatus = rescanPending - Log.Infof("Dispatching historical spend rescan for %v, start=%d, "+ + Log.Debugf("Dispatching historical spend rescan for %v, start=%d, "+ "end=%d", ntfn.SpendRequest, startHeight, n.currentHeight) return &SpendRegistration{ @@ -1171,7 +1171,7 @@ func (n *TxNotifier) CancelSpend(spendRequest SpendRequest, spendID uint64) { return } - Log.Infof("Canceling spend notification: spend_id=%d, %v", spendID, + Log.Debugf("Canceling spend notification: spend_id=%d, %v", spendID, spendRequest) // We'll close all the notification channels to let the client know @@ -1364,7 +1364,7 @@ func (n *TxNotifier) dispatchSpendDetails(ntfn *SpendNtfn, details *SpendDetail) return nil } - Log.Infof("Dispatching confirmed spend notification for %v at "+ + Log.Debugf("Dispatching confirmed spend notification for %v at "+ "current height=%d: %v", ntfn.SpendRequest, n.currentHeight, details) @@ -1743,7 +1743,7 @@ func (n *TxNotifier) NotifyHeight(height uint32) error { for ntfn := range n.ntfnsByConfirmHeight[height] { confSet := n.confNotifications[ntfn.ConfRequest] - Log.Infof("Dispatching %v confirmation notification for %v", + Log.Debugf("Dispatching %v confirmation notification for %v", ntfn.NumConfirmations, ntfn.ConfRequest) // The default notification we assigned above includes the diff --git a/chainreg/chainregistry.go b/chainreg/chainregistry.go index 8e0142eec..da1f8e08a 100644 --- a/chainreg/chainregistry.go +++ b/chainreg/chainregistry.go @@ -5,7 +5,7 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "net" "net/url" "os" @@ -86,10 +86,14 @@ type Config struct { // ActiveNetParams details the current chain we are on. ActiveNetParams BitcoinNetParams - // FeeURL defines the URL for fee estimation we will use. This field is - // optional. + // Deprecated: Use Fee.URL. FeeURL defines the URL for fee estimation + // we will use. This field is optional. FeeURL string + // Fee defines settings for the web fee estimator. This field is + // optional. + Fee *lncfg.Fee + // Dialer is a function closure that will be used to establish outbound // TCP connections to Bitcoin peers in the event of a pruned block being // requested. @@ -252,6 +256,16 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) { "cache: %v", err) } + // Map the deprecated feeurl flag to fee.url. + if cfg.FeeURL != "" { + if cfg.Fee.URL != "" { + return nil, nil, errors.New("fee.url and " + + "feeurl are mutually exclusive") + } + + cfg.Fee.URL = cfg.FeeURL + } + // If spv mode is active, then we'll be using a distinct set of // chainControl interfaces that interface directly with the p2p network // of the selected chain. @@ -270,18 +284,6 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) { return nil, nil, err } - // Map the deprecated neutrino feeurl flag to the general fee - // url. - if cfg.NeutrinoMode.FeeURL != "" { - if cfg.FeeURL != "" { - return nil, nil, errors.New("feeurl and " + - "neutrino.feeurl are mutually " + - "exclusive") - } - - cfg.FeeURL = cfg.NeutrinoMode.FeeURL - } - cc.ChainSource = chain.NewNeutrinoClient( cfg.ActiveNetParams.Params, cfg.NeutrinoCS, ) @@ -556,7 +558,7 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) { if err != nil { return nil, nil, err } - rpcCert, err = ioutil.ReadAll(certFile) + rpcCert, err = io.ReadAll(certFile) if err != nil { return nil, nil, err } @@ -703,27 +705,34 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) { // If the fee URL isn't set, and the user is running mainnet, then // we'll return an error to instruct them to set a proper fee // estimator. - case cfg.FeeURL == "" && cfg.Bitcoin.MainNet && + case cfg.Fee.URL == "" && cfg.Bitcoin.MainNet && cfg.Bitcoin.Node == "neutrino": - return nil, nil, fmt.Errorf("--feeurl parameter required " + + return nil, nil, fmt.Errorf("--fee.url parameter required " + "when running neutrino on mainnet") // Override default fee estimator if an external service is specified. - case cfg.FeeURL != "": + case cfg.Fee.URL != "": // Do not cache fees on regtest to make it easier to execute // manual or automated test cases. cacheFees := !cfg.Bitcoin.RegTest - log.Infof("Using external fee estimator %v: cached=%v", - cfg.FeeURL, cacheFees) + log.Infof("Using external fee estimator %v: cached=%v: "+ + "min update timeout=%v, max update timeout=%v", + cfg.Fee.URL, cacheFees, cfg.Fee.MinUpdateTimeout, + cfg.Fee.MaxUpdateTimeout) - cc.FeeEstimator = chainfee.NewWebAPIEstimator( + cc.FeeEstimator, err = chainfee.NewWebAPIEstimator( chainfee.SparseConfFeeSource{ - URL: cfg.FeeURL, + URL: cfg.Fee.URL, }, !cacheFees, + cfg.Fee.MinUpdateTimeout, + cfg.Fee.MaxUpdateTimeout, ) + if err != nil { + return nil, nil, err + } } ccCleanup := func() { diff --git a/chanbackup/backupfile.go b/chanbackup/backupfile.go index 9c1e788c1..5ba6aa756 100644 --- a/chanbackup/backupfile.go +++ b/chanbackup/backupfile.go @@ -2,7 +2,6 @@ package chanbackup import ( "fmt" - "io/ioutil" "os" "path/filepath" @@ -138,7 +137,7 @@ func (b *MultiFile) ExtractMulti(keyChain keychain.KeyRing) (*Multi, error) { // Now that we've confirmed the target file is populated, we'll read // all the contents of the file. This function ensures that file is // always closed, even if we can't read the contents. - multiBytes, err := ioutil.ReadFile(b.fileName) + multiBytes, err := os.ReadFile(b.fileName) if err != nil { return nil, err } diff --git a/chanbackup/backupfile_test.go b/chanbackup/backupfile_test.go index fbd0cc7dd..b0d3ba66f 100644 --- a/chanbackup/backupfile_test.go +++ b/chanbackup/backupfile_test.go @@ -3,7 +3,6 @@ package chanbackup import ( "bytes" "fmt" - "io/ioutil" "math/rand" "os" "path/filepath" @@ -27,7 +26,7 @@ func assertBackupMatches(t *testing.T, filePath string, t.Helper() - packedBackup, err := ioutil.ReadFile(filePath) + packedBackup, err := os.ReadFile(filePath) require.NoError(t, err, "unable to test file") if !bytes.Equal(packedBackup, currentBackup) { diff --git a/channeldb/forwarding_package_test.go b/channeldb/forwarding_package_test.go index d796c6d51..c113d13d5 100644 --- a/channeldb/forwarding_package_test.go +++ b/channeldb/forwarding_package_test.go @@ -142,8 +142,31 @@ func checkPkgFilterEncodeDecode(t *testing.T, i uint16, f *channeldb.PkgFilter) var ( chanID = lnwire.NewChanIDFromOutPoint(wire.OutPoint{}) +) - adds = []channeldb.LogUpdate{ +func testSettleFails() []channeldb.LogUpdate { + return []channeldb.LogUpdate{ + { + LogIndex: 2, + UpdateMsg: &lnwire.UpdateFulfillHTLC{ + ChanID: chanID, + ID: 0, + PaymentPreimage: [32]byte{0}, + }, + }, + { + LogIndex: 3, + UpdateMsg: &lnwire.UpdateFailHTLC{ + ChanID: chanID, + ID: 1, + Reason: []byte{}, + }, + }, + } +} + +func testAdds() []channeldb.LogUpdate { + return []channeldb.LogUpdate{ { LogIndex: 0, UpdateMsg: &lnwire.UpdateAddHTLC{ @@ -165,26 +188,7 @@ var ( }, }, } - - settleFails = []channeldb.LogUpdate{ - { - LogIndex: 2, - UpdateMsg: &lnwire.UpdateFulfillHTLC{ - ChanID: chanID, - ID: 0, - PaymentPreimage: [32]byte{0}, - }, - }, - { - LogIndex: 3, - UpdateMsg: &lnwire.UpdateFailHTLC{ - ChanID: chanID, - ID: 1, - Reason: []byte{}, - }, - }, - } -) +} // TestPackagerEmptyFwdPkg checks that the state transitions exhibited by a // forwarding package that contains no adds, fails or settles. We expect that @@ -273,6 +277,8 @@ func TestPackagerOnlyAdds(t *testing.T) { t.Fatalf("no forwarding packages should exist, found %d", len(fwdPkgs)) } + adds := testAdds() + // Next, create and write a new forwarding package that only has add // htlcs. fwdPkg := channeldb.NewFwdPkg(shortChanID, 0, adds, nil) @@ -377,6 +383,7 @@ func TestPackagerOnlySettleFails(t *testing.T) { // Next, create and write a new forwarding package that only has add // htlcs. + settleFails := testSettleFails() fwdPkg := channeldb.NewFwdPkg(shortChanID, 0, nil, settleFails) nSettleFails := len(settleFails) @@ -479,8 +486,11 @@ func TestPackagerAddsThenSettleFails(t *testing.T) { t.Fatalf("no forwarding packages should exist, found %d", len(fwdPkgs)) } + adds := testAdds() + // Next, create and write a new forwarding package that only has add // htlcs. + settleFails := testSettleFails() fwdPkg := channeldb.NewFwdPkg(shortChanID, 0, adds, settleFails) nAdds := len(adds) @@ -612,8 +622,11 @@ func TestPackagerSettleFailsThenAdds(t *testing.T) { t.Fatalf("no forwarding packages should exist, found %d", len(fwdPkgs)) } + adds := testAdds() + // Next, create and write a new forwarding package that has both add // and settle/fail htlcs. + settleFails := testSettleFails() fwdPkg := channeldb.NewFwdPkg(shortChanID, 0, adds, settleFails) nAdds := len(adds) diff --git a/channeldb/invoices.go b/channeldb/invoices.go index a7663cabf..df124b632 100644 --- a/channeldb/invoices.go +++ b/channeldb/invoices.go @@ -2139,14 +2139,14 @@ func (d *DB) DeleteCanceledInvoices(_ context.Context) error { invoiceIndexBucket, ) if invoiceIndex == nil { - return invpkg.ErrNoInvoicesCreated + return nil } invoiceAddIndex := invoices.NestedReadWriteBucket( addIndexBucket, ) if invoiceAddIndex == nil { - return invpkg.ErrNoInvoicesCreated + return nil } payAddrIndex := tx.ReadWriteBucket(payAddrIndexBucket) diff --git a/channeldb/migration/lnwire21/announcement_signatures.go b/channeldb/migration/lnwire21/announcement_signatures.go index 639704de0..4c2166a8d 100644 --- a/channeldb/migration/lnwire21/announcement_signatures.go +++ b/channeldb/migration/lnwire21/announcement_signatures.go @@ -2,7 +2,6 @@ package lnwire import ( "io" - "io/ioutil" ) // AnnounceSignatures is a direct message between two endpoints of a @@ -66,7 +65,7 @@ func (a *AnnounceSignatures) Decode(r io.Reader, pver uint32) error { // we'll collect the remainder into the ExtraOpaqueData field. If there // aren't any bytes, then we'll snip off the slice to avoid carrying // around excess capacity. - a.ExtraOpaqueData, err = ioutil.ReadAll(r) + a.ExtraOpaqueData, err = io.ReadAll(r) if err != nil { return err } diff --git a/channeldb/migration/lnwire21/channel_announcement.go b/channeldb/migration/lnwire21/channel_announcement.go index 46efeed88..897844fb1 100644 --- a/channeldb/migration/lnwire21/channel_announcement.go +++ b/channeldb/migration/lnwire21/channel_announcement.go @@ -3,7 +3,6 @@ package lnwire import ( "bytes" "io" - "io/ioutil" "github.com/btcsuite/btcd/chaincfg/chainhash" ) @@ -89,7 +88,7 @@ func (a *ChannelAnnouncement) Decode(r io.Reader, pver uint32) error { // we'll collect the remainder into the ExtraOpaqueData field. If there // aren't any bytes, then we'll snip off the slice to avoid carrying // around excess capacity. - a.ExtraOpaqueData, err = ioutil.ReadAll(r) + a.ExtraOpaqueData, err = io.ReadAll(r) if err != nil { return err } diff --git a/channeldb/migration/lnwire21/channel_update.go b/channeldb/migration/lnwire21/channel_update.go index 6d19e3e4c..77e65e76b 100644 --- a/channeldb/migration/lnwire21/channel_update.go +++ b/channeldb/migration/lnwire21/channel_update.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "github.com/btcsuite/btcd/chaincfg/chainhash" ) @@ -160,7 +159,7 @@ func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error { // we'll collect the remainder into the ExtraOpaqueData field. If there // aren't any bytes, then we'll snip off the slice to avoid carrying // around excess capacity. - a.ExtraOpaqueData, err = ioutil.ReadAll(r) + a.ExtraOpaqueData, err = io.ReadAll(r) if err != nil { return err } diff --git a/channeldb/migration/lnwire21/node_announcement.go b/channeldb/migration/lnwire21/node_announcement.go index 8550fddd3..51cf7367b 100644 --- a/channeldb/migration/lnwire21/node_announcement.go +++ b/channeldb/migration/lnwire21/node_announcement.go @@ -5,7 +5,6 @@ import ( "fmt" "image/color" "io" - "io/ioutil" "net" "unicode/utf8" ) @@ -127,7 +126,7 @@ func (a *NodeAnnouncement) Decode(r io.Reader, pver uint32) error { // we'll collect the remainder into the ExtraOpaqueData field. If there // aren't any bytes, then we'll snip off the slice to avoid carrying // around excess capacity. - a.ExtraOpaqueData, err = ioutil.ReadAll(r) + a.ExtraOpaqueData, err = io.ReadAll(r) if err != nil { return err } diff --git a/cmd/commands/cmd_macaroon.go b/cmd/commands/cmd_macaroon.go index ed299182f..149c7db45 100644 --- a/cmd/commands/cmd_macaroon.go +++ b/cmd/commands/cmd_macaroon.go @@ -4,8 +4,8 @@ import ( "bytes" "encoding/hex" "fmt" - "io/ioutil" "net" + "os" "strconv" "strings" "unicode" @@ -192,7 +192,7 @@ func bakeMacaroon(ctx *cli.Context) error { // a file or write to the standard output using hex encoding. switch { case savePath != "": - err = ioutil.WriteFile(savePath, macBytes, 0644) + err = os.WriteFile(savePath, macBytes, 0644) if err != nil { return err } @@ -352,7 +352,7 @@ func printMacaroon(ctx *cli.Context) error { macPath := lncfg.CleanAndExpandPath(ctx.String("macaroon_file")) // Load the specified macaroon file. - macBytes, err = ioutil.ReadFile(macPath) + macBytes, err = os.ReadFile(macPath) if err != nil { return fmt.Errorf("unable to read macaroon path %v: %v", macPath, err) @@ -441,7 +441,7 @@ func constrainMacaroon(ctx *cli.Context) error { sourceMacFile := lncfg.CleanAndExpandPath(args.First()) args = args.Tail() - sourceMacBytes, err := ioutil.ReadFile(sourceMacFile) + sourceMacBytes, err := os.ReadFile(sourceMacFile) if err != nil { return fmt.Errorf("error trying to read source macaroon file "+ "%s: %v", sourceMacFile, err) @@ -471,7 +471,7 @@ func constrainMacaroon(ctx *cli.Context) error { } // Now we can output the result. - err = ioutil.WriteFile(destMacFile, destMacBytes, 0644) + err = os.WriteFile(destMacFile, destMacBytes, 0644) if err != nil { return fmt.Errorf("error writing destination macaroon file "+ "%s: %v", destMacFile, err) diff --git a/cmd/commands/cmd_open_channel.go b/cmd/commands/cmd_open_channel.go index e0efd397b..1012b82ab 100644 --- a/cmd/commands/cmd_open_channel.go +++ b/cmd/commands/cmd_open_channel.go @@ -9,7 +9,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "os" "strconv" "strings" @@ -1016,7 +1015,7 @@ func readTerminalOrFile(quit chan struct{}) (string, error) { // If it's a path to an existing file and it's small enough, let's try // to read its content now. - content, err := ioutil.ReadFile(maybeFile) + content, err := os.ReadFile(maybeFile) if err != nil { return "", err } diff --git a/cmd/commands/cmd_payments.go b/cmd/commands/cmd_payments.go index 277c82653..cd6a34bad 100644 --- a/cmd/commands/cmd_payments.go +++ b/cmd/commands/cmd_payments.go @@ -7,7 +7,7 @@ import ( "encoding/hex" "errors" "fmt" - "io/ioutil" + "io" "os" "runtime" "strconv" @@ -886,6 +886,7 @@ func payInvoice(ctx *cli.Context) error { PaymentRequest: StripPrefix(payReq), Amt: ctx.Int64("amt"), DestCustomRecords: make(map[uint64][]byte), + Amp: ctx.Bool(ampFlag.Name), } return SendPaymentRequest(ctx, req) @@ -987,7 +988,7 @@ func sendToRoute(ctx *cli.Context) error { // The user is signalling that we should read stdin in order to parse // the set of target routes. case args.Present() && args.First() == "-": - b, err := ioutil.ReadAll(os.Stdin) + b, err := io.ReadAll(os.Stdin) if err != nil { return err } @@ -1783,6 +1784,7 @@ func deletePayments(ctx *cli.Context) error { what) _, err = client.DeleteAllPayments( ctxc, &lnrpc.DeleteAllPaymentsRequest{ + AllPayments: includeNonFailed, FailedPaymentsOnly: !includeNonFailed, FailedHtlcsOnly: failedHTLCsOnly, }, diff --git a/cmd/commands/cmd_profile.go b/cmd/commands/cmd_profile.go index 45bd69041..6767964eb 100644 --- a/cmd/commands/cmd_profile.go +++ b/cmd/commands/cmd_profile.go @@ -2,7 +2,6 @@ package commands import ( "fmt" - "io/ioutil" "os" "path" "strings" @@ -423,7 +422,7 @@ func profileAddMacaroon(ctx *cli.Context) error { // Now load and possibly encrypt the macaroon file. macPath := lncfg.CleanAndExpandPath(ctx.GlobalString("macaroonpath")) - macBytes, err := ioutil.ReadFile(macPath) + macBytes, err := os.ReadFile(macPath) if err != nil { return fmt.Errorf("unable to read macaroon path: %w", err) } diff --git a/cmd/commands/cmd_walletunlocker.go b/cmd/commands/cmd_walletunlocker.go index 8a9393adc..844b48f3d 100644 --- a/cmd/commands/cmd_walletunlocker.go +++ b/cmd/commands/cmd_walletunlocker.go @@ -5,7 +5,6 @@ import ( "bytes" "encoding/hex" "fmt" - "io/ioutil" "os" "strconv" "strings" @@ -712,7 +711,7 @@ func createWatchOnly(ctx *cli.Context) error { } jsonFile := lncfg.CleanAndExpandPath(ctx.Args().First()) - jsonBytes, err := ioutil.ReadFile(jsonFile) + jsonBytes, err := os.ReadFile(jsonFile) if err != nil { return fmt.Errorf("error reading JSON from file %v: %v", jsonFile, err) diff --git a/cmd/commands/commands.go b/cmd/commands/commands.go index 5fbe1056c..c3c58af69 100644 --- a/cmd/commands/commands.go +++ b/cmd/commands/commands.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "math" "os" "regexp" @@ -2433,12 +2432,28 @@ func updateChannelPolicy(ctx *cli.Context) error { return errors.New("inbound_fee_rate_ppm out of range") } + // Inbound fees are optional. However, if an update is required, + // both the base fee and the fee rate must be provided. + var inboundFee *lnrpc.InboundFee + if ctx.IsSet("inbound_base_fee_msat") != + ctx.IsSet("inbound_fee_rate_ppm") { + + return errors.New("both parameters must be provided: " + + "inbound_base_fee_msat and inbound_fee_rate_ppm") + } + + if ctx.IsSet("inbound_fee_rate_ppm") { + inboundFee = &lnrpc.InboundFee{ + BaseFeeMsat: int32(inboundBaseFeeMsat), + FeeRatePpm: int32(inboundFeeRatePpm), + } + } + req := &lnrpc.PolicyUpdateRequest{ - BaseFeeMsat: baseFee, - TimeLockDelta: uint32(timeLockDelta), - MaxHtlcMsat: ctx.Uint64("max_htlc_msat"), - InboundBaseFeeMsat: int32(inboundBaseFeeMsat), - InboundFeeRatePpm: int32(inboundFeeRatePpm), + BaseFeeMsat: baseFee, + TimeLockDelta: uint32(timeLockDelta), + MaxHtlcMsat: ctx.Uint64("max_htlc_msat"), + InboundFee: inboundFee, } if ctx.IsSet("min_htlc_msat") { @@ -2878,7 +2893,7 @@ func parseChanBackups(ctx *cli.Context) (*lnrpc.RestoreChanBackupRequest, error) }, nil case ctx.IsSet("multi_file"): - packedMulti, err := ioutil.ReadFile(ctx.String("multi_file")) + packedMulti, err := os.ReadFile(ctx.String("multi_file")) if err != nil { return nil, fmt.Errorf("unable to decode multi packed "+ "backup: %v", err) diff --git a/cmd/commands/profile.go b/cmd/commands/profile.go index e9f6369a2..0d63ca93c 100644 --- a/cmd/commands/profile.go +++ b/cmd/commands/profile.go @@ -6,7 +6,7 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "os" "path" "strings" @@ -129,7 +129,7 @@ func profileFromContext(ctx *cli.Context, store, skipMacaroons bool) ( var tlsCert []byte if tlsCertPath != "" && !insecure { var err error - tlsCert, err = ioutil.ReadFile(tlsCertPath) + tlsCert, err = os.ReadFile(tlsCertPath) if err != nil { return nil, fmt.Errorf("could not load TLS cert "+ "file: %v", err) @@ -167,7 +167,7 @@ func profileFromContext(ctx *cli.Context, store, skipMacaroons bool) ( } // Now load and possibly encrypt the macaroon file. - macBytes, err := ioutil.ReadFile(macPath) + macBytes, err := os.ReadFile(macPath) if err != nil { return nil, fmt.Errorf("unable to read macaroon path (check "+ "the network setting!): %v", err) @@ -222,7 +222,7 @@ func loadProfileFile(file string) (*profileFile, error) { return nil, errNoProfileFile } - content, err := ioutil.ReadFile(file) + content, err := os.ReadFile(file) if err != nil { return nil, fmt.Errorf("could not load profile file %s: %w", file, err) @@ -243,7 +243,8 @@ func saveProfileFile(file string, f *profileFile) error { if err != nil { return fmt.Errorf("could not marshal profile: %w", err) } - return ioutil.WriteFile(file, content, 0644) + + return os.WriteFile(file, content, 0644) } // profileFile is a struct that represents the whole content of a profile file. diff --git a/cmd/commands/walletrpc_active.go b/cmd/commands/walletrpc_active.go index 2be509a4b..17b0b2939 100644 --- a/cmd/commands/walletrpc_active.go +++ b/cmd/commands/walletrpc_active.go @@ -272,11 +272,26 @@ func bumpFee(ctx *cli.Context) error { client, cleanUp := getWalletClient(ctx) defer cleanUp() + // Parse immediate flag (force flag was deprecated). + immediate := false + switch { + case ctx.IsSet("immediate") && ctx.IsSet("force"): + return fmt.Errorf("cannot set immediate and force flag at " + + "the same time") + + case ctx.Bool("immediate"): + immediate = true + + case ctx.Bool("force"): + immediate = true + } + resp, err := client.BumpFee(ctxc, &walletrpc.BumpFeeRequest{ - Outpoint: protoOutPoint, - TargetConf: uint32(ctx.Uint64("conf_target")), - Immediate: ctx.Bool("force"), - Budget: ctx.Uint64("budget"), + Outpoint: protoOutPoint, + TargetConf: uint32(ctx.Uint64("conf_target")), + Immediate: immediate, + Budget: ctx.Uint64("budget"), + SatPerVbyte: ctx.Uint64("sat_per_vbyte"), }) if err != nil { return err @@ -473,10 +488,11 @@ func bumpForceCloseFee(ctx *cli.Context) error { resp, err := walletClient.BumpFee( ctxc, &walletrpc.BumpFeeRequest{ - Outpoint: sweep.Outpoint, - TargetConf: uint32(ctx.Uint64("conf_target")), - Budget: ctx.Uint64("budget"), - Immediate: ctx.Bool("immediate"), + Outpoint: sweep.Outpoint, + TargetConf: uint32(ctx.Uint64("conf_target")), + Budget: ctx.Uint64("budget"), + Immediate: ctx.Bool("immediate"), + SatPerVbyte: ctx.Uint64("sat_per_vbyte"), }) if err != nil { return err diff --git a/config.go b/config.go index f746be041..13fd2c832 100644 --- a/config.go +++ b/config.go @@ -8,7 +8,7 @@ import ( "encoding/hex" "errors" "fmt" - "io/ioutil" + "io" "net" "os" "os/user" @@ -350,7 +350,7 @@ type Config struct { MaxPendingChannels int `long:"maxpendingchannels" description:"The maximum number of incoming pending channels permitted per peer."` BackupFilePath string `long:"backupfilepath" description:"The target location of the channel backup file"` - FeeURL string `long:"feeurl" description:"Optional URL for external fee estimation. If no URL is specified, the method for fee estimation will depend on the chosen backend and network. Must be set for neutrino on mainnet."` + FeeURL string `long:"feeurl" description:"DEPRECATED: Use 'fee.url' option. Optional URL for external fee estimation. If no URL is specified, the method for fee estimation will depend on the chosen backend and network. Must be set for neutrino on mainnet." hidden:"true"` Bitcoin *lncfg.Chain `group:"Bitcoin" namespace:"bitcoin"` BtcdMode *lncfg.Btcd `group:"btcd" namespace:"btcd"` @@ -442,6 +442,8 @@ type Config struct { DustThreshold uint64 `long:"dust-threshold" description:"Sets the dust sum threshold in satoshis for a channel after which dust HTLC's will be failed."` + Fee *lncfg.Fee `group:"fee" namespace:"fee"` + Invoices *lncfg.Invoices `group:"invoices" namespace:"invoices"` Routing *lncfg.Routing `group:"routing" namespace:"routing"` @@ -582,6 +584,12 @@ func DefaultConfig() Config { MinBackoff: defaultMinBackoff, MaxBackoff: defaultMaxBackoff, ConnectionTimeout: tor.DefaultConnTimeout, + + Fee: &lncfg.Fee{ + MinUpdateTimeout: lncfg.DefaultMinUpdateTimeout, + MaxUpdateTimeout: lncfg.DefaultMaxUpdateTimeout, + }, + SubRPCServers: &subRPCServerConfigs{ SignRPC: &signrpc.Config{}, RouterRPC: routerrpc.DefaultConfig(), @@ -627,9 +635,8 @@ func DefaultConfig() Config { RejectCacheSize: channeldb.DefaultRejectCacheSize, ChannelCacheSize: channeldb.DefaultChannelCacheSize, }, - Prometheus: lncfg.DefaultPrometheus(), - Watchtower: lncfg.DefaultWatchtowerCfg(defaultTowerDir), - ProtocolOptions: lncfg.DefaultProtocol(), + Prometheus: lncfg.DefaultPrometheus(), + Watchtower: lncfg.DefaultWatchtowerCfg(defaultTowerDir), HealthChecks: &lncfg.HealthCheckConfig{ ChainCheck: &lncfg.CheckConfig{ Interval: defaultChainInterval, @@ -1660,8 +1667,7 @@ func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser, // If the experimental protocol options specify any protocol messages // that we want to handle as custom messages, set them now. - //nolint:lll - customMsg := cfg.ProtocolOptions.ExperimentalProtocol.CustomMessageOverrides() + customMsg := cfg.ProtocolOptions.CustomMessageOverrides() // We can safely set our custom override values during startup because // startup is blocked on config parsing. @@ -1845,7 +1851,7 @@ func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{}, // We convert the cookie into a user name and password. if conf.RPCCookie != "" { - cookie, err := ioutil.ReadFile(conf.RPCCookie) + cookie, err := os.ReadFile(conf.RPCCookie) if err != nil { return fmt.Errorf("cannot read cookie file: %w", err) @@ -2006,7 +2012,7 @@ func extractBtcdRPCParams(btcdConfigPath string) (string, string, error) { // With the file open extract the contents of the configuration file so // we can attempt to locate the RPC credentials. - configContents, err := ioutil.ReadAll(btcdConfigFile) + configContents, err := io.ReadAll(btcdConfigFile) if err != nil { return "", "", err } @@ -2056,7 +2062,7 @@ func extractBitcoindRPCParams(networkName, bitcoindDataDir, bitcoindConfigPath, // With the file open extract the contents of the configuration file so // we can attempt to locate the RPC credentials. - configContents, err := ioutil.ReadAll(bitcoindConfigFile) + configContents, err := io.ReadAll(bitcoindConfigFile) if err != nil { return "", "", "", "", err } @@ -2118,7 +2124,7 @@ func extractBitcoindRPCParams(networkName, bitcoindDataDir, bitcoindConfigPath, if rpcCookiePath != "" { cookiePath = rpcCookiePath } - cookie, err := ioutil.ReadFile(cookiePath) + cookie, err := os.ReadFile(cookiePath) if err == nil { splitCookie := strings.Split(string(cookie), ":") if len(splitCookie) == 2 { diff --git a/config_builder.go b/config_builder.go index f1f59e916..4adf7f612 100644 --- a/config_builder.go +++ b/config_builder.go @@ -6,7 +6,6 @@ import ( "database/sql" "errors" "fmt" - "io/ioutil" "net" "os" "path/filepath" @@ -367,7 +366,7 @@ func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context, case d.cfg.WalletUnlockPasswordFile != "" && walletExists: d.logger.Infof("Attempting automatic wallet unlock with " + "password provided in file") - pwBytes, err := ioutil.ReadFile(d.cfg.WalletUnlockPasswordFile) + pwBytes, err := os.ReadFile(d.cfg.WalletUnlockPasswordFile) if err != nil { return nil, nil, nil, fmt.Errorf("error reading "+ "password from file %s: %v", @@ -591,6 +590,11 @@ func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context, AuxSigner: aux.AuxSigner, ActiveNetParams: d.cfg.ActiveNetParams, FeeURL: d.cfg.FeeURL, + Fee: &lncfg.Fee{ + URL: d.cfg.Fee.URL, + MinUpdateTimeout: d.cfg.Fee.MinUpdateTimeout, + MaxUpdateTimeout: d.cfg.Fee.MaxUpdateTimeout, + }, Dialer: func(addr string) (net.Conn, error) { return d.cfg.net.Dial( "tcp", addr, d.cfg.ConnectionTimeout, diff --git a/contractcourt/breach_arbitrator.go b/contractcourt/breach_arbitrator.go index 89927b6e1..96fa689c3 100644 --- a/contractcourt/breach_arbitrator.go +++ b/contractcourt/breach_arbitrator.go @@ -19,6 +19,7 @@ import ( "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/labels" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) @@ -1497,14 +1498,14 @@ func (b *BreachArbitrator) createSweepTx(inputs ...input.Input) (*wire.MsgTx, spendableOutputs = append(spendableOutputs, inp) } - txWeight := int64(weightEstimate.Weight()) + txWeight := weightEstimate.Weight() return b.sweepSpendableOutputsTxn(txWeight, spendableOutputs...) } // sweepSpendableOutputsTxn creates a signed transaction from a sequence of // spendable outputs by sweeping the funds into a single p2wkh output. -func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight int64, +func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit, inputs ...input.Input) (*wire.MsgTx, error) { // First, we obtain a new public key script from the wallet which we'll diff --git a/contractcourt/chain_arbitrator.go b/contractcourt/chain_arbitrator.go index d7d9e93cf..d61e47901 100644 --- a/contractcourt/chain_arbitrator.go +++ b/contractcourt/chain_arbitrator.go @@ -1311,6 +1311,10 @@ func (c *ChainArbitrator) FindOutgoingHTLCDeadline(scid lnwire.ShortChannelID, continue } + // Make sure the channel arbitrator has the latest view of its + // active HTLCs. + channelArb.updateActiveHTLCs() + // Iterate all the known HTLCs to find the targeted incoming // HTLC. for _, htlcs := range channelArb.activeHTLCs { diff --git a/contractcourt/channel_arbitrator.go b/contractcourt/channel_arbitrator.go index 64d062e99..cda0d4e1f 100644 --- a/contractcourt/channel_arbitrator.go +++ b/contractcourt/channel_arbitrator.go @@ -1436,9 +1436,13 @@ func (c *ChannelArbitrator) sweepAnchors(anchors *lnwallet.AnchorResolutions, // findCommitmentDeadlineAndValue finds the deadline (relative block height) // for a commitment transaction by extracting the minimum CLTV from its HTLCs. -// From our PoV, the deadline is defined to be the smaller of, -// - the least CLTV from outgoing HTLCs, or, -// - the least CLTV from incoming HTLCs if the preimage is available. +// From our PoV, the deadline delta is defined to be the smaller of, +// - half of the least CLTV from outgoing HTLCs' corresponding incoming +// HTLCs, or, +// - half of the least CLTV from incoming HTLCs if the preimage is available. +// +// We use half of the CTLV value to ensure that we have enough time to sweep +// the second-level HTLCs. // // It also finds the total value that are time-sensitive, which is the sum of // all the outgoing HTLCs plus incoming HTLCs whose preimages are known. It @@ -1468,10 +1472,24 @@ func (c *ChannelArbitrator) findCommitmentDeadlineAndValue(heightHint uint32, } value := htlc.Amt.ToSatoshis() - totalValue += value - if htlc.RefundTimeout < deadlineMinHeight { - deadlineMinHeight = htlc.RefundTimeout + // Find the expiry height for this outgoing HTLC's incoming + // HTLC. + deadlineOpt := c.cfg.FindOutgoingHTLCDeadline(htlc) + + // The deadline is default to the current deadlineMinHeight, + // and it's overwritten when it's not none. + deadline := deadlineMinHeight + deadlineOpt.WhenSome(func(d int32) { + deadline = uint32(d) + + // We only consider the value is under protection when + // it's time-sensitive. + totalValue += value + }) + + if deadline < deadlineMinHeight { + deadlineMinHeight = deadline log.Tracef("ChannelArbitrator(%v): outgoing HTLC has "+ "deadline=%v, value=%v", c.cfg.ChanPoint, @@ -1521,7 +1539,7 @@ func (c *ChannelArbitrator) findCommitmentDeadlineAndValue(heightHint uint32, // * none of the HTLCs are preimageAvailable. // - when our deadlineMinHeight is no greater than the heightHint, // which means we are behind our schedule. - deadline := deadlineMinHeight - heightHint + var deadline uint32 switch { // When we couldn't find a deadline height from our HTLCs, we will fall // back to the default value as there's no time pressure here. @@ -1535,6 +1553,11 @@ func (c *ChannelArbitrator) findCommitmentDeadlineAndValue(heightHint uint32, "deadlineMinHeight=%d, heightHint=%d", c.cfg.ChanPoint, deadlineMinHeight, heightHint) deadline = 1 + + // Use half of the deadline delta, and leave the other half to be used + // to sweep the HTLCs. + default: + deadline = (deadlineMinHeight - heightHint) / 2 } // Calculate the value left after subtracting the budget used for @@ -2800,7 +2823,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) { // state, so we'll get the most up to date signals to we can // properly do our job. case signalUpdate := <-c.signalUpdates: - log.Tracef("ChannelArbitrator(%v) got new signal "+ + log.Tracef("ChannelArbitrator(%v): got new signal "+ "update!", c.cfg.ChanPoint) // We'll update the ShortChannelID. @@ -3066,6 +3089,9 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) { // We've just received a request to forcibly close out the // channel. We'll case closeReq := <-c.forceCloseReqs: + log.Infof("ChannelArbitrator(%v): received force "+ + "close request", c.cfg.ChanPoint) + if c.state != StateDefault { select { case closeReq.closeTx <- nil: diff --git a/contractcourt/channel_arbitrator_test.go b/contractcourt/channel_arbitrator_test.go index 77c9597c0..43238494e 100644 --- a/contractcourt/channel_arbitrator_test.go +++ b/contractcourt/channel_arbitrator_test.go @@ -2305,20 +2305,22 @@ func TestFindCommitmentDeadlineAndValue(t *testing.T) { } testCases := []struct { - name string - htlcs htlcSet - err error - deadline fn.Option[int32] - expectedBudget btcutil.Amount + name string + htlcs htlcSet + err error + deadline fn.Option[int32] + mockFindOutgoingHTLCDeadline func() + expectedBudget btcutil.Amount }{ { // When we have no HTLCs, the default value should be // used. - name: "use default conf target", - htlcs: htlcSet{}, - err: nil, - deadline: fn.None[int32](), - expectedBudget: 0, + name: "use default conf target", + htlcs: htlcSet{}, + err: nil, + deadline: fn.None[int32](), + mockFindOutgoingHTLCDeadline: func() {}, + expectedBudget: 0, }, { // When we have a preimage available in the local HTLC @@ -2329,8 +2331,17 @@ func TestFindCommitmentDeadlineAndValue(t *testing.T) { htlcs: makeHTLCSet(htlcPreimage, htlcLargeExpiry), err: nil, deadline: fn.Some(int32( - htlcPreimage.RefundTimeout - heightHint, + (htlcPreimage.RefundTimeout - heightHint) / 2, )), + mockFindOutgoingHTLCDeadline: func() { + chanArb.cfg.FindOutgoingHTLCDeadline = func( + htlc channeldb.HTLC) fn.Option[int32] { + + return fn.Some(int32( + htlcLargeExpiry.RefundTimeout, + )) + } + }, expectedBudget: htlcAmt.ToSatoshis(), }, { @@ -2342,8 +2353,18 @@ func TestFindCommitmentDeadlineAndValue(t *testing.T) { htlcs: makeHTLCSet(htlcSmallExipry, htlcLargeExpiry), err: nil, deadline: fn.Some(int32( - htlcLargeExpiry.RefundTimeout - heightHint, + (htlcLargeExpiry.RefundTimeout - + heightHint) / 2, )), + mockFindOutgoingHTLCDeadline: func() { + chanArb.cfg.FindOutgoingHTLCDeadline = func( + htlc channeldb.HTLC) fn.Option[int32] { + + return fn.Some(int32( + htlcLargeExpiry.RefundTimeout, + )) + } + }, expectedBudget: htlcAmt.ToSatoshis() / 2, }, { @@ -2354,18 +2375,36 @@ func TestFindCommitmentDeadlineAndValue(t *testing.T) { htlcs: makeHTLCSet(htlcPreimage, htlcDust), err: nil, deadline: fn.Some(int32( - htlcPreimage.RefundTimeout - heightHint, + (htlcPreimage.RefundTimeout - heightHint) / 2, )), + mockFindOutgoingHTLCDeadline: func() { + chanArb.cfg.FindOutgoingHTLCDeadline = func( + htlc channeldb.HTLC) fn.Option[int32] { + + return fn.Some(int32( + htlcDust.RefundTimeout, + )) + } + }, expectedBudget: htlcAmt.ToSatoshis() / 2, }, { // When we've reached our deadline, use conf target of // 1 as our deadline. And the value left should be // htlcAmt. - name: "use conf target 1", - htlcs: makeHTLCSet(htlcPreimage, htlcExpired), - err: nil, - deadline: fn.Some(int32(1)), + name: "use conf target 1", + htlcs: makeHTLCSet(htlcPreimage, htlcExpired), + err: nil, + deadline: fn.Some(int32(1)), + mockFindOutgoingHTLCDeadline: func() { + chanArb.cfg.FindOutgoingHTLCDeadline = func( + htlc channeldb.HTLC) fn.Option[int32] { + + return fn.Some(int32( + htlcExpired.RefundTimeout, + )) + } + }, expectedBudget: htlcAmt.ToSatoshis(), }, } @@ -2373,7 +2412,9 @@ func TestFindCommitmentDeadlineAndValue(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { - t.Parallel() + // Mock the method `FindOutgoingHTLCDeadline`. + tc.mockFindOutgoingHTLCDeadline() + deadline, budget, err := chanArb. findCommitmentDeadlineAndValue( heightHint, tc.htlcs, @@ -2412,31 +2453,35 @@ func TestSweepAnchors(t *testing.T) { chanArbCtx.chanArb.blocks <- int32(heightHint) htlcIndexBase := uint64(99) - htlcExpiryBase := heightHint + uint32(10) + deadlineDelta := uint32(10) htlcAmt := lnwire.MilliSatoshi(1_000_000) // Create three testing HTLCs. htlcDust := channeldb.HTLC{ HtlcIndex: htlcIndexBase + 1, - RefundTimeout: htlcExpiryBase + 1, + RefundTimeout: heightHint + 1, OutputIndex: -1, } + + deadlinePreimageDelta := deadlineDelta + 2 htlcWithPreimage := channeldb.HTLC{ HtlcIndex: htlcIndexBase + 2, - RefundTimeout: htlcExpiryBase + 2, + RefundTimeout: heightHint + deadlinePreimageDelta, RHash: rHash, Amt: htlcAmt, } + + deadlineSmallDelta := deadlineDelta + 4 htlcSmallExipry := channeldb.HTLC{ HtlcIndex: htlcIndexBase + 3, - RefundTimeout: htlcExpiryBase + 3, + RefundTimeout: heightHint + deadlineSmallDelta, Amt: htlcAmt, } // Setup our local HTLC set such that we will use the HTLC's CLTV from // the incoming HTLC set. - expectedLocalDeadline := htlcWithPreimage.RefundTimeout + expectedLocalDeadline := heightHint + deadlinePreimageDelta/2 chanArb.activeHTLCs[LocalHtlcSet] = htlcSet{ incomingHTLCs: map[uint64]channeldb.HTLC{ htlcWithPreimage.HtlcIndex: htlcWithPreimage, @@ -2475,7 +2520,7 @@ func TestSweepAnchors(t *testing.T) { // Setup out pending remote HTLC set such that we will use the HTLC's // CLTV from the outgoing HTLC set. - expectedPendingDeadline := htlcSmallExipry.RefundTimeout + expectedPendingDeadline := heightHint + deadlineSmallDelta/2 chanArb.activeHTLCs[RemotePendingHtlcSet] = htlcSet{ incomingHTLCs: map[uint64]channeldb.HTLC{ htlcDust.HtlcIndex: htlcDust, @@ -2493,6 +2538,18 @@ func TestSweepAnchors(t *testing.T) { }, } + // Mock FindOutgoingHTLCDeadline so the pending remote's outgoing HTLC + // returns the small expiry value. + chanArb.cfg.FindOutgoingHTLCDeadline = func( + htlc channeldb.HTLC) fn.Option[int32] { + + if htlc.RHash != htlcSmallExipry.RHash { + return fn.None[int32]() + } + + return fn.Some(int32(htlcSmallExipry.RefundTimeout)) + } + // Create AnchorResolutions. anchors := &lnwallet.AnchorResolutions{ Local: &lnwallet.AnchorResolution{ @@ -2599,17 +2656,20 @@ func TestChannelArbitratorAnchors(t *testing.T) { htlcAmt := lnwire.MilliSatoshi(1_000_000) // Create testing HTLCs. - htlcExpiryBase := heightHint + uint32(10) + deadlineDelta := uint32(10) + deadlinePreimageDelta := deadlineDelta + 2 htlcWithPreimage := channeldb.HTLC{ HtlcIndex: 99, - RefundTimeout: htlcExpiryBase + 2, + RefundTimeout: heightHint + deadlinePreimageDelta, RHash: rHash, Incoming: true, Amt: htlcAmt, } + + deadlineHTLCdelta := deadlineDelta + 3 htlc := channeldb.HTLC{ HtlcIndex: 100, - RefundTimeout: htlcExpiryBase + 3, + RefundTimeout: heightHint + deadlineHTLCdelta, Amt: htlcAmt, } @@ -2755,11 +2815,11 @@ func TestChannelArbitratorAnchors(t *testing.T) { // to htlcWithPreimage's CLTV. require.Equal(t, 2, len(chanArbCtx.sweeper.deadlines)) require.EqualValues(t, - htlcWithPreimage.RefundTimeout, + heightHint+deadlinePreimageDelta/2, chanArbCtx.sweeper.deadlines[0], ) require.EqualValues(t, - htlcWithPreimage.RefundTimeout, + heightHint+deadlinePreimageDelta/2, chanArbCtx.sweeper.deadlines[1], ) } diff --git a/contractcourt/htlc_incoming_contest_resolver.go b/contractcourt/htlc_incoming_contest_resolver.go index e9a81d46a..6bda4e398 100644 --- a/contractcourt/htlc_incoming_contest_resolver.go +++ b/contractcourt/htlc_incoming_contest_resolver.go @@ -543,7 +543,7 @@ func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload, return nil, nil, err } - payload, err := iterator.HopPayload() + payload, _, err := iterator.HopPayload() if err != nil { return nil, nil, err } diff --git a/contractcourt/htlc_incoming_contest_resolver_test.go b/contractcourt/htlc_incoming_contest_resolver_test.go index a87b1991c..55d93a6fb 100644 --- a/contractcourt/htlc_incoming_contest_resolver_test.go +++ b/contractcourt/htlc_incoming_contest_resolver_test.go @@ -3,7 +3,6 @@ package contractcourt import ( "bytes" "io" - "io/ioutil" "testing" sphinx "github.com/lightningnetwork/lightning-onion" @@ -264,7 +263,7 @@ type mockHopIterator struct { hop.Iterator } -func (h *mockHopIterator) HopPayload() (*hop.Payload, error) { +func (h *mockHopIterator) HopPayload() (*hop.Payload, hop.RouteRole, error) { var nextAddress [8]byte if !h.isExit { nextAddress = [8]byte{0x01} @@ -276,7 +275,7 @@ func (h *mockHopIterator) HopPayload() (*hop.Payload, error) { ForwardAmount: 100, OutgoingCltv: 40, ExtraBytes: [12]byte{}, - }), nil + }), hop.RouteRoleCleartext, nil } func (h *mockHopIterator) EncodeNextHop(w io.Writer) error { @@ -291,7 +290,7 @@ type mockOnionProcessor struct { func (o *mockOnionProcessor) ReconstructHopIterator(r io.Reader, rHash []byte, _ hop.ReconstructBlindingInfo) (hop.Iterator, error) { - data, err := ioutil.ReadAll(r) + data, err := io.ReadAll(r) if err != nil { return nil, err } diff --git a/contractcourt/nursery_store.go b/contractcourt/nursery_store.go index a976ed89c..c668f22b6 100644 --- a/contractcourt/nursery_store.go +++ b/contractcourt/nursery_store.go @@ -75,7 +75,7 @@ import ( // graduated height and finalized txes. This also prevents people downgrading // and surprising the downgraded nursery with missing data. -// NurseryStore abstracts the persistent storage layer for the utxo nursery. +// NurseryStorer abstracts the persistent storage layer for the utxo nursery. // Concretely, it stores commitment and htlc outputs until any time-bounded // constraints have fully matured. The store exposes methods for enumerating its // contents, and persisting state transitions detected by the utxo nursery. diff --git a/dev.Dockerfile b/dev.Dockerfile index d3ef269de..624c1bc3a 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -4,7 +4,7 @@ # /make/builder.Dockerfile # /.github/workflows/main.yml # /.github/workflows/release.yml -FROM golang:1.21.0-alpine as builder +FROM golang:1.22.3-alpine as builder LABEL maintainer="Olaoluwa Osuntokun " @@ -24,7 +24,7 @@ COPY . /go/src/github.com/lightningnetwork/lnd # Install/build lnd. RUN cd /go/src/github.com/lightningnetwork/lnd \ && make \ -&& make install tags="signrpc walletrpc chainrpc invoicesrpc peersrpc" +&& make install-all tags="signrpc walletrpc chainrpc invoicesrpc peersrpc" # Start a new, final image to reduce size. FROM alpine as final diff --git a/docs/release-notes/release-notes-0.18.0.md b/docs/release-notes/release-notes-0.18.0.md index 955905354..745535f9d 100644 --- a/docs/release-notes/release-notes-0.18.0.md +++ b/docs/release-notes/release-notes-0.18.0.md @@ -107,7 +107,8 @@ which with the default fee allocation in place will eventually lead to the downsizing to the fee floor (1 sat/vByte) in the worst case. -* [Removed](https://github.com/lightningnetwork/lnd/pull/8577) some unreachable code +* [Removed](https://github.com/lightningnetwork/lnd/pull/8577) some unreachable + code. * [Fixed](https://github.com/lightningnetwork/lnd/pull/8609) a function call where arguments were swapped. @@ -119,6 +120,15 @@ * [Fixed a bug in `btcd` that caused an incompatibility with `bitcoind v27.0`](https://github.com/lightningnetwork/lnd/pull/8573). +* [Fixed](https://github.com/lightningnetwork/lnd/pull/8545) UTXO selection + for the internal channel funding flow (Single and Batch Funding Flow). Now + UTXOs which are unconfirmed and originated from the sweeper subsystem are not + selected because they bear the risk of being replaced (BIP 125 RBF). + +* [Fixed](https://github.com/lightningnetwork/lnd/pull/8621) the behaviour of + neutrino LND nodes which would lose sync in case they had very unstable + peer connection. + # New Features ## Functional Enhancements @@ -136,13 +146,13 @@ can be enabled with the option `accept-positive-inbound-fees`. * A new config value, - [sweeper.maxfeerate](https://github.com/lightningnetwork/lnd/pull/7823), is + [`sweeper.maxfeerate`](https://github.com/lightningnetwork/lnd/pull/7823), is added so users can specify the max allowed fee rate when sweeping on-chain - funds. The default value is 1000 sat/vb. Setting this value below 100 sat/vb + funds. The default value is 1000 sat/vB. Setting this value below 100 sat/vB is not allowed, as low fee rate can cause transactions not confirming in time, which could result in fund loss. Please note that the actual fee rate to be used is determined by the fee - estimator used(for instance `bitcoind`), and this value is a cap on the max + estimator used (for instance `bitcoind`), and this value is a cap on the max allowed value. So it's expected that this cap is rarely hit unless there's mempool congestion. @@ -195,13 +205,28 @@ `lnd.conf`](https://github.com/lightningnetwork/lnd/pull/8310) for the `rpcuser` and `rpcpass` fields to better protect the secrets. -* When computing a minimum fee for transaction construction, `lnd` [now takes our -bitcoin peers' feefilter values into account](https://github.com/lightningnetwork/lnd/pull/8418). +* When computing a minimum fee for transaction construction, `lnd` [now takes + its bitcoin peers' `feefilter` values into + account](https://github.com/lightningnetwork/lnd/pull/8418). + +* Web fee estimator settings have been moved into a new `fee` config group. + A new `fee.url` option has been added within this group that replaces the old + `feeurl` option, which is now deprecated. Additionally, [two new config values, + fee.min-update-timeout and fee.max-update-timeout](https://github.com/lightningnetwork/lnd/pull/8484) + are added to allow users to specify the minimum and maximum time between fee + updates from the web fee estimator. The default values are 5 minutes and 20 + minutes respectively. These values are used to prevent the fee estimator from + being queried too frequently. This replaces previously hardcoded values that + were set to the same values as the new defaults. The previously deprecated + `neutrino.feeurl` option has been removed. * [Preparatory work](https://github.com/lightningnetwork/lnd/pull/8159) for forwarding of blinded routes was added, along with [support](https://github.com/lightningnetwork/lnd/pull/8160) - for forwarding blinded payments. Forwarding of blinded payments is disabled - by default, and the feature is not yet advertised to the network. + for forwarding blinded payments and [error handling](https://github.com/lightningnetwork/lnd/pull/8485). + With this change, LND is now eligible to be selected as part of a blinded + route and can forward payments on behalf of nodes that have support for + receiving to blinded paths. This upgrade provides a meaningful improvement + to the anonymity set and usability of blinded paths in the Lightning Network. * Introduced [fee bumper](https://github.com/lightningnetwork/lnd/pull/8424) to handle bumping the fees of sweeping transactions properly. A @@ -250,7 +275,7 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor * Deprecate `bumpclosefee` for `bumpforceclosefee` to accommodate for the fact that only force closing transactions can be bumped to avoid confusion. - Moreover allow to specify a max fee rate range when coop closing a channel. + Moreover, allow to specify a max fee rate range when coop closing a channel. [Deprecate bumpclosefee for bumpforceclosefee and add `max_fee_rate` option to `closechannel` cmd](https://github.com/lightningnetwork/lnd/pull/8350). @@ -259,7 +284,9 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor * [Man pages](https://github.com/lightningnetwork/lnd/pull/8525) Generate man pages automatically using `lncli generatemanpage` command for both `lncli` - and `lnd` commands when running `make install` in the Makefile. + and `lnd` commands when running + [`make install-all`](https://github.com/lightningnetwork/lnd/pull/8739) in + the Makefile. # Improvements ## Functional Updates @@ -287,6 +314,13 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor maintain a healthy connection to the network by checking the number of outbound peers if they are below 6. +* [Add inbound fees](https://github.com/lightningnetwork/lnd/pull/8723) to + `subscribeChannelGraph`. + +* [Moved](https://github.com/lightningnetwork/lnd/pull/8744) the experimental + "custom" options to the main protocol config so that they can be used without + the dev build flag set. + ### Logging * [Add the htlc amount](https://github.com/lightningnetwork/lnd/pull/8156) to contract court logs in case of timed-out HTLCs in order to easily spot dust @@ -329,15 +363,8 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor * [Coin Selection Strategy](https://github.com/lightningnetwork/lnd/pull/8515) add coin selection strategy option to the following on-chain RPC calls - `EstimateFee`, `SendMany`, `SendCoins`, `BatchOpenChannel`, `SendOutputs`, and `FundPsbt`. - -* Previously when callng `SendCoins`, `SendMany`, `OpenChannel` and - `CloseChannel` for coop close, it is allowed to specify both an empty - `SatPerVbyte` and `TargetConf`, and a default conf target of 6 will be used. - This is [no longer allowed]( - https://github.com/lightningnetwork/lnd/pull/8422) and the caller must - specify either `SatPerVbyte` or `TargetConf` so the fee estimator can do a - proper fee estimation. + `EstimateFee`, `SendMany`, `SendCoins`, `BatchOpenChannel`, `SendOutputs`, and + `FundPsbt`. * `BumpFee` has been updated to take advantage of the [new budget-based sweeper](https://github.com/lightningnetwork/lnd/pull/8667). The param @@ -347,6 +374,16 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor `budget`, and `deadline_height`, the fields `force`, `requested_conf_target`, and `next_broadcast_height` are deprecated. +* [Delete All Payments RPC](https://github.com/lightningnetwork/lnd/pull/8672) + adds `all_payments` option to the `DeleteAllPayments` RPC. This update + ensures that the arguments are provided when calling `DeleteAllPayments` RPC, + whether through gRPC or the REST API, due to the destructive nature of the + operation. + +* When paying an AMP payment request, [the `--amp` flag is now + required](https://github.com/lightningnetwork/lnd/pull/8681) to be consistent + with the flow when a payment request isn't used. + ## lncli Updates * [Documented all available `lncli` @@ -381,7 +418,10 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor In particular, the complexity involved in the lifecycle loop has been decoupled into logical steps, with each step having its own responsibility, making it easier to reason about the payment flow. - + +* [Remove io/ioutil package + dependence](https://github.com/lightningnetwork/lnd/pull/7765). + * [Add a watchtower tower client multiplexer](https://github.com/lightningnetwork/lnd/pull/7702) to manage tower clients of different types. @@ -401,6 +441,21 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor manage the lifecycle of the inputs. ## Breaking Changes + +* Previously when calling `SendCoins`, `SendMany`, `OpenChannel` and + `CloseChannel` for coop close, it is allowed to specify both an empty + `SatPerVbyte` and `TargetConf`, and a default conf target of 6 will be used. + This will [no longer be + allowed](https://github.com/lightningnetwork/lnd/pull/8422) in the next + release (v0.19.0) and the caller must specify either `SatPerVbyte` or + `TargetConf` so the fee estimator can do a proper fee estimation. For current + release, [an error will be + logged](https://github.com/lightningnetwork/lnd/pull/8693) when no values are + specified. + +* Removed deprecated `neutrino.feeurl` option. Please use the newer `fee.url` + option instead. + ## Performance Improvements * Watchtower client DB migration to massively [improve the start-up @@ -482,7 +537,7 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor settings between `sqldb` and `kvdb` packages. * [Expanded SweeperStore](https://github.com/lightningnetwork/lnd/pull/8147) to - also store the feerate, fees paid, and whether it's published or not for a + also store the fee rate, fees paid, and whether it's published or not for a given sweeping transaction. ## Code Health @@ -490,29 +545,61 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor * [Remove database pointers](https://github.com/lightningnetwork/lnd/pull/8117) from `channeldb` schema structs. -## Tooling and Documentation - # Contributors (Alphabetical Order) * Alex Akselrod +* Alex Sears * Amin Bashiri * Andras Banki-Horvath +* AtomicInnovation321 +* bartoli * BitcoinerCoderBob +* bitromortac +* bota87 +* Bufo +* Calvin Zachman * Carla Kirk-Cohen +* cristiantroy +* cuinix +* davisv7 * Elle Mouton * ErikEk +* Eugene Siegel * Feelancer21 +* ffranr +* Hao Wang +* hidewrong * Jesse de Wit +* João Thallis +* Jonathan Harvey-Buschel * Joost Jager +* Jordi Montes * Keagan McClelland +* kilrau +* mani2310 * Marcos Fernandez Perez * Matt Morehouse +* Michael Rooke * Mohamed Awnallah +* Olaoluwa Osuntokun +* Oliver Gugger * Ononiwu Maureen Chiamaka +* Sam Korn +* saubyk +* Simone Ragonesi * Slyghtning +* tdb3 * Tee8z +* testwill +* Thabokani +* threewebcode +* Tom Kirkpatrick * Turtle -* Hao Wang +* twofaktor +* vuittont60 * w3irdrobot +* weiliy +* xiaoxianBoy * Yong Yu +* zhiqiangxu * Ziggie diff --git a/feature/default_sets.go b/feature/default_sets.go index 3b7e0f7ea..cc802fe85 100644 --- a/feature/default_sets.go +++ b/feature/default_sets.go @@ -79,6 +79,11 @@ var defaultSetDesc = setDesc{ SetInit: {}, // I SetNodeAnn: {}, // N }, + lnwire.RouteBlindingOptional: { + SetInit: {}, // I + SetNodeAnn: {}, // N + SetInvoice: {}, // 9 + }, lnwire.ShutdownAnySegwitOptional: { SetInit: {}, // I SetNodeAnn: {}, // N diff --git a/feature/deps.go b/feature/deps.go index b9fec2da0..350d49acf 100644 --- a/feature/deps.go +++ b/feature/deps.go @@ -79,6 +79,12 @@ var deps = depDesc{ lnwire.AnchorsZeroFeeHtlcTxOptional: {}, lnwire.ExplicitChannelTypeOptional: {}, }, + lnwire.RouteBlindingOptional: { + lnwire.TLVOnionPayloadOptional: {}, + }, + lnwire.RouteBlindingRequired: { + lnwire.TLVOnionPayloadRequired: {}, + }, } // ValidateDeps asserts that a feature vector sets all features and their diff --git a/feature/manager.go b/feature/manager.go index 98317eb8f..c7029e893 100644 --- a/feature/manager.go +++ b/feature/manager.go @@ -60,6 +60,9 @@ type Config struct { // segwit witness versions for co-op closes. NoAnySegwit bool + // NoRouteBlinding unsets route blinding feature bits. + NoRouteBlinding bool + // CustomFeatures is a set of custom features to advertise in each // set. CustomFeatures map[Set][]lnwire.FeatureBit @@ -123,6 +126,8 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) { raw.Unset(lnwire.PaymentAddrRequired) raw.Unset(lnwire.MPPOptional) raw.Unset(lnwire.MPPRequired) + raw.Unset(lnwire.RouteBlindingOptional) + raw.Unset(lnwire.RouteBlindingRequired) raw.Unset(lnwire.AMPOptional) raw.Unset(lnwire.AMPRequired) raw.Unset(lnwire.KeysendOptional) @@ -179,7 +184,10 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) { raw.Unset(lnwire.SimpleTaprootChannelsOptionalStaging) raw.Unset(lnwire.SimpleTaprootChannelsRequiredStaging) } - + if cfg.NoRouteBlinding { + raw.Unset(lnwire.RouteBlindingOptional) + raw.Unset(lnwire.RouteBlindingRequired) + } for _, custom := range cfg.CustomFeatures[set] { if custom > set.Maximum() { return nil, fmt.Errorf("feature bit: %v "+ diff --git a/fn/map.go b/fn/map.go new file mode 100644 index 000000000..071da9872 --- /dev/null +++ b/fn/map.go @@ -0,0 +1,44 @@ +package fn + +import ( + "fmt" + + "golang.org/x/exp/maps" +) + +// KeySet converts a map into a Set containing the keys of the map. +func KeySet[K comparable, V any](m map[K]V) Set[K] { + return NewSet(maps.Keys(m)...) +} + +// NewSubMapIntersect returns a sub-map of `m` containing only the keys found in +// both `m` and the `keys` slice. +func NewSubMapIntersect[K comparable, V any](m map[K]V, keys []K) map[K]V { + result := make(map[K]V) + for _, k := range keys { + v, ok := m[k] + if !ok { + continue + } + + result[k] = v + } + + return result +} + +// NewSubMap creates a sub-map from a given map using specified keys. It errors +// if any of the keys is not found in the map. +func NewSubMap[K comparable, V any](m map[K]V, keys []K) (map[K]V, error) { + result := make(map[K]V, len(keys)) + for _, k := range keys { + v, ok := m[k] + if !ok { + return nil, fmt.Errorf("NewSubMap: missing key %v", k) + } + + result[k] = v + } + + return result, nil +} diff --git a/fn/map_test.go b/fn/map_test.go new file mode 100644 index 000000000..b0694bde6 --- /dev/null +++ b/fn/map_test.go @@ -0,0 +1,135 @@ +package fn + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestKeySet(t *testing.T) { + testMap := map[string]int{"a": 1, "b": 2, "c": 3} + expected := NewSet([]string{"a", "b", "c"}...) + + require.Equal(t, expected, KeySet(testMap)) +} + +// TestNewSubMap tests the NewSubMap function with various input cases. +func TestNewSubMap(t *testing.T) { + tests := []struct { + name string + original map[int]string + keys []int + expected map[int]string + wantErr bool + }{ + { + name: "Successful submap creation", + original: map[int]string{ + 1: "apple", + 2: "banana", + 3: "cherry", + }, + keys: []int{1, 3}, + expected: map[int]string{ + 1: "apple", + 3: "cherry", + }, + wantErr: false, + }, + { + name: "Key not found", + original: map[int]string{ + 1: "apple", + 2: "banana", + }, + keys: []int{1, 4}, + expected: nil, + wantErr: true, + }, + { + name: "Empty keys list", + original: map[int]string{ + 1: "apple", + 2: "banana", + }, + keys: []int{}, + expected: map[int]string{}, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := NewSubMap(tt.original, tt.keys) + if tt.wantErr { + require.ErrorContains( + t, err, "NewSubMap: missing key", + ) + + require.Nil(t, result) + + return + } + + require.NoError(t, err) + require.Equal(t, tt.expected, result) + }) + } +} + +// TestNewSubMapIntersect tests the NewSubMapIntersect function for correctness. +func TestNewSubMapIntersect(t *testing.T) { + tests := []struct { + name string + original map[int]string + keys []int + expected map[int]string + }{ + { + name: "Successful intersection", + original: map[int]string{ + 1: "apple", + 2: "banana", + 3: "cherry", + 4: "date", + }, + keys: []int{2, 3, 5}, + expected: map[int]string{ + 2: "banana", + 3: "cherry", + }, + }, + { + name: "No intersection", + original: map[int]string{ + 1: "apple", + 2: "banana", + }, + keys: []int{3, 4}, + expected: map[int]string{}, + }, + { + name: "Empty original map", + original: map[int]string{}, + keys: []int{1, 2}, + expected: map[int]string{}, + }, + { + name: "Empty keys list", + original: map[int]string{ + 1: "apple", + 2: "banana", + }, + keys: []int{}, + expected: map[int]string{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal( + t, tt.expected, + NewSubMapIntersect(tt.original, tt.keys)) + }) + } +} diff --git a/fn/slice.go b/fn/slice.go index fe821bff7..f42d34544 100644 --- a/fn/slice.go +++ b/fn/slice.go @@ -1,5 +1,13 @@ package fn +import "golang.org/x/exp/constraints" + +// Number is a type constraint for all numeric types in Go (integers, +// float and complex numbers) +type Number interface { + constraints.Integer | constraints.Float | constraints.Complex +} + // All returns true when the supplied predicate evaluates to true for all of // the values in the slice. func All[A any](pred func(A) bool, s []A) bool { @@ -168,3 +176,32 @@ func ZipWith[A, B, C any](f func(A, B) C, a []A, b []B) []C { return res } + +// SliceToMap converts a slice to a map using the provided key and value +// functions. +func SliceToMap[A any, K comparable, V any](s []A, keyFunc func(A) K, + valueFunc func(A) V) map[K]V { + + res := make(map[K]V, len(s)) + for _, val := range s { + key := keyFunc(val) + value := valueFunc(val) + res[key] = value + } + + return res +} + +// Sum calculates the sum of a slice of numbers, `items`. +func Sum[B Number](items []B) B { + return Foldl(func(a, b B) B { + return a + b + }, 0, items) +} + +// HasDuplicates checks if the given slice contains any duplicate elements. +// It returns false if there are no duplicates in the slice (i.e., all elements +// are unique), otherwise returns false. +func HasDuplicates[A comparable](items []A) bool { + return len(NewSet(items...)) != len(items) +} diff --git a/fn/slice_test.go b/fn/slice_test.go index 0178cc946..016ef87d3 100644 --- a/fn/slice_test.go +++ b/fn/slice_test.go @@ -1,6 +1,7 @@ package fn import ( + "fmt" "slices" "testing" @@ -136,3 +137,147 @@ func TestZipWith(t *testing.T) { z, []bool{false, true, false, false, false}, )) } + +// TestSum checks if the Sum function correctly calculates the sum of the +// numbers in the slice. +func TestSum(t *testing.T) { + tests := []struct { + name string + items interface{} + result interface{} + }{ + { + name: "Sum of positive integers", + items: []int{1, 2, 3}, + result: 6, + }, + { + name: "Sum of negative integers", + items: []int{-1, -2, -3}, + result: -6, + }, + { + name: "Sum of float numbers", + items: []float64{1.1, 2.2, 3.3}, + result: 6.6, + }, + { + name: "Sum of complex numbers", + items: []complex128{ + complex(1, 1), + complex(2, 2), + complex(3, 3), + }, + result: complex(6, 6), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + switch v := tt.items.(type) { + case []int: + require.Equal(t, tt.result, Sum(v)) + case []float64: + require.Equal(t, tt.result, Sum(v)) + case []complex128: + require.Equal(t, tt.result, Sum(v)) + } + }) + } +} + +// TestSliceToMap tests the SliceToMap function. +func TestSliceToMap(t *testing.T) { + tests := []struct { + name string + slice []int + keyFunc func(int) int + valueFunc func(int) string + expected map[int]string + }{ + { + name: "Integers to string map", + slice: []int{1, 2, 3}, + keyFunc: func(a int) int { return a }, + valueFunc: func(a int) string { + return fmt.Sprintf("Value%d", a) + }, + expected: map[int]string{ + 1: "Value1", + 2: "Value2", + 3: "Value3", + }, + }, + { + name: "Duplicates in slice", + slice: []int{1, 2, 2, 3}, + keyFunc: func(a int) int { return a }, + valueFunc: func(a int) string { + return fmt.Sprintf("Value%d", a) + }, + expected: map[int]string{ + 1: "Value1", + 2: "Value2", + 3: "Value3", + }, + }, + { + name: "Empty slice", + slice: []int{}, + keyFunc: func(a int) int { return a }, + valueFunc: func(a int) string { + return fmt.Sprintf("Value%d", a) + }, + expected: map[int]string{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal( + t, tt.expected, + SliceToMap(tt.slice, tt.keyFunc, tt.valueFunc), + ) + }) + } +} + +// TestHasDuplicates tests the HasDuplicates function. +func TestHasDuplicates(t *testing.T) { + // Define test cases. + testCases := []struct { + name string + items []int + want bool + }{ + { + name: "All unique", + items: []int{1, 2, 3, 4, 5}, + want: false, + }, + { + name: "Some duplicates", + items: []int{1, 2, 2, 3, 4}, + want: true, + }, + { + name: "No items", + items: []int{}, + want: false, + }, + { + name: "All duplicates", + items: []int{1, 1, 1, 1}, + want: true, + }, + } + + // Execute each test case. + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got := HasDuplicates(tc.items) + + require.Equal(t, tc.want, got) + }) + } +} diff --git a/funding/manager.go b/funding/manager.go index 086fb9c20..77fba3072 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -539,6 +539,12 @@ type Config struct { // abstracts away the handling of many alias functions. AliasManager aliasHandler + // IsSweeperOutpoint queries the sweeper store for successfully + // published sweeps. This is useful to decide for the internal wallet + // backed funding flow to not use utxos still being swept by the sweeper + // subsystem. + IsSweeperOutpoint func(wire.OutPoint) bool + // AuxLeafStore is an optional store that can be used to store auxiliary // leaves for certain custom channel types. AuxLeafStore fn.Option[lnwallet.AuxLeafStore] @@ -1580,6 +1586,8 @@ func (f *Manager) fundeeProcessOpenChannel(peer lnpeer.Peer, // Fail the funding flow. flowErr := fmt.Errorf("channel acceptor blocked " + "zero-conf channel negotiation") + log.Errorf("Cancelling funding flow for %v based on "+ + "channel acceptor response: %v", cid, flowErr) f.failFundingFlow(peer, cid, flowErr) return } @@ -1594,6 +1602,9 @@ func (f *Manager) fundeeProcessOpenChannel(peer lnpeer.Peer, // Fail the funding flow. flowErr := fmt.Errorf("scid-alias feature " + "must be negotiated for zero-conf") + log.Errorf("Cancelling funding flow for "+ + "zero-conf channel %v: %v", cid, + flowErr) f.failFundingFlow(peer, cid, flowErr) return } @@ -1610,7 +1621,8 @@ func (f *Manager) fundeeProcessOpenChannel(peer lnpeer.Peer, case public && scid: err = fmt.Errorf("option-scid-alias chantype for public " + "channel") - log.Error(err) + log.Errorf("Cancelling funding flow for public channel %v "+ + "with scid-alias: %v", cid, err) f.failFundingFlow(peer, cid, err) return @@ -1619,7 +1631,8 @@ func (f *Manager) fundeeProcessOpenChannel(peer lnpeer.Peer, // unadvertised channels for now. case commitType.IsTaproot() && public: err = fmt.Errorf("taproot channel type for public channel") - log.Error(err) + log.Errorf("Cancelling funding flow for public taproot "+ + "channel %v: %v", cid, err) f.failFundingFlow(peer, cid, err) return @@ -4731,11 +4744,27 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) { MinConfs: msg.MinConfs, CommitType: commitType, ChanFunder: msg.ChanFunder, - ZeroConf: zeroConf, - OptionScidAlias: scid, - ScidAliasFeature: scidFeatureVal, - Memo: msg.Memo, - TapscriptRoot: tapscriptRoot, + // Unconfirmed Utxos which are marked by the sweeper subsystem + // are excluded from the coin selection because they are not + // final and can be RBFed by the sweeper subsystem. + AllowUtxoForFunding: func(u lnwallet.Utxo) bool { + // Utxos with at least 1 confirmation are safe to use + // for channel openings because they don't bare the risk + // of being replaced (BIP 125 RBF). + if u.Confirmations > 0 { + return true + } + + // Query the sweeper storage to make sure we don't use + // an unconfirmed utxo still in use by the sweeper + // subsystem. + return !f.cfg.IsSweeperOutpoint(u.OutPoint) + }, + ZeroConf: zeroConf, + OptionScidAlias: scid, + ScidAliasFeature: scidFeatureVal, + Memo: msg.Memo, + TapscriptRoot: tapscriptRoot, } reservation, err := f.cfg.Wallet.InitChannelReservation(req) diff --git a/funding/manager_test.go b/funding/manager_test.go index 1bd9f9f77..fb6fa8cd7 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -556,6 +556,11 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, return nil, nil }, AliasManager: aliasMgr, + // For unit tests we default to false meaning that no funds + // originated from the sweeper. + IsSweeperOutpoint: func(wire.OutPoint) bool { + return false + }, } for _, op := range options { diff --git a/go.mod b/go.mod index 75e43e573..337bb9a84 100644 --- a/go.mod +++ b/go.mod @@ -5,19 +5,19 @@ require ( github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 github.com/andybalholm/brotli v1.0.4 github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240403021926-ae5533602c46 - github.com/btcsuite/btcd/btcec/v2 v2.3.2 + github.com/btcsuite/btcd/btcec/v2 v2.3.3 github.com/btcsuite/btcd/btcutil v1.1.5 github.com/btcsuite/btcd/btcutil/psbt v1.1.8 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f - github.com/btcsuite/btcwallet v0.16.10-0.20240410030101-6fe19a472a62 + github.com/btcsuite/btcwallet v0.16.10-0.20240404104514-b2f31f9045fb github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 github.com/btcsuite/btcwallet/walletdb v1.4.2 github.com/btcsuite/btcwallet/wtxmgr v1.5.3 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f github.com/davecgh/go-spew v1.1.1 - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 github.com/go-errors/errors v1.0.1 github.com/gorilla/websocket v1.5.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 @@ -30,16 +30,16 @@ require ( github.com/jessevdk/go-flags v1.4.0 github.com/jrick/logrotate v1.0.0 github.com/kkdai/bstream v1.0.0 - github.com/lightninglabs/neutrino v0.16.0 + github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd github.com/lightninglabs/neutrino/cache v1.1.2 github.com/lightningnetwork/lightning-onion v1.2.1-0.20230823005744-06182b1d7d2f github.com/lightningnetwork/lnd/cert v1.2.2 github.com/lightningnetwork/lnd/clock v1.1.1 github.com/lightningnetwork/lnd/fn v1.0.5 github.com/lightningnetwork/lnd/healthcheck v1.2.4 - github.com/lightningnetwork/lnd/kvdb v1.4.6 + github.com/lightningnetwork/lnd/kvdb v1.4.8 github.com/lightningnetwork/lnd/queue v1.1.1 - github.com/lightningnetwork/lnd/sqldb v1.0.1 + github.com/lightningnetwork/lnd/sqldb v1.0.2 github.com/lightningnetwork/lnd/ticker v1.1.1 github.com/lightningnetwork/lnd/tlv v1.2.5 github.com/lightningnetwork/lnd/tor v1.1.2 @@ -51,12 +51,12 @@ require ( github.com/urfave/cli v1.22.9 go.etcd.io/etcd/client/pkg/v3 v3.5.7 go.etcd.io/etcd/client/v3 v3.5.7 - golang.org/x/crypto v0.21.0 + golang.org/x/crypto v0.22.0 golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 golang.org/x/net v0.22.0 golang.org/x/sync v0.6.0 - golang.org/x/term v0.18.0 + golang.org/x/term v0.19.0 golang.org/x/time v0.3.0 google.golang.org/grpc v1.59.0 google.golang.org/protobuf v1.33.0 @@ -82,8 +82,8 @@ require ( github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect - github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect - github.com/decred/dcrd/lru v1.0.0 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/decred/dcrd/lru v1.1.2 // indirect github.com/docker/cli v20.10.17+incompatible // indirect github.com/docker/docker v24.0.7+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect @@ -98,7 +98,7 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.4.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -119,7 +119,7 @@ require ( github.com/juju/testing v0.0.0-20220203020004-a0ff61f03494 // indirect github.com/lib/pq v1.10.9 // indirect github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect @@ -172,7 +172,7 @@ require ( go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.17.0 // indirect golang.org/x/mod v0.16.0 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.19.0 // indirect google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect @@ -183,10 +183,10 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect - modernc.org/libc v1.41.0 // indirect + modernc.org/libc v1.49.3 // indirect modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.7.2 // indirect - modernc.org/sqlite v1.29.5 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/sqlite v1.29.8 // indirect modernc.org/strutil v1.2.0 // indirect modernc.org/token v1.1.0 // indirect sigs.k8s.io/yaml v1.2.0 // indirect diff --git a/go.sum b/go.sum index c3f8b2e82..ecad62827 100644 --- a/go.sum +++ b/go.sum @@ -77,8 +77,8 @@ github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240403021926-ae5533602c46 h1:tjpNT github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240403021926-ae5533602c46/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= -github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= +github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= @@ -92,8 +92,8 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtyd github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcwallet v0.16.10-0.20240410030101-6fe19a472a62 h1:MtcTVTcDbGdTJhfDc7LLikojyl0PYtSRNLwoRaLVbWI= -github.com/btcsuite/btcwallet v0.16.10-0.20240410030101-6fe19a472a62/go.mod h1:2C3Q/MhYAKmk7F+Tey6LfKtKRTdQsrCf8AAAzzDPmH4= +github.com/btcsuite/btcwallet v0.16.10-0.20240404104514-b2f31f9045fb h1:qoIOlBPRZWtfpcbQlNFf67Wz8ZlXo+mxQc9Pnbm/iqU= +github.com/btcsuite/btcwallet v0.16.10-0.20240404104514-b2f31f9045fb/go.mod h1:2C3Q/MhYAKmk7F+Tey6LfKtKRTdQsrCf8AAAzzDPmH4= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 h1:poyHFf7+5+RdxNp5r2T6IBRD7RyraUsYARYbp/7t4D8= github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4/go.mod h1:GETGDQuyq+VFfH1S/+/7slLM/9aNa4l7P4ejX6dJfb0= github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 h1:UZo7YRzdHbwhK7Rhv3PO9bXgTxiOH45edK5qdsdiatk= @@ -162,12 +162,15 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/decred/dcrd/lru v1.1.2 h1:KdCzlkxppuoIDGEvCGah1fZRicrDH36IipvlB1ROkFY= +github.com/decred/dcrd/lru v1.1.2/go.mod h1:gEdCVgXs1/YoBvFWt7Scgknbhwik3FgVSzlnCcXL2N8= github.com/dhui/dktest v0.4.0 h1:z05UmuXZHO/bgj/ds2bGMBu8FI4WA+Ag/m3ghL+om7M= github.com/dhui/dktest v0.4.0/go.mod h1:v/Dbz1LgCBOi2Uki2nUqLBGa83hWBGFMu5MrgMDCc78= github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= @@ -283,14 +286,14 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= @@ -433,8 +436,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= -github.com/lightninglabs/neutrino v0.16.0 h1:YNTQG32fPR/Zg0vvJVI65OBH8l3U18LSXXtX91hx0q0= -github.com/lightninglabs/neutrino v0.16.0/go.mod h1:x3OmY2wsA18+Kc3TSV2QpSUewOCiscw2mKpXgZv2kZk= +github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd h1:D8aRocHpoCv43hL8egXEMYyPmyOiefFHZ66338KQB2s= +github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd/go.mod h1:x3OmY2wsA18+Kc3TSV2QpSUewOCiscw2mKpXgZv2kZk= github.com/lightninglabs/neutrino/cache v1.1.2 h1:C9DY/DAPaPxbFC+xNNEI/z1SJY9GS3shmlu5hIQ798g= github.com/lightninglabs/neutrino/cache v1.1.2/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo= github.com/lightninglabs/protobuf-go-hex-display v1.30.0-hex-display h1:pRdza2wleRN1L2fJXd6ZoQ9ZegVFTAb2bOQfruJPKcY= @@ -449,12 +452,12 @@ github.com/lightningnetwork/lnd/fn v1.0.5 h1:ffDgMSn83avw6rNzxhbt6w5/2oIrwQKTPGf github.com/lightningnetwork/lnd/fn v1.0.5/go.mod h1:P027+0CyELd92H9gnReUkGGAqbFA1HwjHWdfaDFD51U= github.com/lightningnetwork/lnd/healthcheck v1.2.4 h1:lLPLac+p/TllByxGSlkCwkJlkddqMP5UCoawCj3mgFQ= github.com/lightningnetwork/lnd/healthcheck v1.2.4/go.mod h1:G7Tst2tVvWo7cx6mSBEToQC5L1XOGxzZTPB29g9Rv2I= -github.com/lightningnetwork/lnd/kvdb v1.4.6 h1:2pkouBCXW2LHYanvNCXz8Pau16NTsTI8cO6JmBIBjVM= -github.com/lightningnetwork/lnd/kvdb v1.4.6/go.mod h1:sBwBi8uofKqgzWkSe9bPoTZKmanASPFANFTCWa0/iYo= +github.com/lightningnetwork/lnd/kvdb v1.4.8 h1:xH0a5Vi1yrcZ5BEeF2ba3vlKBRxrL9uYXlWTjOjbNTY= +github.com/lightningnetwork/lnd/kvdb v1.4.8/go.mod h1:J2diNABOoII9UrMnxXS5w7vZwP7CA1CStrl8MnIrb3A= github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQZznVb18iNMI= github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4= -github.com/lightningnetwork/lnd/sqldb v1.0.1 h1:lpNoJ6qRh3D02oeIUsKQLZUzjcgZ9ppMZNZnwrpBBmY= -github.com/lightningnetwork/lnd/sqldb v1.0.1/go.mod h1:nSovU1U+gTPDWhfwmXu/kW8l8EJpwbvZQ05ijnkQzkA= +github.com/lightningnetwork/lnd/sqldb v1.0.2 h1:PfuYzScYMD9/QonKo/QvgsbXfTnH5DfldIimkfdW4Bk= +github.com/lightningnetwork/lnd/sqldb v1.0.2/go.mod h1:V2Xl6JNWLTKE97WJnwfs0d0TYJdIQTqK8/3aAwkd3qI= github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM= github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA= github.com/lightningnetwork/lnd/tlv v1.2.5 h1:/VsoWw628t78OiDN90pHDbqwOcuZ9JMicxXZVQjBwX0= @@ -472,8 +475,8 @@ github.com/mattn/go-isatty v0.0.0-20160806122752-66b8e73f3f5c/go.mod h1:M+lRXTBq github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= @@ -684,8 +687,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -839,13 +842,13 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1041,18 +1044,28 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk= +modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA= +modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= +modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= -modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk= -modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY= +modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg= +modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= -modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= -modernc.org/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE= -modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.29.8 h1:nGKglNx9K5v0As+zF0/Gcl1kMkmaU1XynYyq92PbsC8= +modernc.org/sqlite v1.29.8/go.mod h1:lQPm27iqa4UNZpmr4Aor0MH0HkCLbt1huYDfWylLZFk= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/htlcswitch/circuit.go b/htlcswitch/circuit.go index efb2a4779..700b087f5 100644 --- a/htlcswitch/circuit.go +++ b/htlcswitch/circuit.go @@ -205,6 +205,12 @@ func (c *PaymentCircuit) Decode(r io.Reader) error { // Test encrypter. c.ErrorEncrypter = NewMockObfuscator() + case hop.EncrypterTypeIntroduction: + c.ErrorEncrypter = hop.NewIntroductionErrorEncrypter() + + case hop.EncrypterTypeRelaying: + c.ErrorEncrypter = hop.NewRelayingErrorEncrypter() + default: return UnknownEncrypterType(encrypterType) } diff --git a/htlcswitch/hop/error_encryptor.go b/htlcswitch/hop/error_encryptor.go index 7b6a3dd1a..23272ec00 100644 --- a/htlcswitch/hop/error_encryptor.go +++ b/htlcswitch/hop/error_encryptor.go @@ -25,8 +25,26 @@ const ( // EncrypterTypeMock is used to identify a mock obfuscator instance. EncrypterTypeMock = 2 + + // EncrypterTypeIntroduction is used to identify a sphinx onion error + // encrypter where we are the introduction node in a blinded route. It + // has the same functionality as EncrypterTypeSphinx, but is used to + // mark our special-case error handling. + EncrypterTypeIntroduction = 3 + + // EncrypterTypeRelaying is used to identify a sphinx onion error + // encryper where we are a relaying node in a blinded route. It has + // the same functionality as a EncrypterTypeSphinx, but is used to mark + // our special-case error handling. + EncrypterTypeRelaying = 4 ) +// IsBlinded returns a boolean indicating whether the error encrypter belongs +// to a blinded route. +func (e EncrypterType) IsBlinded() bool { + return e == EncrypterTypeIntroduction || e == EncrypterTypeRelaying +} + // ErrorEncrypterExtracter defines a function signature that extracts an // ErrorEncrypter from an sphinx OnionPacket. type ErrorEncrypterExtracter func(*btcec.PublicKey) (ErrorEncrypter, @@ -197,9 +215,72 @@ func (s *SphinxErrorEncrypter) Reextract( s.OnionErrorEncrypter = sphinxEncrypter.OnionErrorEncrypter return nil - } // A compile time check to ensure SphinxErrorEncrypter implements the // ErrorEncrypter interface. var _ ErrorEncrypter = (*SphinxErrorEncrypter)(nil) + +// A compile time check to ensure that IntroductionErrorEncrypter implements +// the ErrorEncrypter interface. +var _ ErrorEncrypter = (*IntroductionErrorEncrypter)(nil) + +// IntroductionErrorEncrypter is a wrapper type on SphinxErrorEncrypter which +// is used to signal that we have special HTLC error handling for this hop. +type IntroductionErrorEncrypter struct { + // ErrorEncrypter is the underlying error encrypter, embedded + // directly in the struct so that we don't have to re-implement the + // ErrorEncrypter interface. + ErrorEncrypter +} + +// NewIntroductionErrorEncrypter returns a blank IntroductionErrorEncrypter. +func NewIntroductionErrorEncrypter() *IntroductionErrorEncrypter { + return &IntroductionErrorEncrypter{ + ErrorEncrypter: NewSphinxErrorEncrypter(), + } +} + +// Type returns the identifier for an introduction error encrypter. +func (i *IntroductionErrorEncrypter) Type() EncrypterType { + return EncrypterTypeIntroduction +} + +// Reextract rederives the error encrypter from the currently held EphemeralKey, +// relying on the logic in the underlying SphinxErrorEncrypter. +func (i *IntroductionErrorEncrypter) Reextract( + extract ErrorEncrypterExtracter) error { + + return i.ErrorEncrypter.Reextract(extract) +} + +// A compile time check to ensure that RelayingErrorEncrypte implements +// the ErrorEncrypter interface. +var _ ErrorEncrypter = (*RelayingErrorEncrypter)(nil) + +// RelayingErrorEncrypter is a wrapper type on SphinxErrorEncrypter which +// is used to signal that we have special HTLC error handling for this hop. +type RelayingErrorEncrypter struct { + ErrorEncrypter +} + +// NewRelayingErrorEncrypter returns a blank RelayingErrorEncrypter with +// an underlying SphinxErrorEncrypter. +func NewRelayingErrorEncrypter() *RelayingErrorEncrypter { + return &RelayingErrorEncrypter{ + ErrorEncrypter: NewSphinxErrorEncrypter(), + } +} + +// Type returns the identifier for a relaying error encrypter. +func (r *RelayingErrorEncrypter) Type() EncrypterType { + return EncrypterTypeRelaying +} + +// Reextract rederives the error encrypter from the currently held EphemeralKey, +// relying on the logic in the underlying SphinxErrorEncrypter. +func (r *RelayingErrorEncrypter) Reextract( + extract ErrorEncrypterExtracter) error { + + return r.ErrorEncrypter.Reextract(extract) +} diff --git a/htlcswitch/hop/fuzz_test.go b/htlcswitch/hop/fuzz_test.go index cbbe88260..e5c00b525 100644 --- a/htlcswitch/hop/fuzz_test.go +++ b/htlcswitch/hop/fuzz_test.go @@ -97,19 +97,37 @@ func hopFromPayload(p *Payload) (*route.Hop, uint64) { // FuzzPayloadFinal fuzzes final hop payloads, providing the additional context // that the hop should be final (which is usually obtained by the structure -// of the sphinx packet). -func FuzzPayloadFinal(f *testing.F) { - fuzzPayload(f, true) +// of the sphinx packet) for the case where a blinding point was provided in +// UpdateAddHtlc. +func FuzzPayloadFinalBlinding(f *testing.F) { + fuzzPayload(f, true, true) +} + +// FuzzPayloadFinal fuzzes final hop payloads, providing the additional context +// that the hop should be final (which is usually obtained by the structure +// of the sphinx packet) for the case where no blinding point was provided in +// UpdateAddHtlc. +func FuzzPayloadFinalNoBlinding(f *testing.F) { + fuzzPayload(f, true, false) } // FuzzPayloadIntermediate fuzzes intermediate hop payloads, providing the // additional context that a hop should be intermediate (which is usually -// obtained by the structure of the sphinx packet). -func FuzzPayloadIntermediate(f *testing.F) { - fuzzPayload(f, false) +// obtained by the structure of the sphinx packet) for the case where a +// blinding point was provided in UpdateAddHtlc. +func FuzzPayloadIntermediateBlinding(f *testing.F) { + fuzzPayload(f, false, true) } -func fuzzPayload(f *testing.F, finalPayload bool) { +// FuzzPayloadIntermediate fuzzes intermediate hop payloads, providing the +// additional context that a hop should be intermediate (which is usually +// obtained by the structure of the sphinx packet) for the case where no +// blinding point was provided in UpdateAddHtlc. +func FuzzPayloadIntermediateNoBlinding(f *testing.F) { + fuzzPayload(f, false, false) +} + +func fuzzPayload(f *testing.F, finalPayload, updateAddBlinded bool) { f.Fuzz(func(t *testing.T, data []byte) { if len(data) > sphinx.MaxPayloadSize { return @@ -117,17 +135,23 @@ func fuzzPayload(f *testing.F, finalPayload bool) { r := bytes.NewReader(data) - payload1, _, err := NewPayloadFromReader(r, finalPayload) + payload1, parsed, err := ParseTLVPayload(r) if err != nil { return } + if err = ValidateParsedPayloadTypes( + parsed, finalPayload, updateAddBlinded, + ); err != nil { + return + } + var b bytes.Buffer hop, nextChanID := hopFromPayload(payload1) err = hop.PackHopPayload(&b, nextChanID, finalPayload) switch { // PackHopPayload refuses to encode an AMP record - // without an MPP record. However, NewPayloadFromReader + // without an MPP record. However, ValidateParsedPayloadTypes // does allow decoding an AMP record without an MPP // record, since validation is done at a later stage. Do // not report a bug for this case. @@ -136,9 +160,9 @@ func fuzzPayload(f *testing.F, finalPayload bool) { // PackHopPayload will not encode regular payloads or final // hops in blinded routes that do not have an amount or expiry - // TLV set. However, NewPayloadFromReader will allow creation - // of payloads where these TLVs are present, but they have - // zero values because validation is done at a later stage. + // TLV set. However, ValidateParsedPayloadTypes will allow + // creation of payloads where these TLVs are present, but they + // have zero values because validation is done at a later stage. case errors.Is(err, route.ErrMissingField): return @@ -146,7 +170,12 @@ func fuzzPayload(f *testing.F, finalPayload bool) { require.NoError(t, err) } - payload2, _, err := NewPayloadFromReader(&b, finalPayload) + payload2, parsed, err := ParseTLVPayload(&b) + require.NoError(t, err) + + err = ValidateParsedPayloadTypes( + parsed, finalPayload, updateAddBlinded, + ) require.NoError(t, err) require.Equal(t, payload1, payload2) diff --git a/htlcswitch/hop/iterator.go b/htlcswitch/hop/iterator.go index df6f5aac7..a89f1b734 100644 --- a/htlcswitch/hop/iterator.go +++ b/htlcswitch/hop/iterator.go @@ -17,8 +17,69 @@ import ( var ( // ErrDecodeFailed is returned when we can't decode blinded data. ErrDecodeFailed = errors.New("could not decode blinded data") + + // ErrNoBlindingPoint is returned when we have not provided a blinding + // point for a validated payload with encrypted data set. + ErrNoBlindingPoint = errors.New("no blinding point set for validated " + + "blinded hop") ) +// RouteRole represents the different types of roles a node can have as a +// recipient of a HTLC. +type RouteRole uint8 + +const ( + // RouteRoleCleartext represents a regular route hop. + RouteRoleCleartext RouteRole = iota + + // RouteRoleIntroduction represents an introduction node in a blinded + // path, characterized by a blinding point in the onion payload. + RouteRoleIntroduction + + // RouteRoleRelaying represents a relaying node in a blinded path, + // characterized by a blinding point in update_add_htlc. + RouteRoleRelaying +) + +// String representation of a role in a route. +func (h RouteRole) String() string { + switch h { + case RouteRoleCleartext: + return "cleartext" + + case RouteRoleRelaying: + return "blinded relay" + + case RouteRoleIntroduction: + return "introduction node" + + default: + return fmt.Sprintf("unknown route role: %d", h) + } +} + +// NewRouteRole returns the role we're playing in a route depending on the +// blinding points set (or not). If we are in the situation where we received +// blinding points in both the update add message and the payload: +// - We must have had a valid update add blinding point, because we were able +// to decrypt our onion to get the payload blinding point. +// - We return a relaying node role, because an introduction node (by +// definition) does not receive a blinding point in update add. +// - We assume the sending node to be buggy (including a payload blinding +// where it shouldn't), and rely on validation elsewhere to handle this. +func NewRouteRole(updateAddBlinding, payloadBlinding bool) RouteRole { + switch { + case updateAddBlinding: + return RouteRoleRelaying + + case payloadBlinding: + return RouteRoleIntroduction + + default: + return RouteRoleCleartext + } +} + // Iterator is an interface that abstracts away the routing information // included in HTLC's which includes the entirety of the payment path of an // HTLC. This interface provides two basic method which carry out: how to @@ -30,8 +91,11 @@ type Iterator interface { // information encoded within the returned ForwardingInfo is to be used // by each hop to authenticate the information given to it by the prior // hop. The payload will also contain any additional TLV fields provided - // by the sender. - HopPayload() (*Payload, error) + // by the sender. The role that this hop plays in the context of + // route blinding (regular, introduction or relaying) is returned + // whenever the payload is successfully parsed, even if we subsequently + // face a validation error. + HopPayload() (*Payload, RouteRole, error) // EncodeNextHop encodes the onion packet destined for the next hop // into the passed io.Writer. @@ -39,8 +103,8 @@ type Iterator interface { // ExtractErrorEncrypter returns the ErrorEncrypter needed for this hop, // along with a failure code to signal if the decoding was successful. - ExtractErrorEncrypter(ErrorEncrypterExtracter) (ErrorEncrypter, - lnwire.FailCode) + ExtractErrorEncrypter(extractor ErrorEncrypterExtracter, + introductionNode bool) (ErrorEncrypter, lnwire.FailCode) } // sphinxHopIterator is the Sphinx implementation of hop iterator which uses @@ -90,29 +154,56 @@ func (r *sphinxHopIterator) EncodeNextHop(w io.Writer) error { // HopPayload returns the set of fields that detail exactly _how_ this hop // should forward the HTLC to the next hop. Additionally, the information // encoded within the returned ForwardingInfo is to be used by each hop to -// authenticate the information given to it by the prior hop. The payload will -// also contain any additional TLV fields provided by the sender. +// authenticate the information given to it by the prior hop. The role that +// this hop plays in the context of route blinding (regular, introduction or +// relaying) is returned whenever the payload is successfully parsed, even if +// we subsequently face a validation error. The payload will also contain any +// additional TLV fields provided by the sender. // // NOTE: Part of the HopIterator interface. -func (r *sphinxHopIterator) HopPayload() (*Payload, error) { +func (r *sphinxHopIterator) HopPayload() (*Payload, RouteRole, error) { switch r.processedPacket.Payload.Type { // If this is the legacy payload, then we'll extract the information // directly from the pre-populated ForwardingInstructions field. case sphinx.PayloadLegacy: fwdInst := r.processedPacket.ForwardingInstructions - return NewLegacyPayload(fwdInst), nil + return NewLegacyPayload(fwdInst), RouteRoleCleartext, nil // Otherwise, if this is the TLV payload, then we'll make a new stream // to decode only what we need to make routing decisions. case sphinx.PayloadTLV: isFinal := r.processedPacket.Action == sphinx.ExitNode - payload, parsed, err := NewPayloadFromReader( + payload, parsed, err := ParseTLVPayload( bytes.NewReader(r.processedPacket.Payload.Payload), - isFinal, ) if err != nil { - return nil, err + // If we couldn't even parse our payload then we do + // a best-effort of determining our role in a blinded + // route, accepting that we can't know whether we + // were the introduction node (as the payload + // is not parseable). + routeRole := RouteRoleCleartext + if r.blindingKit.UpdateAddBlinding.IsSome() { + routeRole = RouteRoleRelaying + } + + return nil, routeRole, err + } + + // Now that we've parsed our payload we can determine which + // role we're playing in the route. + _, payloadBlinding := parsed[record.BlindingPointOnionType] + routeRole := NewRouteRole( + r.blindingKit.UpdateAddBlinding.IsSome(), + payloadBlinding, + ) + + if err := ValidateTLVPayload( + parsed, isFinal, + r.blindingKit.UpdateAddBlinding.IsSome(), + ); err != nil { + return nil, routeRole, err } // If we had an encrypted data payload present, pull out our @@ -122,17 +213,18 @@ func (r *sphinxHopIterator) HopPayload() (*Payload, error) { payload, isFinal, parsed, ) if err != nil { - return nil, err + return nil, routeRole, err } payload.FwdInfo = *fwdInfo } - return payload, err + return payload, routeRole, nil default: - return nil, fmt.Errorf("unknown sphinx payload type: %v", - r.processedPacket.Payload.Type) + return nil, RouteRoleCleartext, + fmt.Errorf("unknown sphinx payload type: %v", + r.processedPacket.Payload.Type) } } @@ -143,9 +235,31 @@ func (r *sphinxHopIterator) HopPayload() (*Payload, error) { // // NOTE: Part of the HopIterator interface. func (r *sphinxHopIterator) ExtractErrorEncrypter( - extracter ErrorEncrypterExtracter) (ErrorEncrypter, lnwire.FailCode) { + extracter ErrorEncrypterExtracter, introductionNode bool) ( + ErrorEncrypter, lnwire.FailCode) { - return extracter(r.ogPacket.EphemeralKey) + encrypter, errCode := extracter(r.ogPacket.EphemeralKey) + if errCode != lnwire.CodeNone { + return nil, errCode + } + + // If we're in a blinded path, wrap the error encrypter that we just + // derived in a "marker" type which we'll use to know what type of + // error we're handling. + switch { + case introductionNode: + return &IntroductionErrorEncrypter{ + ErrorEncrypter: encrypter, + }, errCode + + case r.blindingKit.UpdateAddBlinding.IsSome(): + return &RelayingErrorEncrypter{ + ErrorEncrypter: encrypter, + }, errCode + + default: + return encrypter, errCode + } } // BlindingProcessor is an interface that provides the cryptographic operations @@ -182,35 +296,16 @@ type BlindingKit struct { IncomingAmount lnwire.MilliSatoshi } -// validateBlindingPoint validates that only one blinding point is present for -// the hop and returns the relevant one. -func (b *BlindingKit) validateBlindingPoint(payloadBlinding *btcec.PublicKey, - isFinalHop bool) (*btcec.PublicKey, error) { +// getBlindingPoint returns either the payload or updateAddHtlc blinding point, +// assuming that validation that these values are appropriately set has already +// been handled elsewhere. +func (b *BlindingKit) getBlindingPoint(payloadBlinding *btcec.PublicKey) ( + *btcec.PublicKey, error) { - // Bolt 04: if encrypted_recipient_data is present: - // - if blinding_point (in update add) is set: - // - MUST error if current_blinding_point is set (in payload) - // - otherwise: - // - MUST return an error if current_blinding_point is not present - // (in payload) payloadBlindingSet := payloadBlinding != nil updateBlindingSet := b.UpdateAddBlinding.IsSome() switch { - case !(payloadBlindingSet || updateBlindingSet): - return nil, ErrInvalidPayload{ - Type: record.BlindingPointOnionType, - Violation: OmittedViolation, - FinalHop: isFinalHop, - } - - case payloadBlindingSet && updateBlindingSet: - return nil, ErrInvalidPayload{ - Type: record.BlindingPointOnionType, - Violation: IncludedViolation, - FinalHop: isFinalHop, - } - case payloadBlindingSet: return payloadBlinding, nil @@ -223,9 +318,10 @@ func (b *BlindingKit) validateBlindingPoint(payloadBlinding *btcec.PublicKey, } return pk.Val, nil - } - return nil, fmt.Errorf("expected blinded point set") + default: + return nil, ErrNoBlindingPoint + } } // DecryptAndValidateFwdInfo performs all operations required to decrypt and @@ -235,11 +331,10 @@ func (b *BlindingKit) DecryptAndValidateFwdInfo(payload *Payload, *ForwardingInfo, error) { // We expect this function to be called when we have encrypted data - // present, and a blinding key is set either in the payload or the + // present, and expect validation to already have ensured that a + // blinding key is set either in the payload or the // update_add_htlc message. - blindingPoint, err := b.validateBlindingPoint( - payload.blindingPoint, isFinalHop, - ) + blindingPoint, err := b.getBlindingPoint(payload.blindingPoint) if err != nil { return nil, err } diff --git a/htlcswitch/hop/iterator_test.go b/htlcswitch/hop/iterator_test.go index 60919333b..e69361b0a 100644 --- a/htlcswitch/hop/iterator_test.go +++ b/htlcswitch/hop/iterator_test.go @@ -88,10 +88,10 @@ func TestSphinxHopIteratorForwardingInstructions(t *testing.T) { for i, testCase := range testCases { iterator.processedPacket = testCase.sphinxPacket - pld, err := iterator.HopPayload() - if err != nil { + pld, _, pldErr := iterator.HopPayload() + if pldErr != nil { t.Fatalf("#%v: unable to extract forwarding "+ - "instructions: %v", i, err) + "instructions: %v", i, pldErr) } fwdInfo := pld.ForwardingInfo() @@ -216,24 +216,10 @@ func TestDecryptAndValidateFwdInfo(t *testing.T) { expectedErr error }{ { - name: "no blinding point", - data: validData, - processor: &mockProcessor{}, - expectedErr: ErrInvalidPayload{ - Type: record.BlindingPointOnionType, - Violation: OmittedViolation, - }, - }, - { - name: "both blinding points", - data: validData, - updateAddBlinding: &btcec.PublicKey{}, - payloadBlinding: &btcec.PublicKey{}, - processor: &mockProcessor{}, - expectedErr: ErrInvalidPayload{ - Type: record.BlindingPointOnionType, - Violation: IncludedViolation, - }, + name: "no blinding point", + data: validData, + processor: &mockProcessor{}, + expectedErr: ErrNoBlindingPoint, }, { name: "decryption failed", @@ -265,12 +251,19 @@ func TestDecryptAndValidateFwdInfo(t *testing.T) { }, }, { - name: "valid", + name: "valid using update add", updateAddBlinding: &btcec.PublicKey{}, data: validData, processor: &mockProcessor{}, expectedErr: nil, }, + { + name: "valid using payload", + payloadBlinding: &btcec.PublicKey{}, + data: validData, + processor: &mockProcessor{}, + expectedErr: nil, + }, } for _, testCase := range tests { diff --git a/htlcswitch/hop/payload.go b/htlcswitch/hop/payload.go index 70fdb1403..9e717bbd2 100644 --- a/htlcswitch/hop/payload.go +++ b/htlcswitch/hop/payload.go @@ -133,14 +133,10 @@ func NewLegacyPayload(f *sphinx.HopData) *Payload { } } -// NewPayloadFromReader builds a new Hop from the passed io.Reader and returns -// a map of all the types that were found in the payload. The reader -// should correspond to the bytes encapsulated in a TLV onion payload. The -// final hop bool signals that this payload was the final packet parsed by -// sphinx. -func NewPayloadFromReader(r io.Reader, finalHop bool) (*Payload, - map[tlv.Type][]byte, error) { - +// ParseTLVPayload builds a new Hop from the passed io.Reader and returns +// a map of all the types that were found in the payload. This function +// does not perform validation of TLV types included in the payload. +func ParseTLVPayload(r io.Reader) (*Payload, map[tlv.Type][]byte, error) { var ( cid uint64 amt uint64 @@ -175,23 +171,6 @@ func NewPayloadFromReader(r io.Reader, finalHop bool) (*Payload, return nil, nil, err } - // Validate whether the sender properly included or omitted tlv records - // in accordance with BOLT 04. - err = ValidateParsedPayloadTypes(parsedTypes, finalHop) - if err != nil { - return nil, nil, err - } - - // Check for violation of the rules for mandatory fields. - violatingType := getMinRequiredViolation(parsedTypes) - if violatingType != nil { - return nil, nil, ErrInvalidPayload{ - Type: *violatingType, - Violation: RequiredViolation, - FinalHop: finalHop, - } - } - // If no MPP field was parsed, set the MPP field on the resulting // payload to nil. if _, ok := parsedTypes[record.MPPOnionType]; !ok { @@ -232,7 +211,34 @@ func NewPayloadFromReader(r io.Reader, finalHop bool) (*Payload, blindingPoint: blindingPoint, customRecords: customRecords, totalAmtMsat: lnwire.MilliSatoshi(totalAmtMsat), - }, nil, nil + }, parsedTypes, nil +} + +// ValidateTLVPayload validates the TLV fields that were included in a TLV +// payload. +func ValidateTLVPayload(parsedTypes map[tlv.Type][]byte, + finalHop bool, updateAddBlinding bool) error { + + // Validate whether the sender properly included or omitted tlv records + // in accordance with BOLT 04. + err := ValidateParsedPayloadTypes( + parsedTypes, finalHop, updateAddBlinding, + ) + if err != nil { + return err + } + + // Check for violation of the rules for mandatory fields. + violatingType := getMinRequiredViolation(parsedTypes) + if violatingType != nil { + return ErrInvalidPayload{ + Type: *violatingType, + Violation: RequiredViolation, + FinalHop: finalHop, + } + } + + return nil } // ForwardingInfo returns the basic parameters required for HTLC forwarding, @@ -259,7 +265,7 @@ func NewCustomRecords(parsedTypes tlv.TypeMap) record.CustomSet { // boolean should be true if the payload was parsed for an exit hop. The // requirements for this method are described in BOLT 04. func ValidateParsedPayloadTypes(parsedTypes tlv.TypeMap, - isFinalHop bool) error { + isFinalHop, updateAddBlinding bool) error { _, hasAmt := parsedTypes[record.AmtOnionType] _, hasLockTime := parsedTypes[record.LockTimeOnionType] @@ -267,6 +273,7 @@ func ValidateParsedPayloadTypes(parsedTypes tlv.TypeMap, _, hasMPP := parsedTypes[record.MPPOnionType] _, hasAMP := parsedTypes[record.AMPOnionType] _, hasEncryptedData := parsedTypes[record.EncryptedDataOnionType] + _, hasBlinding := parsedTypes[record.BlindingPointOnionType] // All cleartext hops (including final hop) and the final hop in a // blinded path require the forwading amount and expiry TLVs to be set. @@ -277,6 +284,32 @@ func ValidateParsedPayloadTypes(parsedTypes tlv.TypeMap, needNextHop := !(hasEncryptedData || isFinalHop) switch { + // Both blinding point being set is invalid. + case hasBlinding && updateAddBlinding: + return ErrInvalidPayload{ + Type: record.BlindingPointOnionType, + Violation: IncludedViolation, + FinalHop: isFinalHop, + } + + // If encrypted data is not provided, blinding points should not be + // set. + case !hasEncryptedData && (hasBlinding || updateAddBlinding): + return ErrInvalidPayload{ + Type: record.EncryptedDataOnionType, + Violation: OmittedViolation, + FinalHop: isFinalHop, + } + + // If encrypted data is present, we require that one blinding point + // is set. + case hasEncryptedData && !(hasBlinding || updateAddBlinding): + return ErrInvalidPayload{ + Type: record.EncryptedDataOnionType, + Violation: IncludedViolation, + FinalHop: isFinalHop, + } + // Hops that need forwarding info must include an amount to forward. case needFwdInfo && !hasAmt: return ErrInvalidPayload{ diff --git a/htlcswitch/hop/payload_test.go b/htlcswitch/hop/payload_test.go index 301e57716..c22144d7b 100644 --- a/htlcswitch/hop/payload_test.go +++ b/htlcswitch/hop/payload_test.go @@ -26,6 +26,7 @@ type decodePayloadTest struct { name string payload []byte isFinalHop bool + updateAddBlinded bool expErr error expCustomRecords map[uint64][]byte shouldHaveMPP bool @@ -271,8 +272,9 @@ var decodePayloadTests = []decodePayloadTest{ }, }, { - name: "intermediate hop with encrypted data", - isFinalHop: false, + name: "intermediate hop with encrypted data", + isFinalHop: false, + updateAddBlinded: true, payload: []byte{ // encrypted data 0x0a, 0x03, 0x03, 0x02, 0x01, @@ -365,8 +367,9 @@ var decodePayloadTests = []decodePayloadTest{ shouldHaveTotalAmt: true, }, { - name: "final blinded hop with total amount", - isFinalHop: true, + name: "final blinded hop with total amount", + isFinalHop: true, + updateAddBlinded: true, payload: []byte{ // amount 0x02, 0x00, @@ -378,8 +381,9 @@ var decodePayloadTests = []decodePayloadTest{ shouldHaveEncData: true, }, { - name: "final blinded missing amt", - isFinalHop: true, + name: "final blinded missing amt", + isFinalHop: true, + updateAddBlinded: true, payload: []byte{ // cltv 0x04, 0x00, @@ -394,8 +398,9 @@ var decodePayloadTests = []decodePayloadTest{ }, }, { - name: "final blinded missing cltv", - isFinalHop: true, + name: "final blinded missing cltv", + isFinalHop: true, + updateAddBlinded: true, payload: []byte{ // amount 0x02, 0x00, @@ -410,8 +415,9 @@ var decodePayloadTests = []decodePayloadTest{ }, }, { - name: "intermediate blinded has amount", - isFinalHop: false, + name: "intermediate blinded has amount", + isFinalHop: false, + updateAddBlinded: true, payload: []byte{ // amount 0x02, 0x00, @@ -425,8 +431,9 @@ var decodePayloadTests = []decodePayloadTest{ }, }, { - name: "intermediate blinded has expiry", - isFinalHop: false, + name: "intermediate blinded has expiry", + isFinalHop: false, + updateAddBlinded: true, payload: []byte{ // cltv 0x04, 0x00, @@ -439,6 +446,67 @@ var decodePayloadTests = []decodePayloadTest{ FinalHop: false, }, }, + { + name: "update add blinding no data", + isFinalHop: false, + payload: []byte{ + // cltv + 0x04, 0x00, + }, + updateAddBlinded: true, + expErr: hop.ErrInvalidPayload{ + Type: record.EncryptedDataOnionType, + Violation: hop.OmittedViolation, + FinalHop: false, + }, + }, + { + name: "onion blinding point no data", + isFinalHop: false, + payload: append([]byte{ + // blinding point (type / length) + 0x0c, 0x21, + }, + // blinding point (value) + testPubKey.SerializeCompressed()..., + ), + expErr: hop.ErrInvalidPayload{ + Type: record.EncryptedDataOnionType, + Violation: hop.OmittedViolation, + FinalHop: false, + }, + }, + { + name: "encrypted data no blinding", + isFinalHop: false, + payload: []byte{ + // encrypted data + 0x0a, 0x03, 0x03, 0x02, 0x01, + }, + expErr: hop.ErrInvalidPayload{ + Type: record.EncryptedDataOnionType, + Violation: hop.IncludedViolation, + }, + }, + { + name: "both blinding points", + isFinalHop: false, + updateAddBlinded: true, + payload: append([]byte{ + // encrypted data + 0x0a, 0x03, 0x03, 0x02, 0x01, + // blinding point (type / length) + 0x0c, 0x21, + }, + // blinding point (value) + testPubKey.SerializeCompressed()..., + ), + expErr: hop.ErrInvalidPayload{ + Type: record.BlindingPointOnionType, + Violation: hop.IncludedViolation, + FinalHop: false, + }, + }, } // TestDecodeHopPayloadRecordValidation asserts that parsing the payloads in the @@ -479,8 +547,13 @@ func testDecodeHopPayloadValidation(t *testing.T, test decodePayloadTest) { testChildIndex = uint32(9) ) - p, _, err := hop.NewPayloadFromReader( - bytes.NewReader(test.payload), test.isFinalHop, + p, parsedTypes, err := hop.ParseTLVPayload( + bytes.NewReader(test.payload), + ) + require.NoError(t, err) + + err = hop.ValidateTLVPayload( + parsedTypes, test.isFinalHop, test.updateAddBlinded, ) if !reflect.DeepEqual(test.expErr, err) { t.Fatalf("expected error mismatch, want: %v, got: %v", diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 889c21e87..468eba131 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -204,15 +204,15 @@ type ChannelLinkConfig struct { // receiving node is persistent. UnsafeReplay bool - // MinFeeUpdateTimeout represents the minimum interval in which a link + // MinUpdateTimeout represents the minimum interval in which a link // will propose to update its commitment fee rate. A random timeout will - // be selected between this and MaxFeeUpdateTimeout. - MinFeeUpdateTimeout time.Duration + // be selected between this and MaxUpdateTimeout. + MinUpdateTimeout time.Duration - // MaxFeeUpdateTimeout represents the maximum interval in which a link + // MaxUpdateTimeout represents the maximum interval in which a link // will propose to update its commitment fee rate. A random timeout will - // be selected between this and MinFeeUpdateTimeout. - MaxFeeUpdateTimeout time.Duration + // be selected between this and MinUpdateTimeout. + MaxUpdateTimeout time.Duration // OutgoingCltvRejectDelta defines the number of blocks before expiry of // an htlc where we don't offer an htlc anymore. This should be at least @@ -1560,8 +1560,8 @@ func getResolutionFailure(resolution *invoices.HtlcFailResolution, // within the link's configuration that will be used to determine when the link // should propose an update to its commitment fee rate. func (l *channelLink) randomFeeUpdateTimeout() time.Duration { - lower := int64(l.cfg.MinFeeUpdateTimeout) - upper := int64(l.cfg.MaxFeeUpdateTimeout) + lower := int64(l.cfg.MinUpdateTimeout) + upper := int64(l.cfg.MaxUpdateTimeout) return time.Duration(prand.Int63n(upper-lower) + lower) } @@ -1795,8 +1795,20 @@ func (l *channelLink) handleDownstreamPkt(pkt *htlcPacket) { htlc.ID = pkt.incomingHTLCID // We send the HTLC message to the peer which initially created - // the HTLC. - l.cfg.Peer.SendMessage(false, htlc) + // the HTLC. If the incoming blinding point is non-nil, we + // know that we are a relaying node in a blinded path. + // Otherwise, we're either an introduction node or not part of + // a blinded path at all. + if err := l.sendIncomingHTLCFailureMsg( + htlc.ID, + pkt.obfuscator, + htlc.Reason, + ); err != nil { + l.log.Errorf("unable to send HTLC failure: %v", + err) + + return + } // If the packet does not have a link failure set, it failed // further down the route so we notify a forwarding failure. @@ -2032,6 +2044,19 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { failure = &lnwire.FailInvalidOnionKey{ OnionSHA256: msg.ShaOnionBlob, } + + // Handle malformed errors that are part of a blinded route. + // This case is slightly different, because we expect every + // relaying node in the blinded portion of the route to send + // malformed errors. If we're also a relaying node, we're + // likely going to switch this error out anyway for our own + // malformed error, but we handle the case here for + // completeness. + case lnwire.CodeInvalidBlinding: + failure = &lnwire.FailInvalidBlinding{ + OnionSHA256: msg.ShaOnionBlob, + } + default: l.log.Warnf("unexpected failure code received in "+ "UpdateFailMailformedHTLC: %v", msg.FailureCode) @@ -3253,7 +3278,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // DecodeHopIterator function which process the Sphinx packet. chanIterator, failureCode := decodeResps[i].Result() if failureCode != lnwire.CodeNone { - // If we're unable to process the onion blob than we + // If we're unable to process the onion blob then we // should send the malformed htlc error to payment // sender. l.sendMalformedHTLCError(pd.HtlcIndex, failureCode, @@ -3264,37 +3289,50 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, continue } - // Retrieve onion obfuscator from onion blob in order to - // produce initial obfuscation of the onion failureCode. - obfuscator, failureCode := chanIterator.ExtractErrorEncrypter( - l.cfg.ExtractErrorEncrypter, - ) - if failureCode != lnwire.CodeNone { - // If we're unable to process the onion blob than we - // should send the malformed htlc error to payment - // sender. - l.sendMalformedHTLCError( - pd.HtlcIndex, failureCode, onionBlob[:], pd.SourceRef, - ) - - l.log.Errorf("unable to decode onion "+ - "obfuscator: %v", failureCode) - continue - } - heightNow := l.cfg.BestHeight() - pld, err := chanIterator.HopPayload() - if err != nil { + pld, routeRole, pldErr := chanIterator.HopPayload() + if pldErr != nil { // If we're unable to process the onion payload, or we // received invalid onion payload failure, then we // should send an error back to the caller so the HTLC // can be canceled. var failedType uint64 - if e, ok := err.(hop.ErrInvalidPayload); ok { + + // We need to get the underlying error value, so we + // can't use errors.As as suggested by the linter. + //nolint:errorlint + if e, ok := pldErr.(hop.ErrInvalidPayload); ok { failedType = uint64(e.Type) } + // If we couldn't parse the payload, make our best + // effort at creating an error encrypter that knows + // what blinding type we were, but if we couldn't + // parse the payload we have no way of knowing whether + // we were the introduction node or not. + // + //nolint:lll + obfuscator, failCode := chanIterator.ExtractErrorEncrypter( + l.cfg.ExtractErrorEncrypter, + // We need our route role here because we + // couldn't parse or validate the payload. + routeRole == hop.RouteRoleIntroduction, + ) + if failCode != lnwire.CodeNone { + l.log.Errorf("could not extract error "+ + "encrypter: %v", pldErr) + + // We can't process this htlc, send back + // malformed. + l.sendMalformedHTLCError( + pd.HtlcIndex, failureCode, + onionBlob[:], pd.SourceRef, + ) + + continue + } + // TODO: currently none of the test unit infrastructure // is setup to handle TLV payloads, so testing this // would require implementing a separate mock iterator @@ -3307,7 +3345,29 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, ) l.log.Errorf("unable to decode forwarding "+ - "instructions: %v", err) + "instructions: %v", pldErr) + + continue + } + + // Retrieve onion obfuscator from onion blob in order to + // produce initial obfuscation of the onion failureCode. + obfuscator, failureCode := chanIterator.ExtractErrorEncrypter( + l.cfg.ExtractErrorEncrypter, + routeRole == hop.RouteRoleIntroduction, + ) + if failureCode != lnwire.CodeNone { + // If we're unable to process the onion blob than we + // should send the malformed htlc error to payment + // sender. + l.sendMalformedHTLCError( + pd.HtlcIndex, failureCode, onionBlob[:], + pd.SourceRef, + ) + + l.log.Errorf("unable to decode onion "+ + "obfuscator: %v", failureCode) + continue } @@ -3693,11 +3753,14 @@ func (l *channelLink) sendHTLCError(pd *lnwallet.PaymentDescriptor, return } - l.cfg.Peer.SendMessage(false, &lnwire.UpdateFailHTLC{ - ChanID: l.ChanID(), - ID: pd.HtlcIndex, - Reason: reason, - }) + // Send the appropriate failure message depending on whether we're + // in a blinded route or not. + if err := l.sendIncomingHTLCFailureMsg( + pd.HtlcIndex, e, reason, + ); err != nil { + l.log.Errorf("unable to send HTLC failure: %v", err) + return + } // Notify a link failure on our incoming link. Outgoing htlc information // is not available at this point, because we have not decrypted the @@ -3726,6 +3789,95 @@ func (l *channelLink) sendHTLCError(pd *lnwallet.PaymentDescriptor, ) } +// sendPeerHTLCFailure handles sending a HTLC failure message back to the +// peer from which the HTLC was received. This function is primarily used to +// handle the special requirements of route blinding, specifically: +// - Forwarding nodes must switch out any errors with MalformedFailHTLC +// - Introduction nodes should return regular HTLC failure messages. +// +// It accepts the original opaque failure, which will be used in the case +// that we're not part of a blinded route and an error encrypter that'll be +// used if we are the introduction node and need to present an error as if +// we're the failing party. +// +// Note: this function does not yet handle special error cases for receiving +// nodes in blinded paths, as LND does not support blinded receives. +func (l *channelLink) sendIncomingHTLCFailureMsg(htlcIndex uint64, + e hop.ErrorEncrypter, + originalFailure lnwire.OpaqueReason) error { + + var msg lnwire.Message + switch { + // Our circuit's error encrypter will be nil if this was a locally + // initiated payment. We can only hit a blinded error for a locally + // initiated payment if we allow ourselves to be picked as the + // introduction node for our own payments and in that case we + // shouldn't reach this code. To prevent the HTLC getting stuck, + // we fail it back and log an error. + // code. + case e == nil: + msg = &lnwire.UpdateFailHTLC{ + ChanID: l.ChanID(), + ID: htlcIndex, + Reason: originalFailure, + } + + l.log.Errorf("Unexpected blinded failure when "+ + "we are the sending node, incoming htlc: %v(%v)", + l.ShortChanID(), htlcIndex) + + // For cleartext hops (ie, non-blinded/normal) we don't need any + // transformation on the error message and can just send the original. + case !e.Type().IsBlinded(): + msg = &lnwire.UpdateFailHTLC{ + ChanID: l.ChanID(), + ID: htlcIndex, + Reason: originalFailure, + } + + // When we're the introduction node, we need to convert the error to + // a UpdateFailHTLC. + case e.Type() == hop.EncrypterTypeIntroduction: + l.log.Debugf("Introduction blinded node switching out failure "+ + "error: %v", htlcIndex) + + // The specification does not require that we set the onion + // blob. + failureMsg := lnwire.NewInvalidBlinding(nil) + reason, err := e.EncryptFirstHop(failureMsg) + if err != nil { + return err + } + + msg = &lnwire.UpdateFailHTLC{ + ChanID: l.ChanID(), + ID: htlcIndex, + Reason: reason, + } + + // If we are a relaying node, we need to switch out any error that + // we've received to a malformed HTLC error. + case e.Type() == hop.EncrypterTypeRelaying: + l.log.Debugf("Relaying blinded node switching out malformed "+ + "error: %v", htlcIndex) + + msg = &lnwire.UpdateFailMalformedHTLC{ + ChanID: l.ChanID(), + ID: htlcIndex, + FailureCode: lnwire.CodeInvalidBlinding, + } + + default: + return fmt.Errorf("unexpected encrypter: %d", e) + } + + if err := l.cfg.Peer.SendMessage(false, msg); err != nil { + l.log.Warnf("Send update fail failed: %v", err) + } + + return nil +} + // sendMalformedHTLCError helper function which sends the malformed HTLC update // to the payment sender. func (l *channelLink) sendMalformedHTLCError(htlcIndex uint64, diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 4db22d173..93c76bd40 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -2220,11 +2220,11 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt, BatchTicker: bticker, FwdPkgGCTicker: ticker.NewForce(15 * time.Second), PendingCommitTicker: ticker.New(time.Minute), - // Make the BatchSize and Min/MaxFeeUpdateTimeout large enough + // Make the BatchSize and Min/MaxUpdateTimeout large enough // to not trigger commit updates automatically during tests. BatchSize: 10000, - MinFeeUpdateTimeout: 30 * time.Minute, - MaxFeeUpdateTimeout: 40 * time.Minute, + MinUpdateTimeout: 30 * time.Minute, + MaxUpdateTimeout: 40 * time.Minute, MaxOutgoingCltvExpiry: DefaultMaxOutgoingCltvExpiry, MaxFeeAllocation: DefaultMaxLinkFeeAllocation, NotifyActiveLink: func(wire.OutPoint) {}, @@ -2480,7 +2480,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight := int64(1) * input.HTLCWeight + htlcWeight := lntypes.WeightUnit(1) * input.HTLCWeight feeBuffer := lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -2517,7 +2517,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight = int64(2) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(2) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -2578,7 +2578,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight = int64(1) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(1) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -2609,7 +2609,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight = int64(2) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(2) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -2675,7 +2675,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight = int64(1) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(1) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -2721,7 +2721,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight = int64(2) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(2) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -2771,7 +2771,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight = int64(1) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(1) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -2835,7 +2835,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight = int64(2) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(2) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -2880,7 +2880,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight = int64(1) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(1) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -2976,7 +2976,7 @@ func TestChannelLinkTrimCircuitsPending(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight := int64(1) * input.HTLCWeight + htlcWeight := lntypes.WeightUnit(1) * input.HTLCWeight feeBuffer := lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -3022,7 +3022,7 @@ func TestChannelLinkTrimCircuitsPending(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight = int64(1+halfHtlcs) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(1+halfHtlcs) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -3113,7 +3113,7 @@ func TestChannelLinkTrimCircuitsPending(t *testing.T) { // With two HTLCs on the pending commit, and two added to the in-memory // commitment state, the resulting bandwidth should reflect that Alice // is paying the all htlc amounts in addition to all htlc fees. - htlcWeight = int64(1+numHtlcs) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(1+numHtlcs) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -3205,7 +3205,7 @@ func TestChannelLinkTrimCircuitsPending(t *testing.T) { // Since the latter two HTLCs have been completely dropped from memory, // only the first two HTLCs we added should still be reflected in the // channel bandwidth. - htlcWeight = int64(1+halfHtlcs) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(1+halfHtlcs) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -3264,7 +3264,7 @@ func TestChannelLinkTrimCircuitsNoCommit(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight := int64(1) * input.HTLCWeight + htlcWeight := lntypes.WeightUnit(1) * input.HTLCWeight feeBuffer := lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -3309,7 +3309,7 @@ func TestChannelLinkTrimCircuitsNoCommit(t *testing.T) { // We account for the 2 htlcs and the additional one which would be // needed when sending and htlc. - htlcWeight = int64(1+halfHtlcs) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(1+halfHtlcs) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -3387,7 +3387,7 @@ func TestChannelLinkTrimCircuitsNoCommit(t *testing.T) { } // Alice's bandwidth should have reverted back to her starting value. - htlcWeight = int64(1) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(1) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -3414,7 +3414,7 @@ func TestChannelLinkTrimCircuitsNoCommit(t *testing.T) { // We account for the 2 htlcs and the additional one which would be // needed when sending and htlc. - htlcWeight = int64(1+halfHtlcs) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(1+halfHtlcs) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -3492,7 +3492,7 @@ func TestChannelLinkTrimCircuitsNoCommit(t *testing.T) { t.Fatalf("expected %d packet to be failed", halfHtlcs) } - htlcWeight = int64(1) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(1) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -3540,7 +3540,7 @@ func TestChannelLinkTrimCircuitsRemoteCommit(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight := int64(1) * input.HTLCWeight + htlcWeight := lntypes.WeightUnit(1) * input.HTLCWeight feeBuffer := lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -3602,7 +3602,7 @@ func TestChannelLinkTrimCircuitsRemoteCommit(t *testing.T) { // The resulting bandwidth should reflect that Alice is paying both // htlc amounts, in addition to both htlc fees. - htlcWeight = int64(1+numHtlcs) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(1+numHtlcs) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -3700,7 +3700,7 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) { // Calculate the fee buffer for a channel state. Account for htlcs on // the potential channel state as well. - htlcWeight := int64(1) * input.HTLCWeight + htlcWeight := lntypes.WeightUnit(1) * input.HTLCWeight feeBuffer := lnwallet.CalcFeeBuffer(feePerKw, commitWeight+htlcWeight) // The starting bandwidth of the channel should be exactly the amount @@ -3727,7 +3727,7 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) { _ = harness.aliceLink.handleSwitchPacket(addPkt) time.Sleep(time.Millisecond * 100) - htlcWeight = int64(2) * input.HTLCWeight + htlcWeight = lntypes.WeightUnit(2) * input.HTLCWeight feeBuffer = lnwallet.CalcFeeBuffer( feePerKw, commitWeight+htlcWeight, ) @@ -4881,11 +4881,11 @@ func (h *persistentLinkHarness) restartLink( BatchTicker: bticker, FwdPkgGCTicker: ticker.New(5 * time.Second), PendingCommitTicker: ticker.New(time.Minute), - // Make the BatchSize and Min/MaxFeeUpdateTimeout large enough + // Make the BatchSize and Min/MaxUpdateTimeout large enough // to not trigger commit updates automatically during tests. - BatchSize: 10000, - MinFeeUpdateTimeout: 30 * time.Minute, - MaxFeeUpdateTimeout: 40 * time.Minute, + BatchSize: 10000, + MinUpdateTimeout: 30 * time.Minute, + MaxUpdateTimeout: 40 * time.Minute, // Set any hodl flags requested for the new link. HodlMask: hodl.MaskFromFlags(hodlFlags...), MaxOutgoingCltvExpiry: DefaultMaxOutgoingCltvExpiry, diff --git a/htlcswitch/mailbox.go b/htlcswitch/mailbox.go index 99066497c..a729e3ba5 100644 --- a/htlcswitch/mailbox.go +++ b/htlcswitch/mailbox.go @@ -738,6 +738,7 @@ func (m *memoryMailBox) FailAdd(pkt *htlcPacket) { sourceRef: pkt.sourceRef, hasSource: true, localFailure: localFailure, + obfuscator: pkt.obfuscator, linkFailure: linkError, htlc: &lnwire.UpdateFailHTLC{ Reason: reason, diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 94615113e..c271eb2e6 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -7,7 +7,6 @@ import ( "encoding/binary" "fmt" "io" - "io/ioutil" "net" "os" "path/filepath" @@ -333,10 +332,10 @@ func newMockHopIterator(hops ...*hop.Payload) hop.Iterator { return &mockHopIterator{hops: hops} } -func (r *mockHopIterator) HopPayload() (*hop.Payload, error) { +func (r *mockHopIterator) HopPayload() (*hop.Payload, hop.RouteRole, error) { h := r.hops[0] r.hops = r.hops[1:] - return h, nil + return h, hop.RouteRoleCleartext, nil } func (r *mockHopIterator) ExtraOnionBlob() []byte { @@ -344,7 +343,7 @@ func (r *mockHopIterator) ExtraOnionBlob() []byte { } func (r *mockHopIterator) ExtractErrorEncrypter( - extracter hop.ErrorEncrypterExtracter) (hop.ErrorEncrypter, + extracter hop.ErrorEncrypterExtracter, _ bool) (hop.ErrorEncrypter, lnwire.FailCode) { return extracter(nil) @@ -961,7 +960,7 @@ var _ ChannelLink = (*mockChannelLink)(nil) func newDB() (*channeldb.DB, func(), error) { // First, create a temporary directory to be used for the duration of // this test. - tempDirName, err := ioutil.TempDir("", "channeldb") + tempDirName, err := os.MkdirTemp("", "channeldb") if err != nil { return nil, nil, err } diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index 5babe5ca5..2c0262581 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -1297,6 +1297,11 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error { fail, isFail := htlc.(*lnwire.UpdateFailHTLC) if isFail && !packet.hasSource { + // HTLC resolutions and messages restored from disk + // don't have the obfuscator set from the original htlc + // add packet - set it here for use in blinded errors. + packet.obfuscator = circuit.ErrorEncrypter + switch { // No message to encrypt, locally sourced payment. case circuit.ErrorEncrypter == nil: @@ -1485,6 +1490,7 @@ func (s *Switch) failAddPacket(packet *htlcPacket, failure *LinkError) error { incomingTimeout: packet.incomingTimeout, outgoingTimeout: packet.outgoingTimeout, circuit: packet.circuit, + obfuscator: packet.obfuscator, linkFailure: failure, htlc: &lnwire.UpdateFailHTLC{ Reason: reason, @@ -1841,6 +1847,10 @@ out: // resolution message on restart. resolutionMsg.errChan <- nil + // Create a htlc packet for this resolution. We do + // not have some of the information that we'll need + // for blinded error handling here , so we'll rely on + // our forwarding logic to fill it in later. pkt := &htlcPacket{ outgoingChanID: resolutionMsg.SourceChan, outgoingHTLCID: resolutionMsg.HtlcIndex, @@ -2065,6 +2075,8 @@ func (s *Switch) reforwardResolutions() error { // The circuit is still open, so we can assume that the link or // switch (if we are the source) hasn't cleaned it up yet. + // We rely on our forwarding logic to fill in details that + // are not currently available to us. resPkt := &htlcPacket{ outgoingChanID: resMsg.SourceChan, outgoingHTLCID: resMsg.HtlcIndex, @@ -2214,7 +2226,8 @@ func (s *Switch) reforwardSettleFails(fwdPkgs []*channeldb.FwdPkg) { // we can continue to propagate it. This // failure originated from another node, so // the linkFailure field is not set on this - // packet. + // packet. We rely on the link to fill in + // additional circuit information for us. failPacket := &htlcPacket{ outgoingChanID: fwdPkg.Source, outgoingHTLCID: pd.ParentIndex, diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 0c98fa4fe..9a72197ec 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -1155,8 +1155,8 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer, BatchTicker: ticker.NewForce(testBatchTimeout), FwdPkgGCTicker: ticker.NewForce(fwdPkgTimeout), PendingCommitTicker: ticker.New(2 * time.Minute), - MinFeeUpdateTimeout: minFeeUpdateTimeout, - MaxFeeUpdateTimeout: maxFeeUpdateTimeout, + MinUpdateTimeout: minFeeUpdateTimeout, + MaxUpdateTimeout: maxFeeUpdateTimeout, OnChannelFailure: func(lnwire.ChannelID, lnwire.ShortChannelID, LinkFailureError) {}, OutgoingCltvRejectDelta: 3, MaxOutgoingCltvExpiry: DefaultMaxOutgoingCltvExpiry, diff --git a/input/input.go b/input/input.go index 516ebbdbe..aef524a4c 100644 --- a/input/input.go +++ b/input/input.go @@ -71,7 +71,7 @@ type TxInfo struct { Fee btcutil.Amount // Weight is the weight of the tx. - Weight int64 + Weight lntypes.WeightUnit } // String returns a human readable version of the tx info. @@ -332,7 +332,7 @@ func (h *HtlcSucceedInput) CraftInputScript(signer Signer, txn *wire.MsgTx, }, nil } -// HtlcsSecondLevelAnchorInput is an input type used to spend HTLC outputs +// HtlcSecondLevelAnchorInput is an input type used to spend HTLC outputs // using a re-signed second level transaction, either via the timeout or success // paths. type HtlcSecondLevelAnchorInput struct { diff --git a/input/mocks.go b/input/mocks.go index 915e4ea69..2f38400d8 100644 --- a/input/mocks.go +++ b/input/mocks.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" "github.com/stretchr/testify/mock" ) @@ -157,10 +158,10 @@ func (m *MockWitnessType) WitnessGenerator(signer Signer, // if it would be included in a tx. It also returns if the output itself is a // nested p2sh output, if so then we need to take into account the extra // sigScript data size. -func (m *MockWitnessType) SizeUpperBound() (int, bool, error) { +func (m *MockWitnessType) SizeUpperBound() (lntypes.WeightUnit, bool, error) { args := m.Called() - return args.Int(0), args.Bool(1), args.Error(2) + return args.Get(0).(lntypes.WeightUnit), args.Bool(1), args.Error(2) } // AddWeightEstimation adds the estimated size of the witness in bytes to the diff --git a/input/size.go b/input/size.go index e71e4190b..5d4b15ec1 100644 --- a/input/size.go +++ b/input/size.go @@ -5,6 +5,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/waddrmgr" + "github.com/lightningnetwork/lnd/lntypes" ) const ( @@ -862,9 +863,9 @@ type TxWeightEstimator struct { hasWitness bool inputCount uint32 outputCount uint32 - inputSize int - inputWitnessSize int - outputSize int + inputSize lntypes.VByte + inputWitnessSize lntypes.WeightUnit + outputSize lntypes.VByte } // AddP2PKHInput updates the weight estimate to account for an additional input @@ -888,7 +889,9 @@ func (twe *TxWeightEstimator) AddP2WKHInput() *TxWeightEstimator { // AddWitnessInput updates the weight estimate to account for an additional // input spending a native pay-to-witness output. This accepts the total size // of the witness as a parameter. -func (twe *TxWeightEstimator) AddWitnessInput(witnessSize int) *TxWeightEstimator { +func (twe *TxWeightEstimator) AddWitnessInput( + witnessSize lntypes.WeightUnit) *TxWeightEstimator { + twe.inputSize += InputSize twe.inputWitnessSize += witnessSize twe.inputCount++ @@ -905,7 +908,8 @@ func (twe *TxWeightEstimator) AddWitnessInput(witnessSize int) *TxWeightEstimato // NOTE: The leaf witness size must be calculated without the byte that accounts // for the number of witness elements, only the total size of all elements on // the stack that are consumed by the revealed script should be counted. -func (twe *TxWeightEstimator) AddTapscriptInput(leafWitnessSize int, +func (twe *TxWeightEstimator) AddTapscriptInput( + leafWitnessSize lntypes.WeightUnit, tapscript *waddrmgr.Tapscript) *TxWeightEstimator { // We add 1 byte for the total number of witness elements. @@ -915,7 +919,9 @@ func (twe *TxWeightEstimator) AddTapscriptInput(leafWitnessSize int, 1 + len(tapscript.ControlBlock.InclusionProof) twe.inputSize += InputSize - twe.inputWitnessSize += leafWitnessSize + controlBlockWitnessSize + twe.inputWitnessSize += leafWitnessSize + lntypes.WeightUnit( + controlBlockWitnessSize, + ) twe.inputCount++ twe.hasWitness = true @@ -956,7 +962,9 @@ func (twe *TxWeightEstimator) AddNestedP2WKHInput() *TxWeightEstimator { // AddNestedP2WSHInput updates the weight estimate to account for an additional // input spending a P2SH output with a nested P2WSH redeem script. -func (twe *TxWeightEstimator) AddNestedP2WSHInput(witnessSize int) *TxWeightEstimator { +func (twe *TxWeightEstimator) AddNestedP2WSHInput( + witnessSize lntypes.WeightUnit) *TxWeightEstimator { + twe.inputSize += InputSize + NestedP2WSHSize twe.inputWitnessSize += witnessSize twe.inputCount++ @@ -967,7 +975,7 @@ func (twe *TxWeightEstimator) AddNestedP2WSHInput(witnessSize int) *TxWeightEsti // AddTxOutput adds a known TxOut to the weight estimator. func (twe *TxWeightEstimator) AddTxOutput(txOut *wire.TxOut) *TxWeightEstimator { - twe.outputSize += txOut.SerializeSize() + twe.outputSize += lntypes.VByte(txOut.SerializeSize()) twe.outputCount++ return twe @@ -1020,18 +1028,20 @@ func (twe *TxWeightEstimator) AddP2SHOutput() *TxWeightEstimator { // AddOutput estimates the weight of an output based on the pkScript. func (twe *TxWeightEstimator) AddOutput(pkScript []byte) *TxWeightEstimator { - twe.outputSize += BaseOutputSize + len(pkScript) + twe.outputSize += BaseOutputSize + lntypes.VByte(len(pkScript)) twe.outputCount++ return twe } // Weight gets the estimated weight of the transaction. -func (twe *TxWeightEstimator) Weight() int { - txSizeStripped := BaseTxSize + - wire.VarIntSerializeSize(uint64(twe.inputCount)) + twe.inputSize + - wire.VarIntSerializeSize(uint64(twe.outputCount)) + twe.outputSize - weight := txSizeStripped * witnessScaleFactor +func (twe *TxWeightEstimator) Weight() lntypes.WeightUnit { + inputCount := wire.VarIntSerializeSize(uint64(twe.inputCount)) + outputCount := wire.VarIntSerializeSize(uint64(twe.outputCount)) + txSizeStripped := BaseTxSize + lntypes.VByte(inputCount) + + twe.inputSize + lntypes.VByte(outputCount) + twe.outputSize + weight := lntypes.WeightUnit(txSizeStripped * witnessScaleFactor) + if twe.hasWitness { weight += WitnessHeaderSize + twe.inputWitnessSize } @@ -1041,5 +1051,5 @@ func (twe *TxWeightEstimator) Weight() int { // VSize gets the estimated virtual size of the transactions, in vbytes. func (twe *TxWeightEstimator) VSize() int { // A tx's vsize is 1/4 of the weight, rounded up. - return (twe.Weight() + witnessScaleFactor - 1) / witnessScaleFactor + return int(twe.Weight().ToVB()) } diff --git a/input/size_test.go b/input/size_test.go index 4cc1680ba..53b1151dc 100644 --- a/input/size_test.go +++ b/input/size_test.go @@ -297,11 +297,12 @@ func TestTxWeightEstimator(t *testing.T) { tx.AddTxOut(&wire.TxOut{PkScript: p2shScript}) } - expectedWeight := blockchain.GetTransactionWeight(btcutil.NewTx(tx)) - if weightEstimate.Weight() != int(expectedWeight) { - t.Errorf("Case %d: Got wrong weight: expected %d, got %d", - i, expectedWeight, weightEstimate.Weight()) - } + expectedWeight := blockchain.GetTransactionWeight( + btcutil.NewTx(tx), + ) + require.EqualValuesf(t, expectedWeight, weightEstimate.Weight(), + "Case %d: Got wrong weight: expected %d, got %d", + i, expectedWeight, weightEstimate.Weight()) } } diff --git a/input/witnessgen.go b/input/witnessgen.go index 66b5ef90e..c49328afc 100644 --- a/input/witnessgen.go +++ b/input/witnessgen.go @@ -5,6 +5,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/lntypes" ) // WitnessGenerator represents a function that is able to generate the final @@ -32,7 +33,7 @@ type WitnessType interface { // WitnessType if it would be included in a tx. It also returns if the // output itself is a nested p2sh output, if so then we need to take // into account the extra sigScript data size. - SizeUpperBound() (int, bool, error) + SizeUpperBound() (lntypes.WeightUnit, bool, error) // AddWeightEstimation adds the estimated size of the witness in bytes // to the given weight estimator. @@ -726,7 +727,9 @@ func (wt StandardWitnessType) WitnessGenerator(signer Signer, // sigScript data size. // // NOTE: This is part of the WitnessType interface. -func (wt StandardWitnessType) SizeUpperBound() (int, bool, error) { +func (wt StandardWitnessType) SizeUpperBound() (lntypes.WeightUnit, + bool, error) { + switch wt { // Outputs on a remote commitment transaction that pay directly to us. case CommitSpendNoDelayTweakless: @@ -743,7 +746,8 @@ func (wt StandardWitnessType) SizeUpperBound() (int, bool, error) { case LeaseCommitmentTimeLock: size := ToLocalTimeoutWitnessSize + LeaseWitnessScriptSizeOverhead - return size, false, nil + + return lntypes.WeightUnit(size), false, nil // 1 CSV time locked output to us on remote commitment. case CommitmentToRemoteConfirmed: @@ -751,7 +755,8 @@ func (wt StandardWitnessType) SizeUpperBound() (int, bool, error) { case LeaseCommitmentToRemoteConfirmed: size := ToRemoteConfirmedWitnessSize + LeaseWitnessScriptSizeOverhead - return size, false, nil + + return lntypes.WeightUnit(size), false, nil // Anchor output on the commitment transaction. case CommitmentAnchor: @@ -765,7 +770,8 @@ func (wt StandardWitnessType) SizeUpperBound() (int, bool, error) { case LeaseHtlcOfferedTimeoutSecondLevel: size := ToLocalTimeoutWitnessSize + LeaseWitnessScriptSizeOverhead - return size, false, nil + + return lntypes.WeightUnit(size), false, nil // Input to the outgoing HTLC second layer timeout transaction. case HtlcOfferedTimeoutSecondLevelInputConfirmed: @@ -779,7 +785,8 @@ func (wt StandardWitnessType) SizeUpperBound() (int, bool, error) { case LeaseHtlcAcceptedSuccessSecondLevel: size := ToLocalTimeoutWitnessSize + LeaseWitnessScriptSizeOverhead - return size, false, nil + + return lntypes.WeightUnit(size), false, nil // Input to the incoming second-layer HTLC success transaction. case HtlcAcceptedSuccessSecondLevelInputConfirmed: diff --git a/internal/musig2v040/musig2_test.go b/internal/musig2v040/musig2_test.go index 9ba74f417..47fd71c76 100644 --- a/internal/musig2v040/musig2_test.go +++ b/internal/musig2v040/musig2_test.go @@ -10,7 +10,7 @@ import ( "errors" "flag" "fmt" - "io/ioutil" + "os" "sync" "testing" @@ -256,7 +256,7 @@ func TestMuSig2KeyAggTestVectors(t *testing.T) { var formattedJson bytes.Buffer json.Indent(&formattedJson, jsonBytes, "", "\t") - err = ioutil.WriteFile( + err = os.WriteFile( keyAggTestVectorName, formattedJson.Bytes(), 0644, ) if err != nil { @@ -656,7 +656,7 @@ func TestMuSig2SigningTestVectors(t *testing.T) { var formattedJson bytes.Buffer json.Indent(&formattedJson, jsonBytes, "", "\t") - err = ioutil.WriteFile( + err = os.WriteFile( signTestVectorName, formattedJson.Bytes(), 0644, ) if err != nil { @@ -1540,7 +1540,7 @@ func TestMusig2AggregateNoncesTestVectors(t *testing.T) { var formattedJson bytes.Buffer json.Indent(&formattedJson, jsonBytes, "", "\t") - err = ioutil.WriteFile( + err = os.WriteFile( nonceAggTestVectorName, formattedJson.Bytes(), 0644, ) if err != nil { diff --git a/invoices/invoiceregistry_test.go b/invoices/invoiceregistry_test.go index 16b9bd9b1..69153b316 100644 --- a/invoices/invoiceregistry_test.go +++ b/invoices/invoiceregistry_test.go @@ -6,6 +6,7 @@ import ( "database/sql" "fmt" "math" + "sync" "testing" "testing/quick" "time" @@ -23,6 +24,12 @@ import ( "github.com/stretchr/testify/require" ) +// sqliteConstructorMu is used to ensure that only one thread can call the +// sqldb.NewTestSqliteDB constructor at a time. This is a temporary workaround +// that can be removed once this race condition in the sqlite repo is resolved: +// https://gitlab.com/cznic/sqlite/-/issues/180 +var sqliteConstructorMu sync.Mutex + var ( // htlcModifierMock is a mock implementation of the invoice HtlcModifier // interface. @@ -136,7 +143,9 @@ func TestInvoiceRegistry(t *testing.T) { var db *sqldb.BaseDB if sqlite { + sqliteConstructorMu.Lock() db = sqldb.NewTestSqliteDB(t).BaseDB + sqliteConstructorMu.Unlock() } else { db = sqldb.NewTestPostgresDB(t, pgFixture).BaseDB } diff --git a/invoices/invoices_test.go b/invoices/invoices_test.go index 6f3cbd8f2..71da38571 100644 --- a/invoices/invoices_test.go +++ b/invoices/invoices_test.go @@ -234,7 +234,9 @@ func TestInvoices(t *testing.T) { makeSQLDB := func(t *testing.T, sqlite bool) invpkg.InvoiceDB { var db *sqldb.BaseDB if sqlite { + sqliteConstructorMu.Lock() db = sqldb.NewTestSqliteDB(t).BaseDB + sqliteConstructorMu.Unlock() } else { db = sqldb.NewTestPostgresDB(t, pgFixture).BaseDB } @@ -2681,8 +2683,11 @@ func testDeleteCanceledInvoices(t *testing.T, }, nil } - // Add some invoices to the test db. + // Test deletion of canceled invoices when there are none. ctxb := context.Background() + require.NoError(t, db.DeleteCanceledInvoices(ctxb)) + + // Add some invoices to the test db. var invoices []invpkg.Invoice for i := 0; i < 10; i++ { invoice, err := randInvoice(lnwire.MilliSatoshi(i + 1)) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 6c530b99f..d67736e54 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -117,6 +117,14 @@ var allTestCases = []*lntest.TestCase{ Name: "batch channel funding", TestFunc: testBatchChanFunding, }, + { + Name: "open channel with unstable utxos", + TestFunc: testChannelFundingWithUnstableUtxos, + }, + { + Name: "open psbt channel with unstable utxos", + TestFunc: testPsbtChanFundingWithUnstableUtxos, + }, { Name: "update channel policy", TestFunc: testUpdateChannelPolicy, @@ -574,6 +582,26 @@ var allTestCases = []*lntest.TestCase{ Name: "forward blinded", TestFunc: testForwardBlindedRoute, }, + { + Name: "receiver blinded error", + TestFunc: testReceiverBlindedError, + }, + { + Name: "relayer blinded error", + TestFunc: testRelayingBlindedError, + }, + { + Name: "introduction blinded error", + TestFunc: testIntroductionNodeError, + }, + { + Name: "disable introduction node", + TestFunc: testDisableIntroductionNode, + }, + { + Name: "on chain to blinded", + TestFunc: testErrorHandlingOnChainFailure, + }, { Name: "removetx", TestFunc: testRemoveTx, @@ -599,8 +627,12 @@ var allTestCases = []*lntest.TestCase{ TestFunc: testNativeSQLNoMigration, }, { - Name: "sweep anchor cpfp local force close", - TestFunc: testSweepAnchorCPFPLocalForceClose, + Name: "sweep cpfp anchor outgoing timeout", + TestFunc: testSweepCPFPAnchorOutgoingTimeout, + }, + { + Name: "sweep cpfp anchor incoming timeout", + TestFunc: testSweepCPFPAnchorIncomingTimeout, }, { Name: "sweep htlcs", diff --git a/itest/lnd_amp_test.go b/itest/lnd_amp_test.go index a96911319..23bfd8654 100644 --- a/itest/lnd_amp_test.go +++ b/itest/lnd_amp_test.go @@ -129,6 +129,7 @@ func testSendPaymentAMPInvoiceCase(ht *lntest.HarnessTest, PaymentAddr: externalPayAddr, TimeoutSeconds: 60, FeeLimitMsat: noFeeLimitMsat, + Amp: true, } payment := ht.SendPaymentAssertSettled(mts.alice, sendReq) @@ -252,6 +253,7 @@ func testSendPaymentAMPInvoiceRepeat(ht *lntest.HarnessTest) { // Now we'll use Carol to pay the invoice that Dave created. ht.CompletePaymentRequests( carol, []string{addInvoiceResp.PaymentRequest}, + lntest.WithAMP(), ) // Dave should get a notification that the invoice has been settled. @@ -274,6 +276,7 @@ func testSendPaymentAMPInvoiceRepeat(ht *lntest.HarnessTest) { // has received another payment. ht.CompletePaymentRequests( carol, []string{addInvoiceResp.PaymentRequest}, + lntest.WithAMP(), ) // Dave should get another notification. diff --git a/itest/lnd_channel_backup_test.go b/itest/lnd_channel_backup_test.go index c29859e32..0e7393dc3 100644 --- a/itest/lnd_channel_backup_test.go +++ b/itest/lnd_channel_backup_test.go @@ -3,7 +3,6 @@ package itest import ( "context" "fmt" - "io/ioutil" "os" "path/filepath" "strconv" @@ -285,7 +284,7 @@ func testChannelBackupRestoreBasic(ht *lntest.HarnessTest) { // Read the entire Multi backup stored within // this node's channel.backup file. - multi, err := ioutil.ReadFile(backupFilePath) + multi, err := os.ReadFile(backupFilePath) require.NoError(st, err) // Now that we have Dave's backup file, we'll @@ -376,7 +375,7 @@ func testChannelBackupRestoreBasic(ht *lntest.HarnessTest) { // Read the entire Multi backup stored within // this node's channel.backup file. - multi, err := ioutil.ReadFile(backupFilePath) + multi, err := os.ReadFile(backupFilePath) require.NoError(st, err) // Now that we have Dave's backup file, we'll @@ -501,7 +500,7 @@ func runChanRestoreScenarioUnConfirmed(ht *lntest.HarnessTest, useFile bool) { backupFilePath := dave.Cfg.ChanBackupPath() // Read the entire Multi backup stored within this node's // channel.backup file. - multi, err = ioutil.ReadFile(backupFilePath) + multi, err = os.ReadFile(backupFilePath) require.NoError(ht, err) } else { // For this restoration method, we'll grab the current @@ -646,7 +645,7 @@ func runChanRestoreScenarioCommitTypes(ht *lntest.HarnessTest, // Read the entire Multi backup stored within this node's // channels.backup file. - multi, err := ioutil.ReadFile(backupFilePath) + multi, err := os.ReadFile(backupFilePath) require.NoError(ht, err) // If this was a zero conf taproot channel, then since it's private, @@ -774,7 +773,7 @@ func runChanRestoreScenarioForceClose(ht *lntest.HarnessTest, zeroConf bool) { // Read the entire Multi backup stored within this node's // channel.backup file. - multi, err := ioutil.ReadFile(backupFilePath) + multi, err := os.ReadFile(backupFilePath) require.NoError(ht, err) // Now that we have Dave's backup file, we'll create a new nodeRestorer @@ -907,7 +906,7 @@ func testChannelBackupUpdates(ht *lntest.HarnessTest) { // the on disk back up file to our currentBackup pointer above. assertBackupFileState := func() { err := wait.NoError(func() error { - packedBackup, err := ioutil.ReadFile(backupFilePath) + packedBackup, err := os.ReadFile(backupFilePath) if err != nil { return fmt.Errorf("unable to read backup "+ "file: %v", err) diff --git a/itest/lnd_channel_force_close_test.go b/itest/lnd_channel_force_close_test.go index dc28f034b..11dcd0acb 100644 --- a/itest/lnd_channel_force_close_test.go +++ b/itest/lnd_channel_force_close_test.go @@ -216,34 +216,17 @@ func channelForceClosureTest(ht *lntest.HarnessTest, // We expect to see Alice's force close tx in the mempool. ht.Miner.GetNumTxsFromMempool(1) - // Assert Alice's has the pending anchor outputs - one for local and - // the other for remote (invalid). - sweeps := ht.AssertNumPendingSweeps(alice, 2) - aliceAnchor := sweeps[0] - if aliceAnchor.Outpoint.TxidStr != waitingClose.Commitments.LocalTxid { - aliceAnchor = sweeps[1] - } - require.Equal(ht, aliceAnchor.Outpoint.TxidStr, - waitingClose.Commitments.LocalTxid) - // Mine a block which should confirm the commitment transaction // broadcast as a result of the force closure. Once mined, we also // expect Alice's anchor sweeping tx being published. ht.MineBlocksAndAssertNumTxes(1, 1) - // Assert Alice's anchor sweeping tx is found in the mempool. - aliceSweepTxid := ht.Miner.AssertNumTxsInMempool(1)[0] - - // Add alice's anchor to our expected set of reports. - op := fmt.Sprintf("%v:%v", aliceAnchor.Outpoint.TxidStr, - aliceAnchor.Outpoint.OutputIndex) - aliceReports[op] = &lnrpc.Resolution{ - ResolutionType: lnrpc.ResolutionType_ANCHOR, - Outcome: lnrpc.ResolutionOutcome_CLAIMED, - SweepTxid: aliceSweepTxid.String(), - Outpoint: aliceAnchor.Outpoint, - AmountSat: uint64(anchorSize), - } + // Assert Alice's has one pending anchor output - because she doesn't + // have incoming HTLCs, her outgoing HTLC won't have a deadline, thus + // she won't use the anchor to perform CPFP. + aliceAnchor := ht.AssertNumPendingSweeps(alice, 1)[0] + require.Equal(ht, aliceAnchor.Outpoint.TxidStr, + waitingClose.Commitments.LocalTxid) // Now that the commitment has been confirmed, the channel should be // marked as force closed. @@ -290,10 +273,8 @@ func channelForceClosureTest(ht *lntest.HarnessTest, } // Mine a block to trigger Carol's sweeper to make decisions on the - // anchor sweeping. This block will also confirm Alice's anchor - // sweeping tx as her anchor is used for CPFP due to there are - // time-sensitive HTLCs. - ht.MineBlocksAndAssertNumTxes(1, 1) + // anchor sweeping. + ht.MineEmptyBlocks(1) // Carol's sweep tx should be in the mempool already, as her output is // not timelocked. @@ -307,7 +288,7 @@ func channelForceClosureTest(ht *lntest.HarnessTest, totalFeeCarol := ht.CalculateTxFee(carolTx) // If we have anchors, add an anchor resolution for carol. - op = fmt.Sprintf("%v:%v", carolAnchor.Outpoint.TxidStr, + op := fmt.Sprintf("%v:%v", carolAnchor.Outpoint.TxidStr, carolAnchor.Outpoint.OutputIndex) carolReports[op] = &lnrpc.Resolution{ ResolutionType: lnrpc.ResolutionType_ANCHOR, @@ -336,27 +317,8 @@ func channelForceClosureTest(ht *lntest.HarnessTest, // commit and anchor outputs. ht.MineBlocksAndAssertNumTxes(1, 1) - // Once Alice's anchor sweeping is mined, she should have no pending - // sweep requests atm. - ht.AssertNumPendingSweeps(alice, 0) - - // TODO(yy): fix the case in 0.18.1 - the CPFP anchor sweeping may be - // replaced with a following request after the above restart - the - // anchor will be offered to the sweeper again with updated params, - // which cannot be swept due to it being uneconomical. - var anchorRecovered bool - err = wait.NoError(func() error { - sweepResp := alice.RPC.ListSweeps(false, 0) - txns := sweepResp.GetTransactionIds().TransactionIds - - if len(txns) >= 1 { - anchorRecovered = true - return nil - } - - return fmt.Errorf("expected 1 sweep tx, got %d", len(txns)) - }, wait.DefaultTimeout) - ht.Logf("waiting for Alice's anchor sweep to be broadcast: %v", err) + // Alice should still have the anchor sweeping request. + ht.AssertNumPendingSweeps(alice, 1) // The following restart checks to ensure that outputs in the // kindergarten bucket are persisted while waiting for the required @@ -399,12 +361,8 @@ func channelForceClosureTest(ht *lntest.HarnessTest, return errors.New("all funds should still be in " + "limbo") } - if !anchorRecovered { - return nil - } - if forceClose.RecoveredBalance != anchorSize { - return fmt.Errorf("expected %v to be recovered", - anchorSize) + if forceClose.RecoveredBalance != 0 { + return errors.New("no funds should be recovered") } return nil @@ -417,7 +375,11 @@ func channelForceClosureTest(ht *lntest.HarnessTest, // At this point, the CSV will expire in the next block, meaning that // the output should be offered to the sweeper. - aliceCommit := ht.AssertNumPendingSweeps(alice, 1)[0] + sweeps := ht.AssertNumPendingSweeps(alice, 2) + commitSweep, anchorSweep := sweeps[0], sweeps[1] + if commitSweep.AmountSat < anchorSweep.AmountSat { + commitSweep, anchorSweep = anchorSweep, commitSweep + } // Restart Alice to ensure that she resumes watching the finalized // commitment sweep txid. @@ -438,16 +400,27 @@ func channelForceClosureTest(ht *lntest.HarnessTest, } // We expect a resolution which spends our commit output. - op = fmt.Sprintf("%v:%v", aliceCommit.Outpoint.TxidStr, - aliceCommit.Outpoint.OutputIndex) + op = fmt.Sprintf("%v:%v", commitSweep.Outpoint.TxidStr, + commitSweep.Outpoint.OutputIndex) aliceReports[op] = &lnrpc.Resolution{ ResolutionType: lnrpc.ResolutionType_COMMIT, Outcome: lnrpc.ResolutionOutcome_CLAIMED, SweepTxid: sweepingTXID.String(), - Outpoint: aliceCommit.Outpoint, + Outpoint: commitSweep.Outpoint, AmountSat: uint64(aliceBalance), } + // Add alice's anchor to our expected set of reports. + op = fmt.Sprintf("%v:%v", aliceAnchor.Outpoint.TxidStr, + aliceAnchor.Outpoint.OutputIndex) + aliceReports[op] = &lnrpc.Resolution{ + ResolutionType: lnrpc.ResolutionType_ANCHOR, + Outcome: lnrpc.ResolutionOutcome_CLAIMED, + SweepTxid: sweepingTXID.String(), + Outpoint: aliceAnchor.Outpoint, + AmountSat: uint64(anchorSize), + } + // Check that we can find the commitment sweep in our set of known // sweeps, using the simple transaction id ListSweeps output. ht.AssertSweepFound(alice, sweepingTXID.String(), false, 0) diff --git a/itest/lnd_channel_funding_fund_max_test.go b/itest/lnd_channel_funding_fund_max_test.go index 63d4d471d..fb1987705 100644 --- a/itest/lnd_channel_funding_fund_max_test.go +++ b/itest/lnd_channel_funding_fund_max_test.go @@ -309,7 +309,7 @@ func fundingFee(numInput int, change bool) btcutil.Amount { weightEstimate.AddP2TROutput() } - totalWeight := int64(weightEstimate.Weight()) + totalWeight := weightEstimate.Weight() return feeRate.FeeForWeight(totalWeight) } diff --git a/itest/lnd_channel_funding_utxo_selection_test.go b/itest/lnd_channel_funding_utxo_selection_test.go index 4ba4a557b..8504a17cb 100644 --- a/itest/lnd_channel_funding_utxo_selection_test.go +++ b/itest/lnd_channel_funding_utxo_selection_test.go @@ -330,7 +330,8 @@ func runUtxoSelectionTestCase(ht *lntest.HarnessTest, alice, // When re-selecting a spent output for funding another channel we // expect the respective error message. if tc.reuseUtxo { - expectedErrStr := fmt.Sprintf("outpoint already spent: %s:%d", + expectedErrStr := fmt.Sprintf("outpoint already spent or "+ + "locked by another subsystem: %s:%d", selectedOutpoints[0].TxidStr, selectedOutpoints[0].OutputIndex) expectedErr := fmt.Errorf(expectedErrStr) diff --git a/itest/lnd_channel_policy_test.go b/itest/lnd_channel_policy_test.go index 85527a082..108397453 100644 --- a/itest/lnd_channel_policy_test.go +++ b/itest/lnd_channel_policy_test.go @@ -45,7 +45,9 @@ func testUpdateChannelPolicy(ht *lntest.HarnessTest) { nodes := []*node.HarnessNode{alice, bob} // Alice and Bob should see each other's ChannelUpdates, advertising the - // default routing policies. + // default routing policies. We do not currently set any inbound fees. + // The inbound base and inbound fee rate are advertised with a default + // of 0. expectedPolicy := &lnrpc.RoutingPolicy{ FeeBaseMsat: defaultFeeBase, FeeRateMilliMsat: defaultFeeRate, @@ -227,9 +229,9 @@ func testUpdateChannelPolicy(ht *lntest.HarnessTest) { require.NoError(ht, err, "unable to receive payment stream") require.Empty(ht, sendResp.PaymentError, "expected payment to succeed") - // With our little cluster set up, we'll update the fees and the max - // htlc size for the Bob side of the Alice->Bob channel, and make sure - // all nodes learn about it. + // With our little cluster set up, we'll update the outbound fees and + // the max htlc size for the Bob side of the Alice->Bob channel, and + // make sure all nodes learn about it. Inbound fees remain at 0. baseFee := int64(1500) feeRate := int64(12) timeLockDelta := uint32(66) @@ -298,17 +300,25 @@ func testUpdateChannelPolicy(ht *lntest.HarnessTest) { feeRate = int64(123) timeLockDelta = uint32(22) maxHtlc *= 2 + inboundBaseFee := int32(-400) + inboundFeeRatePpm := int32(-60) expectedPolicy.FeeBaseMsat = baseFee expectedPolicy.FeeRateMilliMsat = testFeeBase * feeRate expectedPolicy.TimeLockDelta = timeLockDelta expectedPolicy.MaxHtlcMsat = maxHtlc + expectedPolicy.InboundFeeBaseMsat = inboundBaseFee + expectedPolicy.InboundFeeRateMilliMsat = inboundFeeRatePpm req = &lnrpc.PolicyUpdateRequest{ BaseFeeMsat: baseFee, FeeRate: float64(feeRate), TimeLockDelta: timeLockDelta, MaxHtlcMsat: maxHtlc, + InboundFee: &lnrpc.InboundFee{ + BaseFeeMsat: inboundBaseFee, + FeeRatePpm: inboundFeeRatePpm, + }, } req.Scope = &lnrpc.PolicyUpdateRequest_Global{} alice.RPC.UpdateChannelPolicy(req) @@ -370,10 +380,13 @@ func testUpdateChannelPolicy(ht *lntest.HarnessTest) { ) } - // Double the base fee and attach to the policy. + // Double the base fee and attach to the policy. Moreover, we set the + // inbound fee to nil and test that it does not change the propagated + // inbound fee. baseFee1 := baseFee * 2 expectedPolicy.FeeBaseMsat = baseFee1 req.BaseFeeMsat = baseFee1 + req.InboundFee = nil assertAliceAndBob(req, expectedPolicy) // Check that Carol has both heard the policy and updated it in her diff --git a/itest/lnd_funding_test.go b/itest/lnd_funding_test.go index 97613429e..38ca71d92 100644 --- a/itest/lnd_funding_test.go +++ b/itest/lnd_funding_test.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/chainreg" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/labels" @@ -1250,3 +1251,211 @@ func deriveFundingShim(ht *lntest.HarnessTest, carol, dave *node.HarnessNode, return fundingShim, chanPoint } + +// testChannelFundingWithUnstableUtxos tests channel openings with restricted +// utxo selection. Internal wallet utxos might be restricted due to another +// subsystems still using it therefore it would be unsecure to use them for +// channel openings. This test focuses on unconfirmed utxos which are still +// being used by the sweeper subsystem hence should only be used when confirmed. +func testChannelFundingWithUnstableUtxos(ht *lntest.HarnessTest) { + // Select funding amt below wumbo size because we later use fundMax to + // open a channel with the total balance. + fundingAmt := btcutil.Amount(3_000_000) + + // We use STATIC_REMOTE_KEY channels because anchor sweeps would + // interfere and create additional utxos. + // Although its the current default we explicitly signal it. + cType := lnrpc.CommitmentType_STATIC_REMOTE_KEY + + // First, we'll create two new nodes that we'll use to open channel + // between for this test. + carol := ht.NewNode("carol", nil) + // We'll attempt at max 2 pending channels, so Dave will need to accept + // two pending ones. + dave := ht.NewNode("dave", []string{ + "--maxpendingchannels=2", + }) + ht.EnsureConnected(carol, dave) + + // Fund Carol's wallet with a confirmed utxo. + ht.FundCoins(fundingAmt, carol) + + // Now spend the coins to create an unconfirmed transaction. This is + // necessary to test also the neutrino behaviour. For neutrino nodes + // only unconfirmed transactions originating from this node will be + // recognized as unconfirmed. + req := &lnrpc.NewAddressRequest{Type: AddrTypeTaprootPubkey} + resp := carol.RPC.NewAddress(req) + + sendCoinsResp := carol.RPC.SendCoins(&lnrpc.SendCoinsRequest{ + Addr: resp.Address, + SendAll: true, + SatPerVbyte: 1, + }) + + walletUtxo := ht.AssertNumUTXOsUnconfirmed(carol, 1)[0] + require.EqualValues(ht, sendCoinsResp.Txid, walletUtxo.Outpoint.TxidStr) + + // We will attempt to open 2 channels at a time. + chanSize := btcutil.Amount(walletUtxo.AmountSat / 3) + + // Open a channel to dave with an unconfirmed utxo. Although this utxo + // is unconfirmed it can be used to open a channel because it did not + // originated from the sweeper subsystem. + update := ht.OpenChannelAssertPending(carol, dave, + lntest.OpenChannelParams{ + Amt: chanSize, + SpendUnconfirmed: true, + CommitmentType: cType, + }) + chanPoint1 := lntest.ChanPointFromPendingUpdate(update) + + // Verify that both nodes know about the channel. + ht.AssertNumPendingOpenChannels(carol, 1) + ht.AssertNumPendingOpenChannels(dave, 1) + + // We open another channel on the fly, funds are unconfirmed but because + // the tx was not created by the sweeper we can use it and open another + // channel. This is a common use case when opening zeroconf channels, + // so unconfirmed utxos originated from prior channel opening are safe + // to use because channel opening should not be RBFed, at least not for + // now. + update = ht.OpenChannelAssertPending(carol, dave, + lntest.OpenChannelParams{ + Amt: chanSize, + SpendUnconfirmed: true, + CommitmentType: cType, + }) + + chanPoint2 := lntest.ChanPointFromPendingUpdate(update) + + ht.AssertNumPendingOpenChannels(carol, 2) + ht.AssertNumPendingOpenChannels(dave, 2) + + // We expect the initial funding tx to confirm and also the two + // unconfirmed channel openings. + ht.MineBlocksAndAssertNumTxes(1, 3) + + // Now we create an unconfirmed utxo which originated from the sweeper + // subsystem and hence is not safe to use for channel openings. We do + // that by dave force-closing the channel. Which let's carol sweep its + // to_remote output which is not encumbered by any relative locktime. + ht.CloseChannelAssertPending(dave, chanPoint2, true) + // Mine the force close commitment transaction. + ht.MineBlocksAndAssertNumTxes(1, 1) + + // Mine one block to trigger the sweep transaction. + ht.MineEmptyBlocks(1) + + // We need to wait for carol initiating the sweep of the to_remote + // output of chanPoint2. + utxos := ht.AssertNumUTXOsUnconfirmed(carol, 1) + + // We filter for the unconfirmed utxo and try to open a channel with + // that utxo. + utxoOpt := fn.Find(func(u *lnrpc.Utxo) bool { + return u.Confirmations == 0 + }, utxos) + fundingUtxo := utxoOpt.UnwrapOrFail(ht.T) + + // Now try to open the channel with this utxo and expect an error. + expectedErr := fmt.Errorf("outpoint already spent or "+ + "locked by another subsystem: %s:%d", + fundingUtxo.Outpoint.TxidStr, + fundingUtxo.Outpoint.OutputIndex) + + ht.OpenChannelAssertErr(carol, dave, + lntest.OpenChannelParams{ + FundMax: true, + SpendUnconfirmed: true, + Outpoints: []*lnrpc.OutPoint{ + fundingUtxo.Outpoint, + }, + }, expectedErr) + + // The channel opening failed because the utxo was unconfirmed and + // originated from the sweeper subsystem. Now we confirm the + // to_remote sweep and expect the channel opening to work. + ht.MineBlocksAndAssertNumTxes(1, 1) + + // Try opening the channel with the same utxo (now confirmed) again. + update = ht.OpenChannelAssertPending(carol, dave, + lntest.OpenChannelParams{ + FundMax: true, + SpendUnconfirmed: true, + Outpoints: []*lnrpc.OutPoint{ + fundingUtxo.Outpoint, + }, + }) + + chanPoint3 := lntest.ChanPointFromPendingUpdate(update) + ht.AssertNumPendingOpenChannels(carol, 1) + ht.AssertNumPendingOpenChannels(dave, 1) + + // We expect chanPoint3 to confirm. + ht.MineBlocksAndAssertNumTxes(1, 1) + + // Force Close the channel and test the opening flow without preselected + // utxos. + // Before we tested the channel funding with a selected coin, now we + // want to make sure that our internal coin selection also adheres to + // the restictions of unstable utxos. + // We create the unconfirmed sweeper originating utxo just like before + // by force-closing a channel from dave's side. + ht.CloseChannelAssertPending(dave, chanPoint3, true) + ht.MineBlocksAndAssertNumTxes(1, 1) + + // Mine one block to trigger the sweep transaction. + ht.MineEmptyBlocks(1) + + // Wait for the to_remote sweep tx to show up in carol's wallet. + ht.AssertNumUTXOsUnconfirmed(carol, 1) + + // Calculate the maximum amount our wallet has for the channel funding + // so that we will use all utxos. + carolBalance := carol.RPC.WalletBalance() + + // Now calculate the fee for the channel opening transaction. We don't + // have to keep a channel reserve because we are using STATIC_REMOTE_KEY + // channels. + // NOTE: The TotalBalance includes the unconfirmed balance as well. + chanSize = btcutil.Amount(carolBalance.TotalBalance) - + fundingFee(2, false) + + // We are trying to open a channel with the maximum amount and expect it + // to fail because one of the utxos cannot be used because it is + // unstable. + expectedErr = fmt.Errorf("not enough witness outputs to create " + + "funding transaction") + + ht.OpenChannelAssertErr(carol, dave, + lntest.OpenChannelParams{ + Amt: chanSize, + SpendUnconfirmed: true, + CommitmentType: cType, + }, expectedErr) + + // Confirm the to_remote sweep utxo. + ht.MineBlocksAndAssertNumTxes(1, 1) + + ht.AssertNumUTXOsConfirmed(carol, 2) + + // Now after the sweep utxo is confirmed it is stable and can be used + // for channel openings again. + update = ht.OpenChannelAssertPending(carol, dave, + lntest.OpenChannelParams{ + Amt: chanSize, + SpendUnconfirmed: true, + CommitmentType: cType, + }) + chanPoint4 := lntest.ChanPointFromPendingUpdate(update) + + // Verify that both nodes know about the channel. + ht.AssertNumPendingOpenChannels(carol, 1) + ht.AssertNumPendingOpenChannels(dave, 1) + + ht.MineBlocksAndAssertNumTxes(1, 1) + + ht.CloseChannel(carol, chanPoint1) + ht.CloseChannel(carol, chanPoint4) +} diff --git a/itest/lnd_misc_test.go b/itest/lnd_misc_test.go index 7124f1488..3c247c4a1 100644 --- a/itest/lnd_misc_test.go +++ b/itest/lnd_misc_test.go @@ -4,7 +4,7 @@ import ( "context" "encoding/hex" "fmt" - "io/ioutil" + "os" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/txscript" @@ -708,7 +708,7 @@ func testAbandonChannel(ht *lntest.HarnessTest) { // To make sure the channel is removed from the backup file as well // when being abandoned, grab a backup snapshot so we can compare it // with the later state. - bkupBefore, err := ioutil.ReadFile(alice.Cfg.ChanBackupPath()) + bkupBefore, err := os.ReadFile(alice.Cfg.ChanBackupPath()) require.NoError(ht, err, "channel backup before abandoning channel") // Send request to abandon channel. @@ -733,7 +733,7 @@ func testAbandonChannel(ht *lntest.HarnessTest) { // Make sure the channel is no longer in the channel backup list. err = wait.NoError(func() error { - bkupAfter, err := ioutil.ReadFile(alice.Cfg.ChanBackupPath()) + bkupAfter, err := os.ReadFile(alice.Cfg.ChanBackupPath()) if err != nil { return fmt.Errorf("could not get channel backup "+ "before abandoning channel: %v", err) @@ -815,13 +815,18 @@ func testSweepAllCoins(ht *lntest.HarnessTest) { TargetConf: 6, }) + // TODO(yy): we still allow default values to be used when neither conf + // target or fee rate is set in 0.18.0. When future release forbidden + // this behavior, we should revive the test below, which asserts either + // conf target or fee rate is set. + // // Send coins to a compatible address without specifying fee rate or // conf target. - ainz.RPC.SendCoinsAssertErr(&lnrpc.SendCoinsRequest{ - Addr: ht.Miner.NewMinerAddress().String(), - SendAll: true, - Label: sendCoinsLabel, - }) + // ainz.RPC.SendCoinsAssertErr(&lnrpc.SendCoinsRequest{ + // Addr: ht.Miner.NewMinerAddress().String(), + // SendAll: true, + // Label: sendCoinsLabel, + // }) // Send coins to a compatible address. ainz.RPC.SendCoins(&lnrpc.SendCoinsRequest{ diff --git a/itest/lnd_mpp_test.go b/itest/lnd_mpp_test.go index 960626500..bf2679846 100644 --- a/itest/lnd_mpp_test.go +++ b/itest/lnd_mpp_test.go @@ -248,8 +248,8 @@ type mppOpenChannelRequest struct { // openChannels is a helper to open channels that sets up a network topology // with three different paths Alice <-> Bob as following, // -// _ Eve _ -// / \ +// _ Eve _ +// / \ // Alice -- Carol ---- Bob // \ / // \__ Dave ____/ diff --git a/itest/lnd_multi-hop-payments_test.go b/itest/lnd_multi-hop-payments_test.go index 2da2213d7..ee07d6f8c 100644 --- a/itest/lnd_multi-hop-payments_test.go +++ b/itest/lnd_multi-hop-payments_test.go @@ -74,8 +74,8 @@ func testMultiHopPayments(ht *lntest.HarnessTest) { const aliceFeeRatePPM = 100000 updateChannelPolicy( ht, alice, chanPointAlice, aliceBaseFeeSat*1000, - aliceFeeRatePPM, 0, 0, chainreg.DefaultBitcoinTimeLockDelta, - maxHtlc, carol, + aliceFeeRatePPM, 0, 0, false, + chainreg.DefaultBitcoinTimeLockDelta, maxHtlc, carol, ) // Define a negative inbound fee for Alice, to verify that this is @@ -85,9 +85,19 @@ func testMultiHopPayments(ht *lntest.HarnessTest) { aliceInboundFeeRate = -50000 // 5% ) + // We update the channel twice. The first time we set the inbound fee, + // the second time we don't. This is done to test whether the switch is + // still aware of the inbound fees. updateChannelPolicy( ht, alice, chanPointDave, 0, 0, - aliceInboundBaseFeeMsat, aliceInboundFeeRate, + aliceInboundBaseFeeMsat, aliceInboundFeeRate, true, + chainreg.DefaultBitcoinTimeLockDelta, maxHtlc, + dave, + ) + + updateChannelPolicy( + ht, alice, chanPointDave, 0, 0, + aliceInboundBaseFeeMsat, aliceInboundFeeRate, false, chainreg.DefaultBitcoinTimeLockDelta, maxHtlc, dave, ) @@ -96,7 +106,7 @@ func testMultiHopPayments(ht *lntest.HarnessTest) { const daveFeeRatePPM = 150000 updateChannelPolicy( ht, dave, chanPointDave, daveBaseFeeSat*1000, daveFeeRatePPM, - 0, 0, + 0, 0, true, chainreg.DefaultBitcoinTimeLockDelta, maxHtlc, carol, ) @@ -236,8 +246,8 @@ func testMultiHopPayments(ht *lntest.HarnessTest) { // // NOTE: only used in current test. func updateChannelPolicy(ht *lntest.HarnessTest, hn *node.HarnessNode, - chanPoint *lnrpc.ChannelPoint, baseFee int64, - feeRate int64, inboundBaseFee, inboundFeeRate int32, + chanPoint *lnrpc.ChannelPoint, baseFee int64, feeRate int64, + inboundBaseFee, inboundFeeRate int32, updateInboundFee bool, timeLockDelta uint32, maxHtlc uint64, listenerNode *node.HarnessNode) { expectedPolicy := &lnrpc.RoutingPolicy{ @@ -250,6 +260,14 @@ func updateChannelPolicy(ht *lntest.HarnessTest, hn *node.HarnessNode, InboundFeeRateMilliMsat: inboundFeeRate, } + var inboundFee *lnrpc.InboundFee + if updateInboundFee { + inboundFee = &lnrpc.InboundFee{ + BaseFeeMsat: inboundBaseFee, + FeeRatePpm: inboundFeeRate, + } + } + updateFeeReq := &lnrpc.PolicyUpdateRequest{ BaseFeeMsat: baseFee, FeeRate: float64(feeRate) / testFeeBase, @@ -257,9 +275,8 @@ func updateChannelPolicy(ht *lntest.HarnessTest, hn *node.HarnessNode, Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{ ChanPoint: chanPoint, }, - MaxHtlcMsat: maxHtlc, - InboundBaseFeeMsat: inboundBaseFee, - InboundFeeRatePpm: inboundFeeRate, + MaxHtlcMsat: maxHtlc, + InboundFee: inboundFee, } hn.RPC.UpdateChannelPolicy(updateFeeReq) diff --git a/itest/lnd_multi-hop_test.go b/itest/lnd_multi-hop_test.go index fa4590d04..09b2cdf46 100644 --- a/itest/lnd_multi-hop_test.go +++ b/itest/lnd_multi-hop_test.go @@ -713,10 +713,6 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, // to be mined to trigger a force close later on. var blocksMined uint32 - // Increase the fee estimate so that the following force close tx will - // be cpfp'ed. - ht.SetFeeEstimate(30000) - // Now that all parties have the HTLC locked in, we'll immediately // force close the Bob -> Carol channel. This should trigger contract // resolution mode for both of them. @@ -755,7 +751,7 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, ht.MineEmptyBlocks(int(defaultCSV - blocksMined)) blocksMined = defaultCSV - // Assert Bob has the sweep and trigger it.. + // Assert Bob has the sweep and trigger it. ht.AssertNumPendingSweeps(bob, 1) ht.MineEmptyBlocks(1) blocksMined++ @@ -1523,10 +1519,6 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, ht.AssertNumPendingSweeps(bob, 1) ht.AssertNumPendingSweeps(alice, 1) - // Mine a block to confirm Alice's CPFP anchor sweeping. - ht.MineBlocksAndAssertNumTxes(1, 1) - blocksMined++ - // Mine enough blocks for Alice to sweep her funds from the force // closed channel. AssertStreamChannelForceClosed() already mined a // block containing the commitment tx and the commit sweep tx will be @@ -1537,7 +1529,7 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, blocksMined = defaultCSV // Alice should now sweep her funds. - ht.AssertNumPendingSweeps(alice, 1) + ht.AssertNumPendingSweeps(alice, 2) // Mine a block to trigger the sweep. ht.MineEmptyBlocks(1) @@ -1690,7 +1682,7 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, ht.MineEmptyBlocks(numBlocks) // Both Alice and Bob should offer their commit sweeps. - ht.AssertNumPendingSweeps(alice, 1) + ht.AssertNumPendingSweeps(alice, 2) ht.AssertNumPendingSweeps(bob, 1) // Mine a block to trigger the sweeps. @@ -2472,7 +2464,6 @@ func runExtraPreimageFromRemoteCommit(ht *lntest.HarnessTest, if ht.IsNeutrinoBackend() { // Mine a block to confirm Carol's 2nd level success tx. ht.MineBlocksAndAssertNumTxes(1, 1) - numTxesMempool-- numBlocks-- } @@ -2503,6 +2494,15 @@ func runExtraPreimageFromRemoteCommit(ht *lntest.HarnessTest, case lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE: } + // For neutrino backend, Carol's second-stage sweep should be offered + // to her sweeper. + if ht.IsNeutrinoBackend() { + ht.AssertNumPendingSweeps(carol, 1) + + // Mine a block to trigger the sweep. + ht.MineEmptyBlocks(1) + } + // Mine a block to clean the mempool. ht.MineBlocksAndAssertNumTxes(1, numTxesMempool) diff --git a/itest/lnd_psbt_test.go b/itest/lnd_psbt_test.go index fd3db1211..84a9c4bfb 100644 --- a/itest/lnd_psbt_test.go +++ b/itest/lnd_psbt_test.go @@ -1021,7 +1021,7 @@ func runFundAndSignPsbt(ht *lntest.HarnessTest, alice *node.HarnessNode) { for _, addrType := range spendAddrTypes { for _, changeType := range changeAddrTypes { - ht.Logf("testing with address type %s and"+ + ht.Logf("testing with address type %s and "+ "change address type %s", addrType, changeType) // First, spend all the coins in the wallet to an @@ -1603,3 +1603,321 @@ func testPsbtChanFundingFailFlow(ht *lntest.HarnessTest) { // funding workflow with an internal error. ht.ReceiveOpenChannelError(chanUpdates, chanfunding.ErrRemoteCanceled) } + +// testPsbtChanFundingWithUnstableUtxos tests that channel openings with +// unstable utxos, in this case in particular unconfirmed utxos still in use by +// the sweeper subsystem, are not considered when opening a channel. They bear +// the risk of being RBFed and are therefore not safe to open a channel with. +func testPsbtChanFundingWithUnstableUtxos(ht *lntest.HarnessTest) { + fundingAmt := btcutil.Amount(2_000_000) + + // First, we'll create two new nodes that we'll use to open channel + // between for this test. + carol := ht.NewNode("carol", nil) + dave := ht.NewNode("dave", nil) + ht.EnsureConnected(carol, dave) + + // Fund Carol's wallet with a confirmed utxo. + ht.FundCoins(fundingAmt, carol) + + ht.AssertNumUTXOs(carol, 1) + + // Now spend the coins to create an unconfirmed transaction. This is + // necessary to test also the neutrino behaviour. For neutrino nodes + // only unconfirmed transactions originating from this node will be + // recognized as unconfirmed. + req := &lnrpc.NewAddressRequest{Type: AddrTypeTaprootPubkey} + resp := carol.RPC.NewAddress(req) + + sendCoinsResp := carol.RPC.SendCoins(&lnrpc.SendCoinsRequest{ + Addr: resp.Address, + SendAll: true, + SatPerVbyte: 1, + }) + + walletUtxo := ht.AssertNumUTXOsUnconfirmed(carol, 1)[0] + require.EqualValues(ht, sendCoinsResp.Txid, walletUtxo.Outpoint.TxidStr) + + chanSize := btcutil.Amount(walletUtxo.AmountSat / 2) + + // We use STATIC_REMOTE_KEY channels to easily generate sweeps without + // anchor sweeps interfering. + cType := lnrpc.CommitmentType_STATIC_REMOTE_KEY + + // We open a normal channel so that we can force-close it and produce + // a sweeper originating utxo. + update := ht.OpenChannelAssertPending(carol, dave, + lntest.OpenChannelParams{ + Amt: chanSize, + SpendUnconfirmed: true, + }) + channelPoint := lntest.ChanPointFromPendingUpdate(update) + ht.MineBlocksAndAssertNumTxes(1, 2) + + // Now force close the channel by dave to generate a utxo which is + // swept by the sweeper. We have STATIC_REMOTE_KEY Channel Types. + ht.CloseChannelAssertPending(dave, channelPoint, true) + ht.MineBlocksAndAssertNumTxes(1, 1) + + // Mine one block to trigger the sweep transaction. + ht.MineEmptyBlocks(1) + + // We wait for the to_remote sweep tx. + ht.AssertNumUTXOsUnconfirmed(carol, 1) + + // We need the maximum funding amount to ensure we are opening the next + // channel with all available utxos. + carolBalance := carol.RPC.WalletBalance() + + // The max chan size needs to account for the fee opening the channel + // itself. + // NOTE: We need to always account for a change here, because their is + // an inaccurarcy in the backend code. + chanSize = btcutil.Amount(carolBalance.TotalBalance) - + fundingFee(2, true) + + // Now open a channel of this amount via a psbt workflow. + // At this point, we can begin our PSBT channel funding workflow. We'll + // start by generating a pending channel ID externally that will be used + // to track this new funding type. + pendingChanID := ht.Random32Bytes() + + // Now that we have the pending channel ID, Carol will open the channel + // by specifying a PSBT shim. We expect it to fail because we try to + // fund a channel with the maximum amount of our wallet, which also + // includes an unstable utxo originating from the sweeper. + chanUpdates, tempPsbt := ht.OpenChannelPsbt( + carol, dave, lntest.OpenChannelParams{ + Amt: chanSize, + FundingShim: &lnrpc.FundingShim{ + Shim: &lnrpc.FundingShim_PsbtShim{ + PsbtShim: &lnrpc.PsbtShim{ + PendingChanId: pendingChanID, + }, + }, + }, + CommitmentType: cType, + SpendUnconfirmed: true, + }, + ) + + fundReq := &walletrpc.FundPsbtRequest{ + Template: &walletrpc.FundPsbtRequest_Psbt{ + Psbt: tempPsbt, + }, + Fees: &walletrpc.FundPsbtRequest_SatPerVbyte{ + SatPerVbyte: 50, + }, + MinConfs: 0, + SpendUnconfirmed: true, + } + carol.RPC.FundPsbtAssertErr(fundReq) + + // We confirm the sweep transaction and make sure we see it as confirmed + // from the perspective of the underlying wallet. + ht.MineBlocksAndAssertNumTxes(1, 1) + + // We expect 2 confirmed utxos, the change of the prior successful + // channel opening and the confirmed to_remote output. + ht.AssertNumUTXOsConfirmed(carol, 2) + + // We fund the psbt request again and now all utxo are stable and can + // finally be used to fund the channel. + fundResp := carol.RPC.FundPsbt(fundReq) + + // We verify the psbt before finalizing it. + carol.RPC.FundingStateStep(&lnrpc.FundingTransitionMsg{ + Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{ + PsbtVerify: &lnrpc.FundingPsbtVerify{ + PendingChanId: pendingChanID, + FundedPsbt: fundResp.FundedPsbt, + }, + }, + }) + + // Now we'll ask Carol's wallet to sign the PSBT so we can finish the + // funding flow. + finalizeReq := &walletrpc.FinalizePsbtRequest{ + FundedPsbt: fundResp.FundedPsbt, + } + finalizeRes := carol.RPC.FinalizePsbt(finalizeReq) + + // We've signed our PSBT now, let's pass it to the intent again. + carol.RPC.FundingStateStep(&lnrpc.FundingTransitionMsg{ + Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{ + PsbtFinalize: &lnrpc.FundingPsbtFinalize{ + PendingChanId: pendingChanID, + SignedPsbt: finalizeRes.SignedPsbt, + }, + }, + }) + + // Consume the "channel pending" update. This waits until the funding + // transaction was fully compiled. + updateResp := ht.ReceiveOpenChannelUpdate(chanUpdates) + upd, ok := updateResp.Update.(*lnrpc.OpenStatusUpdate_ChanPending) + require.True(ht, ok) + channelPoint2 := &lnrpc.ChannelPoint{ + FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ + FundingTxidBytes: upd.ChanPending.Txid, + }, + OutputIndex: upd.ChanPending.OutputIndex, + } + + var finalTx wire.MsgTx + err := finalTx.Deserialize(bytes.NewReader(finalizeRes.RawFinalTx)) + require.NoError(ht, err) + + txHash := finalTx.TxHash() + block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] + ht.Miner.AssertTxInBlock(block, &txHash) + + // Now we do the same but instead use preselected utxos to verify that + // these utxos respects the utxo restrictions on sweeper unconfirmed + // inputs as well. + + // Now force close the channel by dave to generate a utxo which is + // swept by the sweeper. We have STATIC_REMOTE_KEY Channel Types. + ht.CloseChannelAssertPending(dave, channelPoint2, true) + ht.MineBlocksAndAssertNumTxes(1, 1) + + // Mine one block to trigger the sweep transaction. + ht.MineEmptyBlocks(1) + + // We wait for the to_remote sweep tx of channelPoint2. + utxos := ht.AssertNumUTXOsUnconfirmed(carol, 1) + + // We need the maximum funding amount to ensure we are opening the next + // channel with all available utxos. + carolBalance = carol.RPC.WalletBalance() + + // The max chan size needs to account for the fee opening the channel + // itself. + // NOTE: We need to always account for a change here, because their is + // an inaccurarcy in the backend code calculating the fee of a 1 input + // one output transaction, it always account for a channge in that case + // as well. + chanSize = btcutil.Amount(carolBalance.TotalBalance) - + fundingFee(2, true) + + // Now open a channel of this amount via a psbt workflow. + // At this point, we can begin our PSBT channel funding workflow. We'll + // start by generating a pending channel ID externally that will be used + // to track this new funding type. + pendingChanID = ht.Random32Bytes() + + // Now that we have the pending channel ID, Carol will open the channel + // by specifying a PSBT shim. We expect it to fail because we try to + // fund a channel with the maximum amount of our wallet, which also + // includes an unstable utxo originating from the sweeper. + chanUpdates, tempPsbt = ht.OpenChannelPsbt( + carol, dave, lntest.OpenChannelParams{ + Amt: chanSize, + FundingShim: &lnrpc.FundingShim{ + Shim: &lnrpc.FundingShim_PsbtShim{ + PsbtShim: &lnrpc.PsbtShim{ + PendingChanId: pendingChanID, + }, + }, + }, + CommitmentType: cType, + SpendUnconfirmed: true, + }, + ) + // Add selected utxos to the funding intent. + decodedPsbt, err := psbt.NewFromRawBytes( + bytes.NewReader(tempPsbt), false, + ) + require.NoError(ht, err) + + for _, input := range utxos { + txHash, err := chainhash.NewHashFromStr(input.Outpoint.TxidStr) + require.NoError(ht, err) + decodedPsbt.UnsignedTx.TxIn = append( + decodedPsbt.UnsignedTx.TxIn, &wire.TxIn{ + PreviousOutPoint: wire.OutPoint{ + Hash: *txHash, + Index: input.Outpoint.OutputIndex, + }, + }) + + // The inputs we are using to fund the transaction are known to + // the internal wallet that's why we just append an empty input + // element so that the parsing of the psbt package succeeds. + decodedPsbt.Inputs = append(decodedPsbt.Inputs, psbt.PInput{}) + } + + var psbtBytes bytes.Buffer + err = decodedPsbt.Serialize(&psbtBytes) + require.NoError(ht, err) + + fundReq = &walletrpc.FundPsbtRequest{ + Template: &walletrpc.FundPsbtRequest_Psbt{ + Psbt: psbtBytes.Bytes(), + }, + Fees: &walletrpc.FundPsbtRequest_SatPerVbyte{ + SatPerVbyte: 50, + }, + MinConfs: 0, + SpendUnconfirmed: true, + } + carol.RPC.FundPsbtAssertErr(fundReq) + + ht.MineBlocksAndAssertNumTxes(1, 1) + + // We expect 2 confirmed utxos, the change of the last successful + // channel opening and the confirmed to_remote output of channelPoint2. + ht.AssertNumUTXOsConfirmed(carol, 2) + + // After the confirmation of the sweep to_remote output the funding + // will now proceed. + fundResp = carol.RPC.FundPsbt(fundReq) + + // We verify the funded psbt. + carol.RPC.FundingStateStep(&lnrpc.FundingTransitionMsg{ + Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{ + PsbtVerify: &lnrpc.FundingPsbtVerify{ + PendingChanId: pendingChanID, + FundedPsbt: fundResp.FundedPsbt, + }, + }, + }) + + // Now we'll ask Carol's wallet to sign the PSBT so we can finish the + // funding flow. + finalizeReq = &walletrpc.FinalizePsbtRequest{ + FundedPsbt: fundResp.FundedPsbt, + } + finalizeRes = carol.RPC.FinalizePsbt(finalizeReq) + + // We've signed our PSBT now, let's pass it to the intent again. + carol.RPC.FundingStateStep(&lnrpc.FundingTransitionMsg{ + Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{ + PsbtFinalize: &lnrpc.FundingPsbtFinalize{ + PendingChanId: pendingChanID, + SignedPsbt: finalizeRes.SignedPsbt, + }, + }, + }) + + // Consume the "channel pending" update. This waits until the funding + // transaction was fully compiled. + updateResp = ht.ReceiveOpenChannelUpdate(chanUpdates) + upd, ok = updateResp.Update.(*lnrpc.OpenStatusUpdate_ChanPending) + require.True(ht, ok) + channelPoint3 := &lnrpc.ChannelPoint{ + FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ + FundingTxidBytes: upd.ChanPending.Txid, + }, + OutputIndex: upd.ChanPending.OutputIndex, + } + + err = finalTx.Deserialize(bytes.NewReader(finalizeRes.RawFinalTx)) + require.NoError(ht, err) + + txHash = finalTx.TxHash() + block = ht.MineBlocksAndAssertNumTxes(1, 1)[0] + ht.Miner.AssertTxInBlock(block, &txHash) + + ht.CloseChannel(carol, channelPoint3) +} diff --git a/itest/lnd_recovery_test.go b/itest/lnd_recovery_test.go index c3e6efccd..766d10956 100644 --- a/itest/lnd_recovery_test.go +++ b/itest/lnd_recovery_test.go @@ -379,7 +379,7 @@ func testRescanAddressDetection(ht *lntest.HarnessTest) { estimator := input.TxWeightEstimator{} estimator.AddP2WKHInput() estimator.AddP2WKHOutput() - estimatedWeight := int64(estimator.Weight()) + estimatedWeight := estimator.Weight() requiredFee := feeRate.FeeForWeight(estimatedWeight) tx := wire.NewMsgTx(2) diff --git a/itest/lnd_route_blinding_test.go b/itest/lnd_route_blinding_test.go index e2d1ec033..4b0eef859 100644 --- a/itest/lnd_route_blinding_test.go +++ b/itest/lnd_route_blinding_test.go @@ -355,14 +355,16 @@ func (b *blindedForwardTest) setup( ctx context.Context) *routing.BlindedPayment { b.carol = b.ht.NewNode("Carol", []string{ - "requireinterceptor", + "--requireinterceptor", "--bitcoin.timelockdelta=18", }) var err error b.carolInterceptor, err = b.carol.RPC.Router.HtlcInterceptor(ctx) require.NoError(b.ht, err, "interceptor") - b.dave = b.ht.NewNode("Dave", nil) + b.dave = b.ht.NewNode("Dave", []string{ + "--bitcoin.timelockdelta=18", + }) b.channels = setupFourHopNetwork(b.ht, b.carol, b.dave) @@ -448,9 +450,11 @@ func (b *blindedForwardTest) createRouteToBlinded(paymentAmt int64, return resp.Routes[0] } -// sendBlindedPayment dispatches a payment to the route provided. +// sendBlindedPayment dispatches a payment to the route provided, returning a +// cancel function for the payment. Timeout is set for very long to allow +// time for on-chain resolution. func (b *blindedForwardTest) sendBlindedPayment(ctx context.Context, - route *lnrpc.Route) { + route *lnrpc.Route) func() { hash := sha256.Sum256(b.preimage[:]) sendReq := &routerrpc.SendToRouteRequest{ @@ -458,11 +462,13 @@ func (b *blindedForwardTest) sendBlindedPayment(ctx context.Context, Route: route, } - // Dispatch in a goroutine because this call is blocking - we assume - // that we'll have assertions that this payment is sent by the caller. + ctx, cancel := context.WithTimeout(ctx, time.Hour) go func() { - b.ht.Alice.RPC.SendToRouteV2(sendReq) + _, err := b.ht.Alice.RPC.Router.SendToRouteV2(ctx, sendReq) + require.NoError(b.ht, err) }() + + return cancel } // interceptFinalHop launches a goroutine to intercept Carol's htlcs and @@ -524,6 +530,43 @@ func (b *blindedForwardTest) interceptFinalHop() func(routerrpc.ResolveHoldForwa return resolve } +// drainCarolLiquidity will drain all of the liquidity in Carol's channel in +// the direction requested: +// - incoming: Carol has no incoming liquidity from Bob +// - outgoing: Carol has no outgoing liquidity to Dave. +func (b *blindedForwardTest) drainCarolLiquidity(incoming bool) { + sendingNode := b.carol + receivingNode := b.dave + + if incoming { + sendingNode = b.ht.Bob + receivingNode = b.carol + } + + resp := sendingNode.RPC.ListChannels(&lnrpc.ListChannelsRequest{ + Peer: receivingNode.PubKey[:], + }) + require.Len(b.ht, resp.Channels, 1) + + // We can't send our channel reserve, and leave some buffer for fees. + paymentAmt := resp.Channels[0].LocalBalance - + int64(resp.Channels[0].RemoteConstraints.ChanReserveSat) - 25000 + + invoice := receivingNode.RPC.AddInvoice(&lnrpc.Invoice{ + // Leave some leeway for fees for the HTLC. + Value: paymentAmt, + }) + + pmtClient := sendingNode.RPC.SendPayment( + &routerrpc.SendPaymentRequest{ + PaymentRequest: invoice.PaymentRequest, + TimeoutSeconds: 60, + }, + ) + + b.ht.AssertPaymentStatusFromStream(pmtClient, lnrpc.Payment_SUCCEEDED) +} + // setupFourHopNetwork creates a network with the following topology and // liquidity: // Alice (100k)----- Bob (100k) ----- Carol (100k) ----- Dave @@ -768,7 +811,8 @@ func testForwardBlindedRoute(ht *lntest.HarnessTest) { resolveHTLC := testCase.interceptFinalHop() // Once our interceptor is set up, we can send the blinded payment. - testCase.sendBlindedPayment(ctx, blindedRoute) + cancelPmt := testCase.sendBlindedPayment(ctx, blindedRoute) + defer cancelPmt() // Wait for the HTLC to be active on Alice's channel. hash := sha256.Sum256(testCase.preimage[:]) @@ -788,3 +832,231 @@ func testForwardBlindedRoute(ht *lntest.HarnessTest) { ht.AssertHTLCNotActive(ht.Bob, testCase.channels[1], hash[:]) ht.AssertHTLCNotActive(ht.Alice, testCase.channels[0], hash[:]) } + +// Tests handling of errors from the receiving node in a blinded route, testing +// a payment over: Alice -- Bob -- Carol -- Dave, where Bob is the introduction +// node. +// +// Note that at present the payment fails at Dave because we do not yet support +// receiving to blinded routes. In future, we can substitute this test out to +// trigger an IncorrectPaymentDetails failure. In the meantime, this test +// provides valuable coverage for the case where a node in the route is not +// spec compliant (ie, does not return the blinded failure and just uses a +// normal one) because Dave will not appropriately convert the error. +func testReceiverBlindedError(ht *lntest.HarnessTest) { + ctx, testCase := newBlindedForwardTest(ht) + defer testCase.cleanup() + route := testCase.setup(ctx) + + sendAndResumeBlindedPayment(ctx, ht, testCase, route, true) +} + +// testRelayingBlindedError tests handling of errors from relaying nodes in a +// blinded route, testing a failure over on Carol's outgoing link in the +// following topology: Alice -- Bob -- Carol -- Dave, where Bob is the +// introduction node. +func testRelayingBlindedError(ht *lntest.HarnessTest) { + ctx, testCase := newBlindedForwardTest(ht) + defer testCase.cleanup() + route := testCase.setup(ctx) + + // Before we send our payment, drain all of Carol's liquidity + // so that she can't forward the payment to Dave. + testCase.drainCarolLiquidity(false) + + // Then dispatch the payment through Carol which will fail due to + // a lack of liquidity. This check only happens _after_ the interceptor + // has given the instruction to resume so we can use test + // infrastructure that will go ahead and intercept the payment. + sendAndResumeBlindedPayment(ctx, ht, testCase, route, true) +} + +// sendAndResumeBlindedPayment sends a blinded payment through the test +// network provided, intercepting the payment at Carol and allowing it to +// resume. This utility function allows us to ensure that payments at least +// reach Carol and asserts that all errors appear to originate from the +// introduction node. +func sendAndResumeBlindedPayment(ctx context.Context, ht *lntest.HarnessTest, + testCase *blindedForwardTest, route *routing.BlindedPayment, + interceptAtCarol bool) { + + blindedRoute := testCase.createRouteToBlinded(10_000_000, route) + + // Before we dispatch the payment, spin up a goroutine that will + // intercept the HTLC on Carol's forward. This allows us to ensure + // that the HTLC actually reaches the location we expect it to. + var resolveHTLC func(routerrpc.ResolveHoldForwardAction) + if interceptAtCarol { + resolveHTLC = testCase.interceptFinalHop() + } + + // First, test sending the payment all the way through to Dave. We + // expect this payment to fail, because he does not know how to + // process payments to a blinded route (not yet supported). + cancelPmt := testCase.sendBlindedPayment(ctx, blindedRoute) + defer cancelPmt() + + // When Carol intercepts the HTLC, instruct her to resume the payment + // so that it'll reach Dave and fail. + if interceptAtCarol { + resolveHTLC(routerrpc.ResolveHoldForwardAction_RESUME) + } + + // Wait for the HTLC to reflect as failed for Alice. + preimage, err := lntypes.MakePreimage(testCase.preimage[:]) + require.NoError(ht, err) + pmt := ht.AssertPaymentStatus(ht.Alice, preimage, lnrpc.Payment_FAILED) + require.Len(ht, pmt.Htlcs, 1) + require.EqualValues( + ht, 1, pmt.Htlcs[0].Failure.FailureSourceIndex, + ) + require.Equal( + ht, lnrpc.Failure_INVALID_ONION_BLINDING, + pmt.Htlcs[0].Failure.Code, + ) +} + +// testIntroductionNodeError tests handling of errors in a blinded route when +// the introduction node is the source of the error. This test sends a payment +// over Alice -- Bob -- Carol -- Dave, where Bob is the introduction node and +// has insufficient outgoing liquidity to forward on to carol. +func testIntroductionNodeError(ht *lntest.HarnessTest) { + ctx, testCase := newBlindedForwardTest(ht) + defer testCase.cleanup() + route := testCase.setup(ctx) + + // Before we send our payment, drain all of Carol's incoming liquidity + // so that she can't receive the forward from Bob, causing a failure + // at the introduction node. + testCase.drainCarolLiquidity(true) + + // Send the payment, but do not expect it to reach Carol at all. + sendAndResumeBlindedPayment(ctx, ht, testCase, route, false) +} + +// testDisableIntroductionNode tests disabling of blinded forwards for the +// introduction node. +func testDisableIntroductionNode(ht *lntest.HarnessTest) { + // Disable route blinding for Bob, then re-connect to Alice. + ht.RestartNodeWithExtraArgs(ht.Bob, []string{ + "--protocol.no-route-blinding", + }) + ht.EnsureConnected(ht.Alice, ht.Bob) + + ctx, testCase := newBlindedForwardTest(ht) + defer testCase.cleanup() + route := testCase.setup(ctx) + // We always expect failures to look like they originated at Bob + // because blinded errors are converted. However, our tests intercepts + // all of Carol's forwards and we're not providing it any interceptor + // instructions. This means that the test will hang/timeout at Carol + // if Bob _doesn't_ fail the HTLC back as expected. + sendAndResumeBlindedPayment(ctx, ht, testCase, route, false) +} + +// testErrorHandlingOnChainFailure tests handling of blinded errors when we're +// resolving from an on-chain resolution. This test also tests that we're able +// to resolve blinded HTLCs on chain between restarts, as we've got all the +// infrastructure in place already for error testing. +func testErrorHandlingOnChainFailure(ht *lntest.HarnessTest) { + // Setup a test case, note that we don't use its built in clean up + // because we're going to close a channel so we'll close out the + // rest manually. + ctx, testCase := newBlindedForwardTest(ht) + + // Note that we send a larger amount here do it'll be worthwhile for + // the sweeper to claim. + route := testCase.setup(ctx) + blindedRoute := testCase.createRouteToBlinded(50_000_000, route) + + // Once our interceptor is set up, we can send the blinded payment. + cancelPmt := testCase.sendBlindedPayment(ctx, blindedRoute) + defer cancelPmt() + + // Wait for the HTLC to be active on Alice and Bob's channels. + hash := sha256.Sum256(testCase.preimage[:]) + ht.AssertOutgoingHTLCActive(ht.Alice, testCase.channels[0], hash[:]) + ht.AssertOutgoingHTLCActive(ht.Bob, testCase.channels[1], hash[:]) + + // Intercept the forward on Carol's link, but do not take any action + // so that we have the chance to force close with this HTLC in flight. + carolHTLC, err := testCase.carolInterceptor.Recv() + require.NoError(ht, err) + + // Force close Bob <-> Carol. + closeStream, _ := ht.CloseChannelAssertPending( + ht.Bob, testCase.channels[1], true, + ) + + ht.AssertStreamChannelForceClosed( + ht.Bob, testCase.channels[1], false, closeStream, + ) + + // SuspendCarol so that she can't interfere with the resolution of the + // HTLC from now on. + restartCarol := ht.SuspendNode(testCase.carol) + + // Mine blocks so that Bob will claim his CSV delayed local commitment, + // we've already mined 1 block so we need one less than our CSV. + ht.MineBlocks(node.DefaultCSV - 1) + ht.AssertNumPendingSweeps(ht.Bob, 1) + ht.MineEmptyBlocks(1) + ht.Miner.MineBlocksAndAssertNumTxes(1, 1) + + // Restart bob so that we can test that he's able to recover everything + // he needs to claim a blinded HTLC. + ht.RestartNode(ht.Bob) + + // Mine enough blocks for Bob to trigger timeout of his outgoing HTLC. + // Carol's incoming expiry height is Bob's outgoing so we can use this + // value. + info := ht.Bob.RPC.GetInfo() + target := carolHTLC.IncomingExpiry - info.BlockHeight + ht.MineBlocks(target) + + // Wait for Bob's timeout transaction in the mempool, since we've + // suspended Carol we don't need to account for her commitment output + // claim. + ht.Miner.MineBlocksAndAssertNumTxes(1, 1) + ht.AssertNumPendingSweeps(ht.Bob, 0) + + // Assert that the HTLC has cleared. + ht.WaitForBlockchainSync(ht.Bob) + ht.WaitForBlockchainSync(ht.Alice) + + ht.AssertHTLCNotActive(ht.Bob, testCase.channels[0], hash[:]) + ht.AssertHTLCNotActive(ht.Alice, testCase.channels[0], hash[:]) + + // Wait for the HTLC to reflect as failed for Alice. + paymentStream := ht.Alice.RPC.TrackPaymentV2(hash[:]) + htlcs := ht.ReceiveTrackPayment(paymentStream).Htlcs + require.Len(ht, htlcs, 1) + require.NotNil(ht, htlcs[0].Failure) + require.Equal( + ht, htlcs[0].Failure.Code, + lnrpc.Failure_INVALID_ONION_BLINDING, + ) + + // Clean up the rest of our force close: mine blocks so that Bob's CSV + // expires plus one block to trigger his sweep and then mine it. + ht.MineBlocks(node.DefaultCSV + 1) + ht.Miner.MineBlocksAndAssertNumTxes(1, 1) + + // Bring carol back up so that we can close out the rest of our + // channels cooperatively. She requires an interceptor to start up + // so we just re-register our interceptor. + require.NoError(ht, restartCarol()) + _, err = testCase.carol.RPC.Router.HtlcInterceptor(ctx) + require.NoError(ht, err, "interceptor") + + // Assert that Carol has started up and reconnected to dave so that + // we can close out channels cooperatively. + ht.EnsureConnected(testCase.carol, testCase.dave) + + // Manually close out the rest of our channels and cancel (don't use + // built in cleanup which will try close the already-force-closed + // channel). + ht.CloseChannel(ht.Alice, testCase.channels[0]) + ht.CloseChannel(testCase.carol, testCase.channels[2]) + testCase.cancel() +} diff --git a/itest/lnd_sweep_test.go b/itest/lnd_sweep_test.go index 56d25b4f9..27ad13266 100644 --- a/itest/lnd_sweep_test.go +++ b/itest/lnd_sweep_test.go @@ -24,126 +24,159 @@ import ( "github.com/stretchr/testify/require" ) -// testSweepAnchorCPFPLocalForceClose checks when a channel is force closed by -// a local node with a time-sensitive HTLC, the anchor output is used for -// CPFPing the force close tx. +// testSweepCPFPAnchorOutgoingTimeout checks when a channel is force closed by +// a local node due to the outgoing HTLC times out, the anchor output is used +// for CPFPing the force close tx. // // Setup: -// 1. Fund Alice with 2 UTXOs - she will need two to sweep her anchors from -// the local and remote commitments, with one of them being invalid. -// 2. Fund Bob with no UTXOs - his sweeping txns don't need wallet utxos as he -// doesn't need to sweep any time-sensitive outputs. -// 3. Alice opens a channel with Bob, and sends him an HTLC without being -// settled - we achieve this by letting Bob hold the preimage, which means -// he will consider his incoming HTLC has no preimage. -// 4. Alice force closes the channel. +// 1. Fund Alice with 1 UTXO - she only needs one for the funding process, +// 2. Fund Bob with 1 UTXO - he only needs one for the funding process, and +// the change output will be used for sweeping his anchor on local commit. +// 3. Create a linear network from Alice -> Bob -> Carol. +// 4. Alice pays an invoice to Carol through Bob, with Carol holding the +// settlement. +// 5. Carol goes offline. // // Test: -// 1. Alice's force close tx should be CPFPed using the anchor output. -// 2. Bob attempts to sweep his anchor output and fails due to it's -// uneconomical. -// 3. Alice's RBF attempt is using the fee rates calculated from the deadline -// and budget. -// 4. Wallet UTXOs requirements are met - for Alice she needs at least 2, and -// Bob he needs none. -func testSweepAnchorCPFPLocalForceClose(ht *lntest.HarnessTest) { - // Setup testing params for Alice. +// 1. Bob force closes the channel with Carol, using the anchor output for +// CPFPing the force close tx. +// 2. Bob's anchor output is swept and fee bumped based on its deadline and +// budget. +func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) { + // Setup testing params. // - // startFeeRate is returned by the fee estimator in sat/kw. This - // will be used as the starting fee rate for the linear fee func used - // by Alice. - startFeeRate := chainfee.SatPerKWeight(2000) + // Invoice is 100k sats. + invoiceAmt := btcutil.Amount(100_000) - // deadline is the expected deadline for the CPFP transaction. - deadline := uint32(10) + // Use the smallest CLTV so we can mine fewer blocks. + cltvDelta := routing.MinCLTVDelta + + // deadlineDeltaAnchor is the expected deadline delta for the CPFP + // anchor sweeping tx. + deadlineDeltaAnchor := uint32(cltvDelta / 2) + + // startFeeRateAnchor is the starting fee rate for the CPFP anchor + // sweeping tx. + startFeeRateAnchor := chainfee.SatPerKWeight(2500) // Set up the fee estimator to return the testing fee rate when the // conf target is the deadline. - ht.SetFeeEstimateWithConf(startFeeRate, deadline) - - // Calculate the final ctlv delta based on the expected deadline. - finalCltvDelta := int32(deadline - uint32(routing.BlockPadding) + 1) - - // toLocalCSV is the CSV delay for Alice's to_local output. This value - // is chosen so the commit sweep happens after the anchor sweep, - // enabling us to focus on checking the fees in CPFP here. - toLocalCSV := deadline * 2 - - // htlcAmt is the amount of the HTLC in sats. With default settings, - // this will give us 25000 sats as the budget to sweep the CPFP anchor - // output. - htlcAmt := btcutil.Amount(100_000) - - // Calculate the budget. Since it's a time-sensitive HTLC, we will use - // its value after subtracting its own budget as the CPFP budget. - valueLeft := htlcAmt.MulF64(1 - contractcourt.DefaultBudgetRatio) - budget := valueLeft.MulF64(1 - contractcourt.DefaultBudgetRatio) - - // We now set up testing params for Bob. // - // bobBalance is the push amount when Alice opens the channel with Bob. - // We will use zero here so we can focus on testing the CPFP logic from - // Alice's side here. - bobBalance := btcutil.Amount(0) + // TODO(yy): switch to conf when `blockbeat` is in place. + // ht.SetFeeEstimateWithConf(startFeeRateAnchor, deadlineDeltaAnchor) + ht.SetFeeEstimate(startFeeRateAnchor) - // Make sure our assumptions and calculations are correct. - require.EqualValues(ht, 25000, budget) + // htlcValue is the outgoing HTLC's value. + htlcValue := invoiceAmt - // We now set up the force close scenario. Alice will open a channel - // with Bob, send an HTLC, and then force close it with a - // time-sensitive outgoing HTLC. + // htlcBudget is the budget used to sweep the outgoing HTLC. + htlcBudget := htlcValue.MulF64(contractcourt.DefaultBudgetRatio) + + // cpfpBudget is the budget used to sweep the CPFP anchor. + cpfpBudget := (htlcValue - htlcBudget).MulF64( + contractcourt.DefaultBudgetRatio, + ) + + // Create a preimage, that will be held by Carol. + var preimage lntypes.Preimage + copy(preimage[:], ht.Random32Bytes()) + payHash := preimage.Hash() + + // We now set up the force close scenario. We will create a network + // from Alice -> Bob -> Carol, where Alice will send a payment to Carol + // via Bob, Carol goes offline. We expect Bob to sweep his anchor and + // outgoing HTLC. // - // Prepare node params. + // Prepare params. cfg := []string{ - "--hodl.exit-settle", "--protocol.anchors", + // Use a small CLTV to mine less blocks. + fmt.Sprintf("--bitcoin.timelockdelta=%d", cltvDelta), // Use a very large CSV, this way to_local outputs are never // swept so we can focus on testing HTLCs. - fmt.Sprintf("--bitcoin.defaultremotedelay=%v", toLocalCSV), + fmt.Sprintf("--bitcoin.defaultremotedelay=%v", cltvDelta*10), } openChannelParams := lntest.OpenChannelParams{ - Amt: htlcAmt * 10, - PushAmt: bobBalance, + Amt: invoiceAmt * 10, } - // Create a two hop network: Alice -> Bob. - chanPoints, nodes := createSimpleNetwork(ht, cfg, 2, openChannelParams) + // Create a three hop network: Alice -> Bob -> Carol. + chanPoints, nodes := createSimpleNetwork(ht, cfg, 3, openChannelParams) // Unwrap the results. - chanPoint := chanPoints[0] - alice, bob := nodes[0], nodes[1] + abChanPoint, bcChanPoint := chanPoints[0], chanPoints[1] + alice, bob, carol := nodes[0], nodes[1], nodes[2] - // Send one more utxo to Alice - she will need two utxos to sweep the - // anchor output living on the local and remote commits. - ht.FundCoins(btcutil.SatoshiPerBitcoin, alice) + // For neutrino backend, we need one more UTXO for Bob to create his + // sweeping txns. + if ht.IsNeutrinoBackend() { + ht.FundCoins(btcutil.SatoshiPerBitcoin, bob) + } - // Send a payment with a specified finalCTLVDelta, which will be used - // as our deadline later on when Alice force closes the channel. + // Subscribe the invoice. + streamCarol := carol.RPC.SubscribeSingleInvoice(payHash[:]) + + // With the network active, we'll now add a hodl invoice at Carol's + // end. + invoiceReq := &invoicesrpc.AddHoldInvoiceRequest{ + Value: int64(invoiceAmt), + CltvExpiry: finalCltvDelta, + Hash: payHash[:], + } + invoice := carol.RPC.AddHoldInvoice(invoiceReq) + + // Let Alice pay the invoices. req := &routerrpc.SendPaymentRequest{ - Dest: bob.PubKey[:], - Amt: int64(htlcAmt), - PaymentHash: ht.Random32Bytes(), - FinalCltvDelta: finalCltvDelta, + PaymentRequest: invoice.PaymentRequest, TimeoutSeconds: 60, FeeLimitMsat: noFeeLimitMsat, } - alice.RPC.SendPayment(req) - // Once the HTLC has cleared, all the nodes in our mini network should - // show that the HTLC has been locked in. - ht.AssertNumActiveHtlcs(alice, 1) - ht.AssertNumActiveHtlcs(bob, 1) + // Assert the payments are inflight. + ht.SendPaymentAndAssertStatus(alice, req, lnrpc.Payment_IN_FLIGHT) - // Alice force closes the channel. - _, closeTxid := ht.CloseChannelAssertPending(alice, chanPoint, true) + // Wait for Carol to mark invoice as accepted. There is a small gap to + // bridge between adding the htlc to the channel and executing the exit + // hop logic. + ht.AssertInvoiceState(streamCarol, lnrpc.Invoice_ACCEPTED) - // Now that the channel has been force closed, it should show up in the - // PendingChannels RPC under the waiting close section. - ht.AssertChannelWaitingClose(alice, chanPoint) + // At this point, all 3 nodes should now have an active channel with + // the created HTLCs pending on all of them. + // + // Alice should have one outgoing HTLCs on channel Alice -> Bob. + ht.AssertOutgoingHTLCActive(alice, abChanPoint, payHash[:]) - // Alice should have two pending sweeps, - // - anchor sweeping from her local commitment. - // - anchor sweeping from her remote commitment (invalid). + // Bob should have one incoming HTLC on channel Alice -> Bob, and one + // outgoing HTLC on channel Bob -> Carol. + ht.AssertIncomingHTLCActive(bob, abChanPoint, payHash[:]) + ht.AssertOutgoingHTLCActive(bob, bcChanPoint, payHash[:]) + + // Carol should have one incoming HTLC on channel Bob -> Carol. + ht.AssertIncomingHTLCActive(carol, bcChanPoint, payHash[:]) + + // Let Carol go offline so we can focus on testing Bob's sweeping + // behavior. + ht.Shutdown(carol) + + // We'll now mine enough blocks to trigger Bob to force close channel + // Bob->Carol due to his outgoing HTLC is about to timeout. With the + // default outgoing broadcast delta of zero, this will be the same + // height as the outgoing htlc's expiry height. + numBlocks := padCLTV(uint32( + invoiceReq.CltvExpiry - lncfg.DefaultOutgoingBroadcastDelta, + )) + ht.MineEmptyBlocks(int(numBlocks)) + + // Assert Bob's force closing tx has been broadcast. + closeTxid := ht.Miner.AssertNumTxsInMempool(1)[0] + + // Remember the force close height so we can calculate the deadline + // height. + _, forceCloseHeight := ht.Miner.GetBestBlock() + + // Bob should have two pending sweeps, + // - anchor sweeping from his local commitment. + // - anchor sweeping from his remote commitment (invalid). // // TODO(yy): consider only sweeping the anchor from the local // commitment. Previously we would sweep up to three versions of @@ -152,108 +185,114 @@ func testSweepAnchorCPFPLocalForceClose(ht *lntest.HarnessTest) { // their commitment tx and replaces ours. With the new fee bumping, we // should be safe to only sweep our local anchor since we RBF it on // every new block, which destroys the remote's ability to pin us. - ht.AssertNumPendingSweeps(alice, 2) + sweeps := ht.AssertNumPendingSweeps(bob, 2) - // Bob should have no pending sweeps here. Although he learned about - // the force close tx, because he doesn't have any outgoing HTLCs, he - // doesn't need to sweep anything. - ht.AssertNumPendingSweeps(bob, 0) + // The two anchor sweeping should have the same deadline height. + deadlineHeight := uint32(forceCloseHeight) + deadlineDeltaAnchor + require.Equal(ht, deadlineHeight, sweeps[0].DeadlineHeight) + require.Equal(ht, deadlineHeight, sweeps[1].DeadlineHeight) - // Mine a block so Alice's force closing tx stays in the mempool, which - // also triggers the sweep. + // Remember the deadline height for the CPFP anchor. + anchorDeadline := sweeps[0].DeadlineHeight + + // Mine a block so Bob's force closing tx stays in the mempool, which + // also triggers the CPFP anchor sweep. ht.MineEmptyBlocks(1) - // TODO(yy): we should also handle the edge case where the force close - // tx confirms here - we should cancel the fee bumping attempt for this - // anchor sweep and let it stay in mempool? Or should we unlease the - // wallet input and ask the sweeper to re-sweep the anchor? - // ht.MineBlocksAndAssertNumTxes(1, 1) + // Bob should still have two pending sweeps, + // - anchor sweeping from his local commitment. + // - anchor sweeping from his remote commitment (invalid). + ht.AssertNumPendingSweeps(bob, 2) - // We now check the expected fee and fee rate are used for Alice. + // We now check the expected fee and fee rate are used for Bob's anchor + // sweeping tx. // - // We should see Alice's anchor sweeping tx triggered by the above - // block, along with Alice's force close tx. + // We should see Bob's anchor sweeping tx triggered by the above + // block, along with his force close tx. txns := ht.Miner.GetNumTxsFromMempool(2) // Find the sweeping tx. sweepTx := ht.FindSweepingTxns(txns, 1, *closeTxid)[0] - // Get the weight for Alice's sweep tx. + // Get the weight for Bob's anchor sweeping tx. txWeight := ht.CalculateTxWeight(sweepTx) - // Calculate the fee and fee rate of Alice's sweeping tx. + // Bob should start with the initial fee rate of 2500 sat/kw. + startFeeAnchor := startFeeRateAnchor.FeeForWeight(txWeight) + + // Calculate the fee and fee rate of Bob's sweeping tx. fee := uint64(ht.CalculateTxFee(sweepTx)) feeRate := uint64(ht.CalculateTxFeeRate(sweepTx)) - // Alice should start with the initial fee rate of 2000 sat/kw. - startFee := startFeeRate.FeeForWeight(txWeight) + // feeFuncWidth is the width of the fee function. By the time we got + // here, we've already mined one block, and the fee function maxes + // out one block before the deadline, so the width is the original + // deadline minus 2. + feeFuncWidth := deadlineDeltaAnchor - 2 // Calculate the expected delta increased per block. - // - // NOTE: Assume a wallet tr output is used for fee bumping, with the tx - // weight of 725, we expect this value to be 2355. - feeDeltaAlice := (budget - startFee).MulF64(1 / float64(10)) + feeDelta := (cpfpBudget - startFeeAnchor).MulF64( + 1 / float64(feeFuncWidth), + ) // We expect the startingFee and startingFeeRate being used. Allow some // deviation because weight estimates during tx generation are // estimates. // // TODO(yy): unify all the units and types re int vs uint! - require.InEpsilonf(ht, uint64(startFee), fee, 0.01, - "want %d, got %d", startFee, fee) - require.InEpsilonf(ht, uint64(startFeeRate), feeRate, - 0.01, "want %d, got %d", startFeeRate, fee) + require.InEpsilonf(ht, uint64(startFeeAnchor), fee, 0.01, + "want %d, got %d", startFeeAnchor, fee) + require.InEpsilonf(ht, uint64(startFeeRateAnchor), feeRate, + 0.01, "want %d, got %d", startFeeRateAnchor, fee) - // Bob has no time-sensitive outputs, so he should sweep nothing. - ht.AssertNumPendingSweeps(bob, 0) - - // We now mine deadline-1 empty blocks. For each block mined, Alice - // should perform an RBF on her CPFP anchor sweeping tx. By the end of - // this iteration, we expect Alice to use start sweeping her htlc - // output after one more block. - for i := uint32(1); i <= deadline; i++ { + // We now mine deadline-2 empty blocks. For each block mined, Bob + // should perform an RBF on his CPFP anchor sweeping tx. By the end of + // this iteration, we expect Bob to use up his CPFP budget after one + // more block. + for i := uint32(1); i <= feeFuncWidth-1; i++ { // Mine an empty block. Since the sweeping tx is not confirmed, - // Alice's fee bumper should increase its fees. + // Bob's fee bumper should increase its fees. ht.MineEmptyBlocks(1) - // Alice should still have two pending sweeps, - // - anchor sweeping from her local commitment. - // - anchor sweeping from her remote commitment (invalid). - ht.AssertNumPendingSweeps(alice, 2) + // Bob should still have two pending sweeps, + // - anchor sweeping from his local commitment. + // - anchor sweeping from his remote commitment (invalid). + ht.AssertNumPendingSweeps(bob, 2) - // We expect to see two txns in the mempool, - // - Alice's force close tx. - // - Alice's anchor sweep tx. - ht.Miner.AssertNumTxsInMempool(2) - - // Make sure Alice's old sweeping tx has been removed from the + // Make sure Bob's old sweeping tx has been removed from the // mempool. ht.Miner.AssertTxNotInMempool(sweepTx.TxHash()) + // We expect to see two txns in the mempool, + // - Bob's force close tx. + // - Bob's anchor sweep tx. + ht.Miner.AssertNumTxsInMempool(2) + // We expect the fees to increase by i*delta. - expectedFee := startFee + feeDeltaAlice.MulF64(float64(i)) + expectedFee := startFeeAnchor + feeDelta.MulF64(float64(i)) expectedFeeRate := chainfee.NewSatPerKWeight( - expectedFee, uint64(txWeight), + expectedFee, txWeight, ) - // We should see Alice's anchor sweeping tx being fee bumped - // since it's not confirmed, along with her force close tx. + // We should see Bob's anchor sweeping tx being fee bumped + // since it's not confirmed, along with his force close tx. txns = ht.Miner.GetNumTxsFromMempool(2) // Find the sweeping tx. sweepTx = ht.FindSweepingTxns(txns, 1, *closeTxid)[0] - // Calculate the fee rate of Alice's new sweeping tx. + // Calculate the fee rate of Bob's new sweeping tx. feeRate = uint64(ht.CalculateTxFeeRate(sweepTx)) - // Calculate the fee of Alice's new sweeping tx. + // Calculate the fee of Bob's new sweeping tx. fee = uint64(ht.CalculateTxFee(sweepTx)) - ht.Logf("Alice(deadline=%v): txWeight=%v, expected: [fee=%d, "+ - "feerate=%v], got: [fee=%v, feerate=%v]", deadline-i, - txWeight, expectedFee, expectedFeeRate, fee, feeRate) + ht.Logf("Bob(position=%v): txWeight=%v, expected: [fee=%d, "+ + "feerate=%v], got: [fee=%v, feerate=%v]", + feeFuncWidth-i, txWeight, expectedFee, + expectedFeeRate, fee, feeRate) - // Assert Alice's tx has the expected fee and fee rate. + // Assert Bob's tx has the expected fee and fee rate. require.InEpsilonf(ht, uint64(expectedFee), fee, 0.01, "deadline=%v, want %d, got %d", i, expectedFee, fee) require.InEpsilonf(ht, uint64(expectedFeeRate), feeRate, 0.01, @@ -261,27 +300,34 @@ func testSweepAnchorCPFPLocalForceClose(ht *lntest.HarnessTest) { feeRate) } - // Once out of the above loop, we should've mined deadline-1 blocks. If - // we mine one more block, we'd use up all the CPFP budget. + // We now check the budget has been used up at the deadline-1 block. + // + // Once out of the above loop, we expect to be 2 blocks before the CPFP + // deadline. + _, currentHeight := ht.Miner.GetBestBlock() + require.Equal(ht, int(anchorDeadline-2), int(currentHeight)) + + // Mine one more block, we'd use up all the CPFP budget. ht.MineEmptyBlocks(1) - // Get the last sweeping tx - we should see two txns here, Alice's - // anchor sweeping tx and her force close tx. + // Make sure Bob's old sweeping tx has been removed from the mempool. + ht.Miner.AssertTxNotInMempool(sweepTx.TxHash()) + + // Get the last sweeping tx - we should see two txns here, Bob's anchor + // sweeping tx and his force close tx. txns = ht.Miner.GetNumTxsFromMempool(2) // Find the sweeping tx. sweepTx = ht.FindSweepingTxns(txns, 1, *closeTxid)[0] - // Calculate the fee and fee rate of Alice's new sweeping tx. + // Calculate the fee of Bob's new sweeping tx. fee = uint64(ht.CalculateTxFee(sweepTx)) - feeRate = uint64(ht.CalculateTxFeeRate(sweepTx)) - // Alice should still have two pending sweeps, - // - anchor sweeping from her local commitment. - // - anchor sweeping from her remote commitment (invalid). - ht.AssertNumPendingSweeps(alice, 2) + // Assert the budget is now used up. + require.InEpsilonf(ht, uint64(cpfpBudget), fee, 0.01, "want %d, got %d", + cpfpBudget, fee) - // Mine one more block. Since Alice's budget has been used up, there + // Mine one more block. Since Bob's budget has been used up, there // won't be any more sweeping attempts. We now assert this by checking // that the sweeping tx stayed unchanged. ht.MineEmptyBlocks(1) @@ -289,30 +335,361 @@ func testSweepAnchorCPFPLocalForceClose(ht *lntest.HarnessTest) { // Get the current sweeping tx and assert it stays unchanged. // // We expect two txns here, one for the anchor sweeping, the other for - // the HTLC sweeping. + // the force close tx. txns = ht.Miner.GetNumTxsFromMempool(2) // Find the sweeping tx. currentSweepTx := ht.FindSweepingTxns(txns, 1, *closeTxid)[0] - // Calculate the fee and fee rate of Alice's current sweeping tx. - currentFee := uint64(ht.CalculateTxFee(sweepTx)) - currentFeeRate := uint64(ht.CalculateTxFeeRate(sweepTx)) + // Assert the anchor sweep tx stays unchanged. + require.Equal(ht, sweepTx.TxHash(), currentSweepTx.TxHash()) + + // Mine a block to confirm Bob's sweeping and force close txns, this is + // needed to clean up the mempool. + ht.MineBlocksAndAssertNumTxes(1, 2) + + // The above mined block should confirm Bob's force close tx, and his + // contractcourt will offer the HTLC to his sweeper. We are not testing + // the HTLC sweeping behaviors so we just perform a simple check and + // exit the test. + ht.AssertNumPendingSweeps(bob, 1) + + // Finally, clean the mempool for the next test. + ht.CleanShutDown() +} + +// testSweepCPFPAnchorIncomingTimeout checks when a channel is force closed by +// a local node due to the incoming HTLC is about to time out, the anchor +// output is used for CPFPing the force close tx. +// +// Setup: +// 1. Fund Alice with 1 UTXOs - she only needs one for the funding process, +// 2. Fund Bob with 1 UTXO - he only needs one for the funding process, and +// the change output will be used for sweeping his anchor on local commit. +// 3. Create a linear network from Alice -> Bob -> Carol. +// 4. Alice pays an invoice to Carol through Bob. +// 5. Alice goes offline. +// 6. Carol settles the invoice. +// +// Test: +// 1. Bob force closes the channel with Alice, using the anchor output for +// CPFPing the force close tx. +// 2. Bob's anchor output is swept and fee bumped based on its deadline and +// budget. +func testSweepCPFPAnchorIncomingTimeout(ht *lntest.HarnessTest) { + // Setup testing params. + // + // Invoice is 100k sats. + invoiceAmt := btcutil.Amount(100_000) + + // Use the smallest CLTV so we can mine fewer blocks. + cltvDelta := routing.MinCLTVDelta + + // goToChainDelta is the broadcast delta of Bob's incoming HTLC. When + // the block height is at CLTV-goToChainDelta, Bob will force close the + // channel Alice=>Bob. + goToChainDelta := uint32(lncfg.DefaultIncomingBroadcastDelta) + + // deadlineDeltaAnchor is the expected deadline delta for the CPFP + // anchor sweeping tx. + deadlineDeltaAnchor := goToChainDelta / 2 + + // startFeeRateAnchor is the starting fee rate for the CPFP anchor + // sweeping tx. + startFeeRateAnchor := chainfee.SatPerKWeight(2500) + + // Set up the fee estimator to return the testing fee rate when the + // conf target is the deadline. + // + // TODO(yy): switch to conf when `blockbeat` is in place. + // ht.SetFeeEstimateWithConf(startFeeRateAnchor, deadlineDeltaAnchor) + ht.SetFeeEstimate(startFeeRateAnchor) + + // Create a preimage, that will be held by Carol. + var preimage lntypes.Preimage + copy(preimage[:], ht.Random32Bytes()) + payHash := preimage.Hash() + + // We now set up the force close scenario. We will create a network + // from Alice -> Bob -> Carol, where Alice will send a payment to Carol + // via Bob, Alice goes offline, Carol settles the payment. We expect + // Bob to sweep his anchor and incoming HTLC. + // + // Prepare params. + cfg := []string{ + "--protocol.anchors", + // Use a small CLTV to mine less blocks. + fmt.Sprintf("--bitcoin.timelockdelta=%d", cltvDelta), + // Use a very large CSV, this way to_local outputs are never + // swept so we can focus on testing HTLCs. + fmt.Sprintf("--bitcoin.defaultremotedelay=%v", cltvDelta*10), + } + openChannelParams := lntest.OpenChannelParams{ + Amt: invoiceAmt * 10, + } + + // Create a three hop network: Alice -> Bob -> Carol. + chanPoints, nodes := createSimpleNetwork(ht, cfg, 3, openChannelParams) + + // Unwrap the results. + abChanPoint, bcChanPoint := chanPoints[0], chanPoints[1] + alice, bob, carol := nodes[0], nodes[1], nodes[2] + + // For neutrino backend, we need one more UTXO for Bob to create his + // sweeping txns. + if ht.IsNeutrinoBackend() { + ht.FundCoins(btcutil.SatoshiPerBitcoin, bob) + } + + // Subscribe the invoice. + streamCarol := carol.RPC.SubscribeSingleInvoice(payHash[:]) + + // With the network active, we'll now add a hodl invoice at Carol's + // end. + invoiceReq := &invoicesrpc.AddHoldInvoiceRequest{ + Value: int64(invoiceAmt), + CltvExpiry: finalCltvDelta, + Hash: payHash[:], + } + invoice := carol.RPC.AddHoldInvoice(invoiceReq) + + // Let Alice pay the invoices. + req := &routerrpc.SendPaymentRequest{ + PaymentRequest: invoice.PaymentRequest, + TimeoutSeconds: 60, + FeeLimitMsat: noFeeLimitMsat, + } + + // Assert the payments are inflight. + ht.SendPaymentAndAssertStatus(alice, req, lnrpc.Payment_IN_FLIGHT) + + // Wait for Carol to mark invoice as accepted. There is a small gap to + // bridge between adding the htlc to the channel and executing the exit + // hop logic. + ht.AssertInvoiceState(streamCarol, lnrpc.Invoice_ACCEPTED) + + // At this point, all 3 nodes should now have an active channel with + // the created HTLCs pending on all of them. + // + // Alice should have one outgoing HTLCs on channel Alice -> Bob. + ht.AssertOutgoingHTLCActive(alice, abChanPoint, payHash[:]) + + // Bob should have one incoming HTLC on channel Alice -> Bob, and one + // outgoing HTLC on channel Bob -> Carol. + htlc := ht.AssertIncomingHTLCActive(bob, abChanPoint, payHash[:]) + ht.AssertOutgoingHTLCActive(bob, bcChanPoint, payHash[:]) + + // Calculate the budget used for Bob's anchor sweeping. + // + // htlcValue is the incoming HTLC's value. + htlcValue := btcutil.Amount(htlc.Amount) + + // htlcBudget is the budget used to sweep the incoming HTLC. + htlcBudget := htlcValue.MulF64(contractcourt.DefaultBudgetRatio) + + // cpfpBudget is the budget used to sweep the CPFP anchor. + cpfpBudget := (htlcValue - htlcBudget).MulF64( + contractcourt.DefaultBudgetRatio, + ) + + // Carol should have one incoming HTLC on channel Bob -> Carol. + ht.AssertIncomingHTLCActive(carol, bcChanPoint, payHash[:]) + + // Let Alice go offline. Once Bob later learns the preimage, he + // couldn't settle it with Alice so he has to go onchain to collect it. + ht.Shutdown(alice) + + // Carol settles invoice. + carol.RPC.SettleInvoice(preimage[:]) + + // Bob should have settled his outgoing HTLC with Carol. + ht.AssertHTLCNotActive(bob, bcChanPoint, payHash[:]) + + // We'll now mine enough blocks to trigger Bob to force close channel + // Alice->Bob due to his incoming HTLC is about to timeout. With the + // default incoming broadcast delta of 10, this will be the same + // height as the incoming htlc's expiry height minus 10. + forceCloseHeight := htlc.ExpirationHeight - goToChainDelta + + // Mine till the goToChainHeight is reached. + _, currentHeight := ht.Miner.GetBestBlock() + numBlocks := forceCloseHeight - uint32(currentHeight) + ht.MineEmptyBlocks(int(numBlocks)) + + // Assert Bob's force closing tx has been broadcast. + closeTxid := ht.Miner.AssertNumTxsInMempool(1)[0] + + // Bob should have two pending sweeps, + // - anchor sweeping from his local commitment. + // - anchor sweeping from his remote commitment (invalid). + sweeps := ht.AssertNumPendingSweeps(bob, 2) + + // The two anchor sweeping should have the same deadline height. + deadlineHeight := forceCloseHeight + deadlineDeltaAnchor + require.Equal(ht, deadlineHeight, sweeps[0].DeadlineHeight) + require.Equal(ht, deadlineHeight, sweeps[1].DeadlineHeight) + + // Remember the deadline height for the CPFP anchor. + anchorDeadline := sweeps[0].DeadlineHeight + + // Mine a block so Bob's force closing tx stays in the mempool, which + // also triggers the CPFP anchor sweep. + ht.MineEmptyBlocks(1) + + // Bob should still have two pending sweeps, + // - anchor sweeping from his local commitment. + // - anchor sweeping from his remote commitment (invalid). + ht.AssertNumPendingSweeps(bob, 2) + + // We now check the expected fee and fee rate are used for Bob's anchor + // sweeping tx. + // + // We should see Bob's anchor sweeping tx triggered by the above + // block, along with his force close tx. + txns := ht.Miner.GetNumTxsFromMempool(2) + + // Find the sweeping tx. + sweepTx := ht.FindSweepingTxns(txns, 1, *closeTxid)[0] + + // Get the weight for Bob's anchor sweeping tx. + txWeight := ht.CalculateTxWeight(sweepTx) + + // Bob should start with the initial fee rate of 2500 sat/kw. + startFeeAnchor := startFeeRateAnchor.FeeForWeight(txWeight) + + // Calculate the fee and fee rate of Bob's sweeping tx. + fee := uint64(ht.CalculateTxFee(sweepTx)) + feeRate := uint64(ht.CalculateTxFeeRate(sweepTx)) + + // feeFuncWidth is the width of the fee function. By the time we got + // here, we've already mined one block, and the fee function maxes + // out one block before the deadline, so the width is the original + // deadline minus 2. + feeFuncWidth := deadlineDeltaAnchor - 2 + + // Calculate the expected delta increased per block. + feeDelta := (cpfpBudget - startFeeAnchor).MulF64( + 1 / float64(feeFuncWidth), + ) + + // We expect the startingFee and startingFeeRate being used. Allow some + // deviation because weight estimates during tx generation are + // estimates. + // + // TODO(yy): unify all the units and types re int vs uint! + require.InEpsilonf(ht, uint64(startFeeAnchor), fee, 0.01, + "want %d, got %d", startFeeAnchor, fee) + require.InEpsilonf(ht, uint64(startFeeRateAnchor), feeRate, + 0.01, "want %d, got %d", startFeeRateAnchor, fee) + + // We now mine deadline-2 empty blocks. For each block mined, Bob + // should perform an RBF on his CPFP anchor sweeping tx. By the end of + // this iteration, we expect Bob to use up his CPFP budget after one + // more block. + for i := uint32(1); i <= feeFuncWidth-1; i++ { + // Mine an empty block. Since the sweeping tx is not confirmed, + // Bob's fee bumper should increase its fees. + ht.MineEmptyBlocks(1) + + // Bob should still have two pending sweeps, + // - anchor sweeping from his local commitment. + // - anchor sweeping from his remote commitment (invalid). + ht.AssertNumPendingSweeps(bob, 2) + + // Make sure Bob's old sweeping tx has been removed from the + // mempool. + ht.Miner.AssertTxNotInMempool(sweepTx.TxHash()) + + // We expect to see two txns in the mempool, + // - Bob's force close tx. + // - Bob's anchor sweep tx. + ht.Miner.AssertNumTxsInMempool(2) + + // We expect the fees to increase by i*delta. + expectedFee := startFeeAnchor + feeDelta.MulF64(float64(i)) + expectedFeeRate := chainfee.NewSatPerKWeight( + expectedFee, txWeight, + ) + + // We should see Bob's anchor sweeping tx being fee bumped + // since it's not confirmed, along with his force close tx. + txns = ht.Miner.GetNumTxsFromMempool(2) + + // Find the sweeping tx. + sweepTx = ht.FindSweepingTxns(txns, 1, *closeTxid)[0] + + // Calculate the fee rate of Bob's new sweeping tx. + feeRate = uint64(ht.CalculateTxFeeRate(sweepTx)) + + // Calculate the fee of Bob's new sweeping tx. + fee = uint64(ht.CalculateTxFee(sweepTx)) + + ht.Logf("Bob(position=%v): txWeight=%v, expected: [fee=%d, "+ + "feerate=%v], got: [fee=%v, feerate=%v]", + feeFuncWidth-i, txWeight, expectedFee, + expectedFeeRate, fee, feeRate) + + // Assert Bob's tx has the expected fee and fee rate. + require.InEpsilonf(ht, uint64(expectedFee), fee, 0.01, + "deadline=%v, want %d, got %d", i, expectedFee, fee) + require.InEpsilonf(ht, uint64(expectedFeeRate), feeRate, 0.01, + "deadline=%v, want %d, got %d", i, expectedFeeRate, + feeRate) + } + + // We now check the budget has been used up at the deadline-1 block. + // + // Once out of the above loop, we expect to be 2 blocks before the CPFP + // deadline. + _, currentHeight = ht.Miner.GetBestBlock() + require.Equal(ht, int(anchorDeadline-2), int(currentHeight)) + + // Mine one more block, we'd use up all the CPFP budget. + ht.MineEmptyBlocks(1) + + // Make sure Bob's old sweeping tx has been removed from the mempool. + ht.Miner.AssertTxNotInMempool(sweepTx.TxHash()) + + // Get the last sweeping tx - we should see two txns here, Bob's anchor + // sweeping tx and his force close tx. + txns = ht.Miner.GetNumTxsFromMempool(2) + + // Find the sweeping tx. + sweepTx = ht.FindSweepingTxns(txns, 1, *closeTxid)[0] + + // Calculate the fee of Bob's new sweeping tx. + fee = uint64(ht.CalculateTxFee(sweepTx)) + + // Assert the budget is now used up. + require.InEpsilonf(ht, uint64(cpfpBudget), fee, 0.01, "want %d, got %d", + cpfpBudget, fee) + + // Mine one more block. Since Bob's budget has been used up, there + // won't be any more sweeping attempts. We now assert this by checking + // that the sweeping tx stayed unchanged. + ht.MineEmptyBlocks(1) + + // Get the current sweeping tx and assert it stays unchanged. + // + // We expect two txns here, one for the anchor sweeping, the other for + // the force close tx. + txns = ht.Miner.GetNumTxsFromMempool(2) + + // Find the sweeping tx. + currentSweepTx := ht.FindSweepingTxns(txns, 1, *closeTxid)[0] // Assert the anchor sweep tx stays unchanged. require.Equal(ht, sweepTx.TxHash(), currentSweepTx.TxHash()) - require.Equal(ht, fee, currentFee) - require.Equal(ht, feeRate, currentFeeRate) - // Mine a block to confirm Alice's sweeping and force close txns, this - // is needed to clean up the mempool. + // Mine a block to confirm Bob's sweeping and force close txns, this is + // needed to clean up the mempool. ht.MineBlocksAndAssertNumTxes(1, 2) - // The above mined block should confirm Alice's force close tx, and her - // contractcourt will offer the HTLC to her sweeper. We are not testing + // The above mined block should confirm Bob's force close tx, and his + // contractcourt will offer the HTLC to his sweeper. We are not testing // the HTLC sweeping behaviors so we just perform a simple check and // exit the test. - ht.AssertNumPendingSweeps(alice, 1) + ht.AssertNumPendingSweeps(bob, 1) // Finally, clean the mempool for the next test. ht.CleanShutDown() @@ -497,9 +874,6 @@ func testSweepHTLCs(ht *lntest.HarnessTest) { )) ht.MineBlocks(numBlocks) - // Bob force closes the channel. - // ht.CloseChannelAssertPending(bob, bcChanPoint, true) - // Before we mine empty blocks to check the RBF behavior, we need to be // aware that Bob's incoming HTLC will expire before his outgoing HTLC // deadline is reached. This happens because the incoming HTLC is sent @@ -555,7 +929,7 @@ func testSweepHTLCs(ht *lntest.HarnessTest) { outgoingBudget := 2 * invoiceAmt outgoingTxSize := ht.CalculateTxWeight(outgoingSweep) outgoingEndFeeRate := chainfee.NewSatPerKWeight( - outgoingBudget, uint64(outgoingTxSize), + outgoingBudget, outgoingTxSize, ) // Assert the initial sweeping tx is using the start fee rate. @@ -567,7 +941,7 @@ func testSweepHTLCs(ht *lntest.HarnessTest) { // Now the start fee rate is checked, we can calculate the fee rate // delta. outgoingFeeRateDelta := (outgoingEndFeeRate - outgoingStartFeeRate) / - chainfee.SatPerKWeight(outgoingHTLCDeadline) + chainfee.SatPerKWeight(outgoingHTLCDeadline-1) // outgoingFuncPosition records the position of Bob's fee function used // for his outgoing HTLC sweeping tx. @@ -576,7 +950,8 @@ func testSweepHTLCs(ht *lntest.HarnessTest) { // assertSweepFeeRate is a helper closure that asserts the expected fee // rate is used at the given position for a sweeping tx. assertSweepFeeRate := func(sweepTx *wire.MsgTx, - startFeeRate, delta chainfee.SatPerKWeight, txSize int64, + startFeeRate, delta chainfee.SatPerKWeight, + txSize lntypes.WeightUnit, deadline, position int32, desc string) { // Bob's HTLC sweeping tx should be fee bumped. @@ -694,7 +1069,7 @@ func testSweepHTLCs(ht *lntest.HarnessTest) { incomingBudget := invoiceAmt.MulF64(contractcourt.DefaultBudgetRatio) incomingTxSize := ht.CalculateTxWeight(incomingSweep) incomingEndFeeRate := chainfee.NewSatPerKWeight( - incomingBudget, uint64(incomingTxSize), + incomingBudget, incomingTxSize, ) // Assert the initial sweeping tx is using the start fee rate. @@ -706,7 +1081,7 @@ func testSweepHTLCs(ht *lntest.HarnessTest) { // Now the start fee rate is checked, we can calculate the fee rate // delta. incomingFeeRateDelta := (incomingEndFeeRate - incomingStartFeeRate) / - chainfee.SatPerKWeight(incomingHTLCDeadline) + chainfee.SatPerKWeight(incomingHTLCDeadline-1) // incomingFuncPosition records the position of Bob's fee function used // for his incoming HTLC sweeping tx. @@ -766,7 +1141,10 @@ func testSweepHTLCs(ht *lntest.HarnessTest) { // We now mine enough blocks till we reach the end of the outgoing // HTLC's deadline. Along the way, we check the expected fee rates are // used for both incoming and outgoing HTLC sweeping txns. - blocksLeft := outgoingHTLCDeadline - outgoingFuncPosition + // + // NOTE: We need to subtract 1 from the deadline as the budget must be + // used up before the deadline. + blocksLeft := outgoingHTLCDeadline - outgoingFuncPosition - 1 for i := int32(0); i < blocksLeft; i++ { // Mine an empty block. ht.MineEmptyBlocks(1) @@ -1038,10 +1416,10 @@ func testSweepCommitOutputAndAnchor(ht *lntest.HarnessTest) { // Calculate the ending fee rate and fee rate delta used in his fee // function. - bobTxWeight := uint64(ht.CalculateTxWeight(bobSweepTx)) + bobTxWeight := ht.CalculateTxWeight(bobSweepTx) bobEndingFeeRate := chainfee.NewSatPerKWeight(bobBudget, bobTxWeight) bobFeeRateDelta := (bobEndingFeeRate - bobStartFeeRate) / - chainfee.SatPerKWeight(deadlineB) + chainfee.SatPerKWeight(deadlineB-1) // Mine an empty block, which should trigger Alice's contractcourt to // offer her commit output to the sweeper. @@ -1173,7 +1551,7 @@ func testSweepCommitOutputAndAnchor(ht *lntest.HarnessTest) { aliceTxWeight := uint64(ht.CalculateTxWeight(aliceSweepTx)) aliceEndingFeeRate := sweep.DefaultMaxFeeRate.FeePerKWeight() aliceFeeRateDelta := (aliceEndingFeeRate - aliceStartingFeeRate) / - chainfee.SatPerKWeight(deadlineA) + chainfee.SatPerKWeight(deadlineA-1) aliceFeeRate := ht.CalculateTxFeeRate(aliceSweepTx) expectedFeeRateAlice := aliceStartingFeeRate + diff --git a/itest/lnd_taproot_test.go b/itest/lnd_taproot_test.go index ed37e04e8..3b5cb4799 100644 --- a/itest/lnd_taproot_test.go +++ b/itest/lnd_taproot_test.go @@ -22,6 +22,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/node" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/stretchr/testify/require" ) @@ -187,7 +188,7 @@ func testTaprootComputeInputScriptKeySpendBip86(ht *lntest.HarnessTest, estimator := input.TxWeightEstimator{} estimator.AddTaprootKeySpendInput(txscript.SigHashDefault) estimator.AddP2WKHOutput() - estimatedWeight := int64(estimator.Weight()) + estimatedWeight := estimator.Weight() requiredFee := feeRate.FeeForWeight(estimatedWeight) tx := wire.NewMsgTx(2) @@ -278,7 +279,7 @@ func testTaprootSignOutputRawScriptSpend(ht *lntest.HarnessTest, ) estimator.AddP2WKHOutput() - estimatedWeight := int64(estimator.Weight()) + estimatedWeight := estimator.Weight() sigHash := txscript.SigHashDefault if len(sigHashType) != 0 { sigHash = sigHashType[0] @@ -431,7 +432,7 @@ func testTaprootSignOutputRawKeySpendBip86(ht *lntest.HarnessTest, estimator := input.TxWeightEstimator{} estimator.AddTaprootKeySpendInput(sigHash) estimator.AddP2WKHOutput() - estimatedWeight := int64(estimator.Weight()) + estimatedWeight := estimator.Weight() requiredFee := feeRate.FeeForWeight(estimatedWeight) tx := wire.NewMsgTx(2) @@ -525,7 +526,7 @@ func testTaprootSignOutputRawKeySpendRootHash(ht *lntest.HarnessTest, estimator := input.TxWeightEstimator{} estimator.AddTaprootKeySpendInput(txscript.SigHashDefault) estimator.AddP2WKHOutput() - estimatedWeight := int64(estimator.Weight()) + estimatedWeight := estimator.Weight() requiredFee := feeRate.FeeForWeight(estimatedWeight) tx := wire.NewMsgTx(2) @@ -610,7 +611,7 @@ func testTaprootMuSig2KeySpendBip86(ht *lntest.HarnessTest, estimator := input.TxWeightEstimator{} estimator.AddTaprootKeySpendInput(txscript.SigHashDefault) estimator.AddP2WKHOutput() - estimatedWeight := int64(estimator.Weight()) + estimatedWeight := estimator.Weight() requiredFee := feeRate.FeeForWeight(estimatedWeight) tx := wire.NewMsgTx(2) @@ -740,7 +741,7 @@ func testTaprootMuSig2KeySpendRootHash(ht *lntest.HarnessTest, estimator := input.TxWeightEstimator{} estimator.AddTaprootKeySpendInput(txscript.SigHashDefault) estimator.AddP2WKHOutput() - estimatedWeight := int64(estimator.Weight()) + estimatedWeight := estimator.Weight() requiredFee := feeRate.FeeForWeight(estimatedWeight) tx := wire.NewMsgTx(2) @@ -874,10 +875,11 @@ func testTaprootMuSig2ScriptSpend(ht *lntest.HarnessTest, feeRate := chainfee.SatPerKWeight(12500) estimator := input.TxWeightEstimator{} estimator.AddTapscriptInput( - len([]byte("foobar"))+len(leaf1.Script)+1, tapscript, + lntypes.WeightUnit(len([]byte("foobar"))+len(leaf1.Script)+1), + tapscript, ) estimator.AddP2WKHOutput() - estimatedWeight := int64(estimator.Weight()) + estimatedWeight := estimator.Weight() requiredFee := feeRate.FeeForWeight(estimatedWeight) tx := wire.NewMsgTx(2) @@ -956,7 +958,7 @@ func testTaprootMuSig2CombinedLeafKeySpend(ht *lntest.HarnessTest, input.TaprootSignatureWitnessSize, tapscript, ) estimator.AddP2WKHOutput() - estimatedWeight := int64(estimator.Weight()) + estimatedWeight := estimator.Weight() requiredFee := feeRate.FeeForWeight(estimatedWeight) tx := wire.NewMsgTx(2) @@ -1525,7 +1527,7 @@ func sendToTaprootOutput(ht *lntest.HarnessTest, hn *node.HarnessNode, // spend request, the given sweep address' balance is verified to be seen as // funds belonging to the wallet. func publishTxAndConfirmSweep(ht *lntest.HarnessTest, node *node.HarnessNode, - tx *wire.MsgTx, estimatedWeight int64, + tx *wire.MsgTx, estimatedWeight lntypes.WeightUnit, spendRequest *chainrpc.SpendRequest, sweepAddr string) { ht.Helper() @@ -1564,7 +1566,7 @@ func publishTxAndConfirmSweep(ht *lntest.HarnessTest, node *node.HarnessNode, // Since Schnorr signatures are fixed size, we must be able to estimate // the size of this transaction exactly. txWeight := blockchain.GetTransactionWeight(btcutil.NewTx(tx)) - require.Equal(ht, estimatedWeight, txWeight) + require.EqualValues(ht, estimatedWeight, txWeight) txReq := &walletrpc.Transaction{ TxHex: buf.Bytes(), diff --git a/kvdb/backend.go b/kvdb/backend.go index f034cee7e..e42c02233 100644 --- a/kvdb/backend.go +++ b/kvdb/backend.go @@ -9,7 +9,6 @@ import ( "encoding/binary" "encoding/hex" "fmt" - "io/ioutil" "os" "path/filepath" "time" @@ -218,7 +217,7 @@ func lastCompactionDate(dbFile string) (time.Time, error) { return zeroTime, nil } - tsBytes, err := ioutil.ReadFile(tsFile) + tsBytes, err := os.ReadFile(tsFile) if err != nil { return zeroTime, err } @@ -235,7 +234,7 @@ func updateLastCompactionDate(dbFile string) error { byteOrder.PutUint64(tsBytes[:], uint64(time.Now().UnixNano())) tsFile := fmt.Sprintf("%s%s", dbFile, LastCompactionFileNameSuffix) - return ioutil.WriteFile(tsFile, tsBytes[:], 0600) + return os.WriteFile(tsFile, tsBytes[:], 0600) } // GetTestBackend opens (or creates if doesn't exist) a bbolt or etcd diff --git a/kvdb/go.mod b/kvdb/go.mod index c09adfc47..df0b62a13 100644 --- a/kvdb/go.mod +++ b/kvdb/go.mod @@ -8,7 +8,7 @@ require ( github.com/google/btree v1.0.1 github.com/jackc/pgx/v4 v4.18.1 github.com/lightningnetwork/lnd/healthcheck v1.2.4 - github.com/lightningnetwork/lnd/sqldb v1.0.1 + github.com/lightningnetwork/lnd/sqldb v1.0.2 github.com/stretchr/testify v1.9.0 go.etcd.io/bbolt v1.3.7 go.etcd.io/etcd/api/v3 v3.5.7 @@ -16,7 +16,7 @@ require ( go.etcd.io/etcd/client/v3 v3.5.7 go.etcd.io/etcd/server/v3 v3.5.7 golang.org/x/net v0.22.0 - modernc.org/sqlite v1.29.5 + modernc.org/sqlite v1.29.8 ) require ( @@ -41,7 +41,7 @@ require ( github.com/golang-migrate/migrate/v4 v4.17.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.4.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect @@ -63,7 +63,7 @@ require ( github.com/lib/pq v1.10.9 // indirect github.com/lightningnetwork/lnd/ticker v1.1.0 // indirect github.com/lightningnetwork/lnd/tor v1.0.0 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/miekg/dns v1.1.43 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect @@ -107,7 +107,7 @@ require ( golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect golang.org/x/mod v0.16.0 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.19.0 // indirect @@ -120,9 +120,9 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect - modernc.org/libc v1.41.0 // indirect + modernc.org/libc v1.49.3 // indirect modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.7.2 // indirect + modernc.org/memory v1.8.0 // indirect modernc.org/strutil v1.2.0 // indirect modernc.org/token v1.1.0 // indirect sigs.k8s.io/yaml v1.2.0 // indirect diff --git a/kvdb/go.sum b/kvdb/go.sum index a41bc8234..c7fda8448 100644 --- a/kvdb/go.sum +++ b/kvdb/go.sum @@ -180,14 +180,14 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= @@ -294,8 +294,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightningnetwork/lnd/healthcheck v1.2.4 h1:lLPLac+p/TllByxGSlkCwkJlkddqMP5UCoawCj3mgFQ= github.com/lightningnetwork/lnd/healthcheck v1.2.4/go.mod h1:G7Tst2tVvWo7cx6mSBEToQC5L1XOGxzZTPB29g9Rv2I= -github.com/lightningnetwork/lnd/sqldb v1.0.1 h1:lpNoJ6qRh3D02oeIUsKQLZUzjcgZ9ppMZNZnwrpBBmY= -github.com/lightningnetwork/lnd/sqldb v1.0.1/go.mod h1:nSovU1U+gTPDWhfwmXu/kW8l8EJpwbvZQ05ijnkQzkA= +github.com/lightningnetwork/lnd/sqldb v1.0.2 h1:PfuYzScYMD9/QonKo/QvgsbXfTnH5DfldIimkfdW4Bk= +github.com/lightningnetwork/lnd/sqldb v1.0.2/go.mod h1:V2Xl6JNWLTKE97WJnwfs0d0TYJdIQTqK8/3aAwkd3qI= github.com/lightningnetwork/lnd/ticker v1.1.0 h1:ShoBiRP3pIxZHaETndfQ5kEe+S4NdAY1hiX7YbZ4QE4= github.com/lightningnetwork/lnd/ticker v1.1.0/go.mod h1:ubqbSVCn6RlE0LazXuBr7/Zi6QT0uQo++OgIRBxQUrk= github.com/lightningnetwork/lnd/tor v1.0.0 h1:wvEc7I+Y7IOtPglVP3cVBbYhiVhc7uTd7cMF9gQRzwA= @@ -305,8 +305,8 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -604,10 +604,10 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -717,18 +717,28 @@ gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk= +modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA= +modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= +modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= -modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk= -modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY= +modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg= +modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= -modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= -modernc.org/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE= -modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.29.8 h1:nGKglNx9K5v0As+zF0/Gcl1kMkmaU1XynYyq92PbsC8= +modernc.org/sqlite v1.29.8/go.mod h1:lQPm27iqa4UNZpmr4Aor0MH0HkCLbt1huYDfWylLZFk= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/lncfg/fee.go b/lncfg/fee.go new file mode 100644 index 000000000..200ae9e81 --- /dev/null +++ b/lncfg/fee.go @@ -0,0 +1,20 @@ +package lncfg + +import "time" + +// DefaultMinUpdateTimeout represents the minimum interval in which a +// WebAPIEstimator will request fresh fees from its API. +const DefaultMinUpdateTimeout = 5 * time.Minute + +// DefaultMaxUpdateTimeout represents the maximum interval in which a +// WebAPIEstimator will request fresh fees from its API. +const DefaultMaxUpdateTimeout = 20 * time.Minute + +// Fee holds the configuration options for fee estimation. +// +//nolint:lll +type Fee struct { + URL string `long:"url" description:"Optional URL for external fee estimation. If no URL is specified, the method for fee estimation will depend on the chosen backend and network. Must be set for neutrino on mainnet."` + MinUpdateTimeout time.Duration `long:"min-update-timeout" description:"The minimum interval in which fees will be updated from the specified fee URL."` + MaxUpdateTimeout time.Duration `long:"max-update-timeout" description:"The maximum interval in which fees will be updated from the specified fee URL."` +} diff --git a/lncfg/neutrino.go b/lncfg/neutrino.go index 09a66312a..e46c292dc 100644 --- a/lncfg/neutrino.go +++ b/lncfg/neutrino.go @@ -12,7 +12,6 @@ type Neutrino struct { MaxPeers int `long:"maxpeers" description:"Max number of inbound and outbound peers"` BanDuration time.Duration `long:"banduration" description:"How long to ban misbehaving peers. Valid time units are {s, m, h}. Minimum 1 second"` BanThreshold uint32 `long:"banthreshold" description:"Maximum allowed ban score before disconnecting and banning misbehaving peers."` - FeeURL string `long:"feeurl" description:"DEPRECATED: Use top level 'feeurl' option. Optional URL for fee estimation. If a URL is not specified, static fees will be used for estimation." hidden:"true"` AssertFilterHeader string `long:"assertfilterheader" description:"Optional filter header in height:hash format to assert the state of neutrino's filter header chain on startup. If the assertion does not hold, then the filter header chain will be re-synced from the genesis block."` UserAgentName string `long:"useragentname" description:"Used to help identify ourselves to other bitcoin peers"` UserAgentVersion string `long:"useragentversion" description:"Used to help identify ourselves to other bitcoin peers"` diff --git a/lncfg/protocol.go b/lncfg/protocol.go index 59027a09b..d86613188 100644 --- a/lncfg/protocol.go +++ b/lncfg/protocol.go @@ -2,6 +2,11 @@ package lncfg +import ( + "github.com/lightningnetwork/lnd/feature" + "github.com/lightningnetwork/lnd/lnwire" +) + // ProtocolOptions is a struct that we use to be able to test backwards // compatibility of protocol additions, while defaulting to the latest within // lnd, or to enable experimental protocol changes. @@ -57,14 +62,23 @@ type ProtocolOptions struct { // NoRouteBlindingOption disables forwarding of payments in blinded routes. NoRouteBlindingOption bool `long:"no-route-blinding" description:"do not forward payments that are a part of a blinded route"` -} -// DefaultProtocol returns a protocol config with route blinding turned off, -// temporarily in place until full handling of blinded route errors is merged. -func DefaultProtocol() *ProtocolOptions { - return &ProtocolOptions{ - NoRouteBlindingOption: true, - } + // CustomMessage allows the custom message APIs to handle messages with + // the provided protocol numbers, which fall outside the custom message + // number range. + CustomMessage []uint16 `long:"custom-message" description:"allows the custom message apis to send and report messages with the protocol number provided that fall outside of the custom message number range."` + + // CustomInit specifies feature bits to advertise in the node's init + // message. + CustomInit []uint16 `long:"custom-init" description:"custom feature bits — numbers defined in BOLT 9 — to advertise in the node's init message"` + + // CustomNodeAnn specifies custom feature bits to advertise in the + // node's announcement message. + CustomNodeAnn []uint16 `long:"custom-nodeann" description:"custom feature bits — numbers defined in BOLT 9 — to advertise in the node's announcement message"` + + // CustomInvoice specifies custom feature bits to advertise in the + // node's invoices. + CustomInvoice []uint16 `long:"custom-invoice" description:"custom feature bits — numbers defined in BOLT 9 — to advertise in the node's invoices"` } // Wumbo returns true if lnd should permit the creation and acceptance of wumbo @@ -113,3 +127,29 @@ func (l *ProtocolOptions) NoTimestampsQuery() bool { func (l *ProtocolOptions) NoRouteBlinding() bool { return l.NoRouteBlindingOption } + +// CustomMessageOverrides returns the set of protocol messages that we override +// to allow custom handling. +func (p ProtocolOptions) CustomMessageOverrides() []uint16 { + return p.CustomMessage +} + +// CustomFeatures returns a custom set of feature bits to advertise. +func (p ProtocolOptions) CustomFeatures() map[feature.Set][]lnwire.FeatureBit { + customFeatures := make(map[feature.Set][]lnwire.FeatureBit) + + setFeatures := func(set feature.Set, bits []uint16) { + for _, customFeature := range bits { + customFeatures[set] = append( + customFeatures[set], + lnwire.FeatureBit(customFeature), + ) + } + } + + setFeatures(feature.SetInit, p.CustomInit) + setFeatures(feature.SetNodeAnn, p.CustomNodeAnn) + setFeatures(feature.SetInvoice, p.CustomInvoice) + + return customFeatures +} diff --git a/lncfg/protocol_experimental_off.go b/lncfg/protocol_experimental_off.go index 51c90ec38..34143a224 100644 --- a/lncfg/protocol_experimental_off.go +++ b/lncfg/protocol_experimental_off.go @@ -3,23 +3,7 @@ package lncfg -import ( - "github.com/lightningnetwork/lnd/feature" - "github.com/lightningnetwork/lnd/lnwire" -) - // ExperimentalProtocol is a sub-config that houses any experimental protocol // features that also require a build-tag to activate. type ExperimentalProtocol struct { } - -// CustomMessageOverrides returns the set of protocol messages that we override -// to allow custom handling. -func (p ExperimentalProtocol) CustomMessageOverrides() []uint16 { - return nil -} - -// CustomFeatures returns a custom set of feature bits to advertise. -func (p ExperimentalProtocol) CustomFeatures() map[feature.Set][]lnwire.FeatureBit { - return map[feature.Set][]lnwire.FeatureBit{} -} diff --git a/lncfg/protocol_experimental_on.go b/lncfg/protocol_experimental_on.go index bb23e83c2..b7d74acfe 100644 --- a/lncfg/protocol_experimental_on.go +++ b/lncfg/protocol_experimental_on.go @@ -3,49 +3,7 @@ package lncfg -import ( - "github.com/lightningnetwork/lnd/feature" - "github.com/lightningnetwork/lnd/lnwire" -) - // ExperimentalProtocol is a sub-config that houses any experimental protocol // features that also require a build-tag to activate. -// -//nolint:lll type ExperimentalProtocol struct { - CustomMessage []uint16 `long:"custom-message" description:"allows the custom message apis to send and report messages with the protocol number provided that fall outside of the custom message number range."` - - CustomInit []uint16 `long:"custom-init" description:"custom feature bits to advertise in the node's init message"` - - CustomNodeAnn []uint16 `long:"custom-nodeann" description:"custom feature bits to advertise in the node's announcement message"` - - CustomInvoice []uint16 `long:"custom-invoice" description:"custom feature bits to advertise in the node's invoices"` -} - -// CustomMessageOverrides returns the set of protocol messages that we override -// to allow custom handling. -func (p ExperimentalProtocol) CustomMessageOverrides() []uint16 { - return p.CustomMessage -} - -// CustomFeatures returns a custom set of feature bits to advertise. -// -//nolint:lll -func (p ExperimentalProtocol) CustomFeatures() map[feature.Set][]lnwire.FeatureBit { - customFeatures := make(map[feature.Set][]lnwire.FeatureBit) - - setFeatures := func(set feature.Set, bits []uint16) { - for _, customFeature := range bits { - customFeatures[set] = append( - customFeatures[set], - lnwire.FeatureBit(customFeature), - ) - } - } - - setFeatures(feature.SetInit, p.CustomInit) - setFeatures(feature.SetNodeAnn, p.CustomNodeAnn) - setFeatures(feature.SetInvoice, p.CustomInvoice) - - return customFeatures } diff --git a/lncfg/protocol_integration.go b/lncfg/protocol_integration.go index f44aa1246..e568b6b9a 100644 --- a/lncfg/protocol_integration.go +++ b/lncfg/protocol_integration.go @@ -2,6 +2,11 @@ package lncfg +import ( + "github.com/lightningnetwork/lnd/feature" + "github.com/lightningnetwork/lnd/lnwire" +) + // ProtocolOptions is a struct that we use to be able to test backwards // compatibility of protocol additions, while defaulting to the latest within // lnd, or to enable experimental protocol changes. @@ -60,13 +65,23 @@ type ProtocolOptions struct { // NoRouteBlindingOption disables forwarding of payments in blinded routes. NoRouteBlindingOption bool `long:"no-route-blinding" description:"do not forward payments that are a part of a blinded route"` -} -// DefaultProtocol returns a protocol config with route blinding turned on, -// so that itests can run against route blinding features even while we've -// got it turned off for the daemon (pending completion of error handling). -func DefaultProtocol() *ProtocolOptions { - return &ProtocolOptions{} + // CustomMessage allows the custom message APIs to handle messages with + // the provided protocol numbers, which fall outside the custom message + // number range. + CustomMessage []uint16 `long:"custom-message" description:"allows the custom message apis to send and report messages with the protocol number provided that fall outside of the custom message number range."` + + // CustomInit specifies feature bits to advertise in the node's init + // message. + CustomInit []uint16 `long:"custom-init" description:"custom feature bits to advertise in the node's init message"` + + // CustomNodeAnn specifies custom feature bits to advertise in the + // node's announcement message. + CustomNodeAnn []uint16 `long:"custom-nodeann" description:"custom feature bits to advertise in the node's announcement message"` + + // CustomInvoice specifies custom feature bits to advertise in the + // node's invoices. + CustomInvoice []uint16 `long:"custom-invoice" description:"custom feature bits to advertise in the node's invoices"` } // Wumbo returns true if lnd should permit the creation and acceptance of wumbo @@ -107,3 +122,29 @@ func (l *ProtocolOptions) NoAnySegwit() bool { func (l *ProtocolOptions) NoRouteBlinding() bool { return l.NoRouteBlindingOption } + +// CustomMessageOverrides returns the set of protocol messages that we override +// to allow custom handling. +func (l ProtocolOptions) CustomMessageOverrides() []uint16 { + return l.CustomMessage +} + +// CustomFeatures returns a custom set of feature bits to advertise. +func (l ProtocolOptions) CustomFeatures() map[feature.Set][]lnwire.FeatureBit { + customFeatures := make(map[feature.Set][]lnwire.FeatureBit) + + setFeatures := func(set feature.Set, bits []uint16) { + for _, customFeature := range bits { + customFeatures[set] = append( + customFeatures[set], + lnwire.FeatureBit(customFeature), + ) + } + } + + setFeatures(feature.SetInit, l.CustomInit) + setFeatures(feature.SetNodeAnn, l.CustomNodeAnn) + setFeatures(feature.SetInvoice, l.CustomInvoice) + + return customFeatures +} diff --git a/lnd.go b/lnd.go index 56db5bf82..5ae67d357 100644 --- a/lnd.go +++ b/lnd.go @@ -8,7 +8,6 @@ import ( "context" "errors" "fmt" - "io/ioutil" "net" "net/http" "net/http/pprof" @@ -83,7 +82,7 @@ func AdminAuthOptions(cfg *Config, skipMacaroons bool) ([]grpc.DialOption, // Get the admin macaroon if macaroons are active. if !skipMacaroons && !cfg.NoMacaroons { // Load the admin macaroon file. - macBytes, err := ioutil.ReadFile(cfg.AdminMacPath) + macBytes, err := os.ReadFile(cfg.AdminMacPath) if err != nil { return nil, fmt.Errorf("unable to read macaroon "+ "path (check the network setting!): %v", err) diff --git a/lnrpc/chainrpc/chain_server.go b/lnrpc/chainrpc/chain_server.go index 00a30c07c..1defadafc 100644 --- a/lnrpc/chainrpc/chain_server.go +++ b/lnrpc/chainrpc/chain_server.go @@ -7,7 +7,6 @@ import ( "bytes" "context" "errors" - "io/ioutil" "os" "path/filepath" "sync" @@ -153,7 +152,7 @@ func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) { if err != nil { return nil, nil, err } - err = ioutil.WriteFile(macFilePath, chainNotifierMacBytes, 0644) + err = os.WriteFile(macFilePath, chainNotifierMacBytes, 0644) if err != nil { _ = os.Remove(macFilePath) return nil, nil, err diff --git a/lnrpc/invoicesrpc/invoices_server.go b/lnrpc/invoicesrpc/invoices_server.go index 91e1c66d3..d48b3e2a8 100644 --- a/lnrpc/invoicesrpc/invoices_server.go +++ b/lnrpc/invoicesrpc/invoices_server.go @@ -7,7 +7,6 @@ import ( "context" "errors" "fmt" - "io/ioutil" "os" "path/filepath" "sync/atomic" @@ -150,7 +149,7 @@ func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) { if err != nil { return nil, nil, err } - err = ioutil.WriteFile(macFilePath, invoicesMacBytes, 0644) + err = os.WriteFile(macFilePath, invoicesMacBytes, 0644) if err != nil { _ = os.Remove(macFilePath) return nil, nil, err diff --git a/lnrpc/lightning.pb.go b/lnrpc/lightning.pb.go index 64658c710..29ac5dfa8 100644 --- a/lnrpc/lightning.pb.go +++ b/lnrpc/lightning.pb.go @@ -651,6 +651,8 @@ const ( FeatureBit_ANCHORS_OPT FeatureBit = 21 FeatureBit_ANCHORS_ZERO_FEE_HTLC_REQ FeatureBit = 22 FeatureBit_ANCHORS_ZERO_FEE_HTLC_OPT FeatureBit = 23 + FeatureBit_ROUTE_BLINDING_REQUIRED FeatureBit = 24 + FeatureBit_ROUTE_BLINDING_OPTIONAL FeatureBit = 25 FeatureBit_AMP_REQ FeatureBit = 30 FeatureBit_AMP_OPT FeatureBit = 31 ) @@ -681,6 +683,8 @@ var ( 21: "ANCHORS_OPT", 22: "ANCHORS_ZERO_FEE_HTLC_REQ", 23: "ANCHORS_ZERO_FEE_HTLC_OPT", + 24: "ROUTE_BLINDING_REQUIRED", + 25: "ROUTE_BLINDING_OPTIONAL", 30: "AMP_REQ", 31: "AMP_OPT", } @@ -708,6 +712,8 @@ var ( "ANCHORS_OPT": 21, "ANCHORS_ZERO_FEE_HTLC_REQ": 22, "ANCHORS_ZERO_FEE_HTLC_OPT": 23, + "ROUTE_BLINDING_REQUIRED": 24, + "ROUTE_BLINDING_OPTIONAL": 25, "AMP_REQ": 30, "AMP_OPT": 31, } @@ -1362,7 +1368,7 @@ func (x Failure_FailureCode) Number() protoreflect.EnumNumber { // Deprecated: Use Failure_FailureCode.Descriptor instead. func (Failure_FailureCode) EnumDescriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{186, 0} + return file_lightning_proto_rawDescGZIP(), []int{187, 0} } type LookupHtlcResolutionRequest struct { @@ -13924,6 +13930,9 @@ type DeleteAllPaymentsRequest struct { FailedPaymentsOnly bool `protobuf:"varint,1,opt,name=failed_payments_only,json=failedPaymentsOnly,proto3" json:"failed_payments_only,omitempty"` // Only delete failed HTLCs from payments, not the payment itself. FailedHtlcsOnly bool `protobuf:"varint,2,opt,name=failed_htlcs_only,json=failedHtlcsOnly,proto3" json:"failed_htlcs_only,omitempty"` + // Delete all payments. NOTE: Using this option requires careful + // consideration as it is a destructive operation. + AllPayments bool `protobuf:"varint,3,opt,name=all_payments,json=allPayments,proto3" json:"all_payments,omitempty"` } func (x *DeleteAllPaymentsRequest) Reset() { @@ -13972,6 +13981,13 @@ func (x *DeleteAllPaymentsRequest) GetFailedHtlcsOnly() bool { return false } +func (x *DeleteAllPaymentsRequest) GetAllPayments() bool { + if x != nil { + return x.AllPayments + } + return false +} + type DeletePaymentResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -14730,6 +14746,65 @@ func (x *FeeReportResponse) GetMonthFeeSum() uint64 { return 0 } +type InboundFee struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The inbound base fee charged regardless of the number of milli-satoshis + // received in the channel. By default, only negative values are accepted. + BaseFeeMsat int32 `protobuf:"varint,1,opt,name=base_fee_msat,json=baseFeeMsat,proto3" json:"base_fee_msat,omitempty"` + // The effective inbound fee rate in micro-satoshis (parts per million). + // By default, only negative values are accepted. + FeeRatePpm int32 `protobuf:"varint,2,opt,name=fee_rate_ppm,json=feeRatePpm,proto3" json:"fee_rate_ppm,omitempty"` +} + +func (x *InboundFee) Reset() { + *x = InboundFee{} + if protoimpl.UnsafeEnabled { + mi := &file_lightning_proto_msgTypes[160] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InboundFee) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InboundFee) ProtoMessage() {} + +func (x *InboundFee) ProtoReflect() protoreflect.Message { + mi := &file_lightning_proto_msgTypes[160] + 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 InboundFee.ProtoReflect.Descriptor instead. +func (*InboundFee) Descriptor() ([]byte, []int) { + return file_lightning_proto_rawDescGZIP(), []int{160} +} + +func (x *InboundFee) GetBaseFeeMsat() int32 { + if x != nil { + return x.BaseFeeMsat + } + return 0 +} + +func (x *InboundFee) GetFeeRatePpm() int32 { + if x != nil { + return x.FeeRatePpm + } + return 0 +} + type PolicyUpdateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -14756,15 +14831,16 @@ type PolicyUpdateRequest struct { // min_htlc_msat_specified is true. MinHtlcMsat uint64 `protobuf:"varint,7,opt,name=min_htlc_msat,json=minHtlcMsat,proto3" json:"min_htlc_msat,omitempty"` // If true, min_htlc_msat is applied. - MinHtlcMsatSpecified bool `protobuf:"varint,8,opt,name=min_htlc_msat_specified,json=minHtlcMsatSpecified,proto3" json:"min_htlc_msat_specified,omitempty"` - InboundBaseFeeMsat int32 `protobuf:"varint,10,opt,name=inbound_base_fee_msat,json=inboundBaseFeeMsat,proto3" json:"inbound_base_fee_msat,omitempty"` - InboundFeeRatePpm int32 `protobuf:"varint,11,opt,name=inbound_fee_rate_ppm,json=inboundFeeRatePpm,proto3" json:"inbound_fee_rate_ppm,omitempty"` + MinHtlcMsatSpecified bool `protobuf:"varint,8,opt,name=min_htlc_msat_specified,json=minHtlcMsatSpecified,proto3" json:"min_htlc_msat_specified,omitempty"` + // Optional inbound fee. If unset, the previously set value will be + // retained [EXPERIMENTAL]. + InboundFee *InboundFee `protobuf:"bytes,10,opt,name=inbound_fee,json=inboundFee,proto3" json:"inbound_fee,omitempty"` } func (x *PolicyUpdateRequest) Reset() { *x = PolicyUpdateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[160] + mi := &file_lightning_proto_msgTypes[161] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14777,7 +14853,7 @@ func (x *PolicyUpdateRequest) String() string { func (*PolicyUpdateRequest) ProtoMessage() {} func (x *PolicyUpdateRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[160] + mi := &file_lightning_proto_msgTypes[161] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14790,7 +14866,7 @@ func (x *PolicyUpdateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyUpdateRequest.ProtoReflect.Descriptor instead. func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{160} + return file_lightning_proto_rawDescGZIP(), []int{161} } func (m *PolicyUpdateRequest) GetScope() isPolicyUpdateRequest_Scope { @@ -14863,18 +14939,11 @@ func (x *PolicyUpdateRequest) GetMinHtlcMsatSpecified() bool { return false } -func (x *PolicyUpdateRequest) GetInboundBaseFeeMsat() int32 { +func (x *PolicyUpdateRequest) GetInboundFee() *InboundFee { if x != nil { - return x.InboundBaseFeeMsat + return x.InboundFee } - return 0 -} - -func (x *PolicyUpdateRequest) GetInboundFeeRatePpm() int32 { - if x != nil { - return x.InboundFeeRatePpm - } - return 0 + return nil } type isPolicyUpdateRequest_Scope interface { @@ -14911,7 +14980,7 @@ type FailedUpdate struct { func (x *FailedUpdate) Reset() { *x = FailedUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[161] + mi := &file_lightning_proto_msgTypes[162] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14924,7 +14993,7 @@ func (x *FailedUpdate) String() string { func (*FailedUpdate) ProtoMessage() {} func (x *FailedUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[161] + mi := &file_lightning_proto_msgTypes[162] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14937,7 +15006,7 @@ func (x *FailedUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use FailedUpdate.ProtoReflect.Descriptor instead. func (*FailedUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{161} + return file_lightning_proto_rawDescGZIP(), []int{162} } func (x *FailedUpdate) GetOutpoint() *OutPoint { @@ -14973,7 +15042,7 @@ type PolicyUpdateResponse struct { func (x *PolicyUpdateResponse) Reset() { *x = PolicyUpdateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[162] + mi := &file_lightning_proto_msgTypes[163] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14986,7 +15055,7 @@ func (x *PolicyUpdateResponse) String() string { func (*PolicyUpdateResponse) ProtoMessage() {} func (x *PolicyUpdateResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[162] + mi := &file_lightning_proto_msgTypes[163] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14999,7 +15068,7 @@ func (x *PolicyUpdateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyUpdateResponse.ProtoReflect.Descriptor instead. func (*PolicyUpdateResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{162} + return file_lightning_proto_rawDescGZIP(), []int{163} } func (x *PolicyUpdateResponse) GetFailedUpdates() []*FailedUpdate { @@ -15036,7 +15105,7 @@ type ForwardingHistoryRequest struct { func (x *ForwardingHistoryRequest) Reset() { *x = ForwardingHistoryRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[163] + mi := &file_lightning_proto_msgTypes[164] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15049,7 +15118,7 @@ func (x *ForwardingHistoryRequest) String() string { func (*ForwardingHistoryRequest) ProtoMessage() {} func (x *ForwardingHistoryRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[163] + mi := &file_lightning_proto_msgTypes[164] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15062,7 +15131,7 @@ func (x *ForwardingHistoryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ForwardingHistoryRequest.ProtoReflect.Descriptor instead. func (*ForwardingHistoryRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{163} + return file_lightning_proto_rawDescGZIP(), []int{164} } func (x *ForwardingHistoryRequest) GetStartTime() uint64 { @@ -15143,7 +15212,7 @@ type ForwardingEvent struct { func (x *ForwardingEvent) Reset() { *x = ForwardingEvent{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[164] + mi := &file_lightning_proto_msgTypes[165] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15156,7 +15225,7 @@ func (x *ForwardingEvent) String() string { func (*ForwardingEvent) ProtoMessage() {} func (x *ForwardingEvent) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[164] + mi := &file_lightning_proto_msgTypes[165] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15169,7 +15238,7 @@ func (x *ForwardingEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use ForwardingEvent.ProtoReflect.Descriptor instead. func (*ForwardingEvent) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{164} + return file_lightning_proto_rawDescGZIP(), []int{165} } // Deprecated: Marked as deprecated in lightning.proto. @@ -15273,7 +15342,7 @@ type ForwardingHistoryResponse struct { func (x *ForwardingHistoryResponse) Reset() { *x = ForwardingHistoryResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[165] + mi := &file_lightning_proto_msgTypes[166] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15286,7 +15355,7 @@ func (x *ForwardingHistoryResponse) String() string { func (*ForwardingHistoryResponse) ProtoMessage() {} func (x *ForwardingHistoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[165] + mi := &file_lightning_proto_msgTypes[166] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15299,7 +15368,7 @@ func (x *ForwardingHistoryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ForwardingHistoryResponse.ProtoReflect.Descriptor instead. func (*ForwardingHistoryResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{165} + return file_lightning_proto_rawDescGZIP(), []int{166} } func (x *ForwardingHistoryResponse) GetForwardingEvents() []*ForwardingEvent { @@ -15328,7 +15397,7 @@ type ExportChannelBackupRequest struct { func (x *ExportChannelBackupRequest) Reset() { *x = ExportChannelBackupRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[166] + mi := &file_lightning_proto_msgTypes[167] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15341,7 +15410,7 @@ func (x *ExportChannelBackupRequest) String() string { func (*ExportChannelBackupRequest) ProtoMessage() {} func (x *ExportChannelBackupRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[166] + mi := &file_lightning_proto_msgTypes[167] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15354,7 +15423,7 @@ func (x *ExportChannelBackupRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ExportChannelBackupRequest.ProtoReflect.Descriptor instead. func (*ExportChannelBackupRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{166} + return file_lightning_proto_rawDescGZIP(), []int{167} } func (x *ExportChannelBackupRequest) GetChanPoint() *ChannelPoint { @@ -15381,7 +15450,7 @@ type ChannelBackup struct { func (x *ChannelBackup) Reset() { *x = ChannelBackup{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[167] + mi := &file_lightning_proto_msgTypes[168] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15394,7 +15463,7 @@ func (x *ChannelBackup) String() string { func (*ChannelBackup) ProtoMessage() {} func (x *ChannelBackup) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[167] + mi := &file_lightning_proto_msgTypes[168] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15407,7 +15476,7 @@ func (x *ChannelBackup) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelBackup.ProtoReflect.Descriptor instead. func (*ChannelBackup) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{167} + return file_lightning_proto_rawDescGZIP(), []int{168} } func (x *ChannelBackup) GetChanPoint() *ChannelPoint { @@ -15441,7 +15510,7 @@ type MultiChanBackup struct { func (x *MultiChanBackup) Reset() { *x = MultiChanBackup{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[168] + mi := &file_lightning_proto_msgTypes[169] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15454,7 +15523,7 @@ func (x *MultiChanBackup) String() string { func (*MultiChanBackup) ProtoMessage() {} func (x *MultiChanBackup) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[168] + mi := &file_lightning_proto_msgTypes[169] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15467,7 +15536,7 @@ func (x *MultiChanBackup) ProtoReflect() protoreflect.Message { // Deprecated: Use MultiChanBackup.ProtoReflect.Descriptor instead. func (*MultiChanBackup) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{168} + return file_lightning_proto_rawDescGZIP(), []int{169} } func (x *MultiChanBackup) GetChanPoints() []*ChannelPoint { @@ -15493,7 +15562,7 @@ type ChanBackupExportRequest struct { func (x *ChanBackupExportRequest) Reset() { *x = ChanBackupExportRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[169] + mi := &file_lightning_proto_msgTypes[170] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15506,7 +15575,7 @@ func (x *ChanBackupExportRequest) String() string { func (*ChanBackupExportRequest) ProtoMessage() {} func (x *ChanBackupExportRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[169] + mi := &file_lightning_proto_msgTypes[170] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15519,7 +15588,7 @@ func (x *ChanBackupExportRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ChanBackupExportRequest.ProtoReflect.Descriptor instead. func (*ChanBackupExportRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{169} + return file_lightning_proto_rawDescGZIP(), []int{170} } type ChanBackupSnapshot struct { @@ -15538,7 +15607,7 @@ type ChanBackupSnapshot struct { func (x *ChanBackupSnapshot) Reset() { *x = ChanBackupSnapshot{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[170] + mi := &file_lightning_proto_msgTypes[171] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15551,7 +15620,7 @@ func (x *ChanBackupSnapshot) String() string { func (*ChanBackupSnapshot) ProtoMessage() {} func (x *ChanBackupSnapshot) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[170] + mi := &file_lightning_proto_msgTypes[171] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15564,7 +15633,7 @@ func (x *ChanBackupSnapshot) ProtoReflect() protoreflect.Message { // Deprecated: Use ChanBackupSnapshot.ProtoReflect.Descriptor instead. func (*ChanBackupSnapshot) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{170} + return file_lightning_proto_rawDescGZIP(), []int{171} } func (x *ChanBackupSnapshot) GetSingleChanBackups() *ChannelBackups { @@ -15593,7 +15662,7 @@ type ChannelBackups struct { func (x *ChannelBackups) Reset() { *x = ChannelBackups{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[171] + mi := &file_lightning_proto_msgTypes[172] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15606,7 +15675,7 @@ func (x *ChannelBackups) String() string { func (*ChannelBackups) ProtoMessage() {} func (x *ChannelBackups) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[171] + mi := &file_lightning_proto_msgTypes[172] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15619,7 +15688,7 @@ func (x *ChannelBackups) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelBackups.ProtoReflect.Descriptor instead. func (*ChannelBackups) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{171} + return file_lightning_proto_rawDescGZIP(), []int{172} } func (x *ChannelBackups) GetChanBackups() []*ChannelBackup { @@ -15644,7 +15713,7 @@ type RestoreChanBackupRequest struct { func (x *RestoreChanBackupRequest) Reset() { *x = RestoreChanBackupRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[172] + mi := &file_lightning_proto_msgTypes[173] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15657,7 +15726,7 @@ func (x *RestoreChanBackupRequest) String() string { func (*RestoreChanBackupRequest) ProtoMessage() {} func (x *RestoreChanBackupRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[172] + mi := &file_lightning_proto_msgTypes[173] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15670,7 +15739,7 @@ func (x *RestoreChanBackupRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreChanBackupRequest.ProtoReflect.Descriptor instead. func (*RestoreChanBackupRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{172} + return file_lightning_proto_rawDescGZIP(), []int{173} } func (m *RestoreChanBackupRequest) GetBackup() isRestoreChanBackupRequest_Backup { @@ -15722,7 +15791,7 @@ type RestoreBackupResponse struct { func (x *RestoreBackupResponse) Reset() { *x = RestoreBackupResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[173] + mi := &file_lightning_proto_msgTypes[174] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15735,7 +15804,7 @@ func (x *RestoreBackupResponse) String() string { func (*RestoreBackupResponse) ProtoMessage() {} func (x *RestoreBackupResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[173] + mi := &file_lightning_proto_msgTypes[174] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15748,7 +15817,7 @@ func (x *RestoreBackupResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreBackupResponse.ProtoReflect.Descriptor instead. func (*RestoreBackupResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{173} + return file_lightning_proto_rawDescGZIP(), []int{174} } type ChannelBackupSubscription struct { @@ -15760,7 +15829,7 @@ type ChannelBackupSubscription struct { func (x *ChannelBackupSubscription) Reset() { *x = ChannelBackupSubscription{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[174] + mi := &file_lightning_proto_msgTypes[175] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15773,7 +15842,7 @@ func (x *ChannelBackupSubscription) String() string { func (*ChannelBackupSubscription) ProtoMessage() {} func (x *ChannelBackupSubscription) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[174] + mi := &file_lightning_proto_msgTypes[175] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15786,7 +15855,7 @@ func (x *ChannelBackupSubscription) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelBackupSubscription.ProtoReflect.Descriptor instead. func (*ChannelBackupSubscription) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{174} + return file_lightning_proto_rawDescGZIP(), []int{175} } type VerifyChanBackupResponse struct { @@ -15798,7 +15867,7 @@ type VerifyChanBackupResponse struct { func (x *VerifyChanBackupResponse) Reset() { *x = VerifyChanBackupResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[175] + mi := &file_lightning_proto_msgTypes[176] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15811,7 +15880,7 @@ func (x *VerifyChanBackupResponse) String() string { func (*VerifyChanBackupResponse) ProtoMessage() {} func (x *VerifyChanBackupResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[175] + mi := &file_lightning_proto_msgTypes[176] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15824,7 +15893,7 @@ func (x *VerifyChanBackupResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyChanBackupResponse.ProtoReflect.Descriptor instead. func (*VerifyChanBackupResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{175} + return file_lightning_proto_rawDescGZIP(), []int{176} } type MacaroonPermission struct { @@ -15841,7 +15910,7 @@ type MacaroonPermission struct { func (x *MacaroonPermission) Reset() { *x = MacaroonPermission{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[176] + mi := &file_lightning_proto_msgTypes[177] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15854,7 +15923,7 @@ func (x *MacaroonPermission) String() string { func (*MacaroonPermission) ProtoMessage() {} func (x *MacaroonPermission) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[176] + mi := &file_lightning_proto_msgTypes[177] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15867,7 +15936,7 @@ func (x *MacaroonPermission) ProtoReflect() protoreflect.Message { // Deprecated: Use MacaroonPermission.ProtoReflect.Descriptor instead. func (*MacaroonPermission) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{176} + return file_lightning_proto_rawDescGZIP(), []int{177} } func (x *MacaroonPermission) GetEntity() string { @@ -15901,7 +15970,7 @@ type BakeMacaroonRequest struct { func (x *BakeMacaroonRequest) Reset() { *x = BakeMacaroonRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[177] + mi := &file_lightning_proto_msgTypes[178] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15914,7 +15983,7 @@ func (x *BakeMacaroonRequest) String() string { func (*BakeMacaroonRequest) ProtoMessage() {} func (x *BakeMacaroonRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[177] + mi := &file_lightning_proto_msgTypes[178] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15927,7 +15996,7 @@ func (x *BakeMacaroonRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BakeMacaroonRequest.ProtoReflect.Descriptor instead. func (*BakeMacaroonRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{177} + return file_lightning_proto_rawDescGZIP(), []int{178} } func (x *BakeMacaroonRequest) GetPermissions() []*MacaroonPermission { @@ -15963,7 +16032,7 @@ type BakeMacaroonResponse struct { func (x *BakeMacaroonResponse) Reset() { *x = BakeMacaroonResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[178] + mi := &file_lightning_proto_msgTypes[179] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15976,7 +16045,7 @@ func (x *BakeMacaroonResponse) String() string { func (*BakeMacaroonResponse) ProtoMessage() {} func (x *BakeMacaroonResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[178] + mi := &file_lightning_proto_msgTypes[179] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15989,7 +16058,7 @@ func (x *BakeMacaroonResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BakeMacaroonResponse.ProtoReflect.Descriptor instead. func (*BakeMacaroonResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{178} + return file_lightning_proto_rawDescGZIP(), []int{179} } func (x *BakeMacaroonResponse) GetMacaroon() string { @@ -16008,7 +16077,7 @@ type ListMacaroonIDsRequest struct { func (x *ListMacaroonIDsRequest) Reset() { *x = ListMacaroonIDsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[179] + mi := &file_lightning_proto_msgTypes[180] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16021,7 +16090,7 @@ func (x *ListMacaroonIDsRequest) String() string { func (*ListMacaroonIDsRequest) ProtoMessage() {} func (x *ListMacaroonIDsRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[179] + mi := &file_lightning_proto_msgTypes[180] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16034,7 +16103,7 @@ func (x *ListMacaroonIDsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListMacaroonIDsRequest.ProtoReflect.Descriptor instead. func (*ListMacaroonIDsRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{179} + return file_lightning_proto_rawDescGZIP(), []int{180} } type ListMacaroonIDsResponse struct { @@ -16049,7 +16118,7 @@ type ListMacaroonIDsResponse struct { func (x *ListMacaroonIDsResponse) Reset() { *x = ListMacaroonIDsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[180] + mi := &file_lightning_proto_msgTypes[181] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16062,7 +16131,7 @@ func (x *ListMacaroonIDsResponse) String() string { func (*ListMacaroonIDsResponse) ProtoMessage() {} func (x *ListMacaroonIDsResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[180] + mi := &file_lightning_proto_msgTypes[181] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16075,7 +16144,7 @@ func (x *ListMacaroonIDsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListMacaroonIDsResponse.ProtoReflect.Descriptor instead. func (*ListMacaroonIDsResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{180} + return file_lightning_proto_rawDescGZIP(), []int{181} } func (x *ListMacaroonIDsResponse) GetRootKeyIds() []uint64 { @@ -16097,7 +16166,7 @@ type DeleteMacaroonIDRequest struct { func (x *DeleteMacaroonIDRequest) Reset() { *x = DeleteMacaroonIDRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[181] + mi := &file_lightning_proto_msgTypes[182] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16110,7 +16179,7 @@ func (x *DeleteMacaroonIDRequest) String() string { func (*DeleteMacaroonIDRequest) ProtoMessage() {} func (x *DeleteMacaroonIDRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[181] + mi := &file_lightning_proto_msgTypes[182] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16123,7 +16192,7 @@ func (x *DeleteMacaroonIDRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteMacaroonIDRequest.ProtoReflect.Descriptor instead. func (*DeleteMacaroonIDRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{181} + return file_lightning_proto_rawDescGZIP(), []int{182} } func (x *DeleteMacaroonIDRequest) GetRootKeyId() uint64 { @@ -16145,7 +16214,7 @@ type DeleteMacaroonIDResponse struct { func (x *DeleteMacaroonIDResponse) Reset() { *x = DeleteMacaroonIDResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[182] + mi := &file_lightning_proto_msgTypes[183] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16158,7 +16227,7 @@ func (x *DeleteMacaroonIDResponse) String() string { func (*DeleteMacaroonIDResponse) ProtoMessage() {} func (x *DeleteMacaroonIDResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[182] + mi := &file_lightning_proto_msgTypes[183] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16171,7 +16240,7 @@ func (x *DeleteMacaroonIDResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteMacaroonIDResponse.ProtoReflect.Descriptor instead. func (*DeleteMacaroonIDResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{182} + return file_lightning_proto_rawDescGZIP(), []int{183} } func (x *DeleteMacaroonIDResponse) GetDeleted() bool { @@ -16193,7 +16262,7 @@ type MacaroonPermissionList struct { func (x *MacaroonPermissionList) Reset() { *x = MacaroonPermissionList{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[183] + mi := &file_lightning_proto_msgTypes[184] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16206,7 +16275,7 @@ func (x *MacaroonPermissionList) String() string { func (*MacaroonPermissionList) ProtoMessage() {} func (x *MacaroonPermissionList) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[183] + mi := &file_lightning_proto_msgTypes[184] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16219,7 +16288,7 @@ func (x *MacaroonPermissionList) ProtoReflect() protoreflect.Message { // Deprecated: Use MacaroonPermissionList.ProtoReflect.Descriptor instead. func (*MacaroonPermissionList) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{183} + return file_lightning_proto_rawDescGZIP(), []int{184} } func (x *MacaroonPermissionList) GetPermissions() []*MacaroonPermission { @@ -16238,7 +16307,7 @@ type ListPermissionsRequest struct { func (x *ListPermissionsRequest) Reset() { *x = ListPermissionsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[184] + mi := &file_lightning_proto_msgTypes[185] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16251,7 +16320,7 @@ func (x *ListPermissionsRequest) String() string { func (*ListPermissionsRequest) ProtoMessage() {} func (x *ListPermissionsRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[184] + mi := &file_lightning_proto_msgTypes[185] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16264,7 +16333,7 @@ func (x *ListPermissionsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPermissionsRequest.ProtoReflect.Descriptor instead. func (*ListPermissionsRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{184} + return file_lightning_proto_rawDescGZIP(), []int{185} } type ListPermissionsResponse struct { @@ -16280,7 +16349,7 @@ type ListPermissionsResponse struct { func (x *ListPermissionsResponse) Reset() { *x = ListPermissionsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[185] + mi := &file_lightning_proto_msgTypes[186] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16293,7 +16362,7 @@ func (x *ListPermissionsResponse) String() string { func (*ListPermissionsResponse) ProtoMessage() {} func (x *ListPermissionsResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[185] + mi := &file_lightning_proto_msgTypes[186] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16306,7 +16375,7 @@ func (x *ListPermissionsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPermissionsResponse.ProtoReflect.Descriptor instead. func (*ListPermissionsResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{185} + return file_lightning_proto_rawDescGZIP(), []int{186} } func (x *ListPermissionsResponse) GetMethodPermissions() map[string]*MacaroonPermissionList { @@ -16343,7 +16412,7 @@ type Failure struct { func (x *Failure) Reset() { *x = Failure{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[186] + mi := &file_lightning_proto_msgTypes[187] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16356,7 +16425,7 @@ func (x *Failure) String() string { func (*Failure) ProtoMessage() {} func (x *Failure) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[186] + mi := &file_lightning_proto_msgTypes[187] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16369,7 +16438,7 @@ func (x *Failure) ProtoReflect() protoreflect.Message { // Deprecated: Use Failure.ProtoReflect.Descriptor instead. func (*Failure) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{186} + return file_lightning_proto_rawDescGZIP(), []int{187} } func (x *Failure) GetCode() Failure_FailureCode { @@ -16483,7 +16552,7 @@ type ChannelUpdate struct { func (x *ChannelUpdate) Reset() { *x = ChannelUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[187] + mi := &file_lightning_proto_msgTypes[188] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16496,7 +16565,7 @@ func (x *ChannelUpdate) String() string { func (*ChannelUpdate) ProtoMessage() {} func (x *ChannelUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[187] + mi := &file_lightning_proto_msgTypes[188] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16509,7 +16578,7 @@ func (x *ChannelUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelUpdate.ProtoReflect.Descriptor instead. func (*ChannelUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{187} + return file_lightning_proto_rawDescGZIP(), []int{188} } func (x *ChannelUpdate) GetSignature() []byte { @@ -16609,7 +16678,7 @@ type MacaroonId struct { func (x *MacaroonId) Reset() { *x = MacaroonId{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[188] + mi := &file_lightning_proto_msgTypes[189] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16622,7 +16691,7 @@ func (x *MacaroonId) String() string { func (*MacaroonId) ProtoMessage() {} func (x *MacaroonId) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[188] + mi := &file_lightning_proto_msgTypes[189] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16635,7 +16704,7 @@ func (x *MacaroonId) ProtoReflect() protoreflect.Message { // Deprecated: Use MacaroonId.ProtoReflect.Descriptor instead. func (*MacaroonId) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{188} + return file_lightning_proto_rawDescGZIP(), []int{189} } func (x *MacaroonId) GetNonce() []byte { @@ -16671,7 +16740,7 @@ type Op struct { func (x *Op) Reset() { *x = Op{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[189] + mi := &file_lightning_proto_msgTypes[190] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16684,7 +16753,7 @@ func (x *Op) String() string { func (*Op) ProtoMessage() {} func (x *Op) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[189] + mi := &file_lightning_proto_msgTypes[190] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16697,7 +16766,7 @@ func (x *Op) ProtoReflect() protoreflect.Message { // Deprecated: Use Op.ProtoReflect.Descriptor instead. func (*Op) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{189} + return file_lightning_proto_rawDescGZIP(), []int{190} } func (x *Op) GetEntity() string { @@ -16727,7 +16796,7 @@ type CheckMacPermRequest struct { func (x *CheckMacPermRequest) Reset() { *x = CheckMacPermRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[190] + mi := &file_lightning_proto_msgTypes[191] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16740,7 +16809,7 @@ func (x *CheckMacPermRequest) String() string { func (*CheckMacPermRequest) ProtoMessage() {} func (x *CheckMacPermRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[190] + mi := &file_lightning_proto_msgTypes[191] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16753,7 +16822,7 @@ func (x *CheckMacPermRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckMacPermRequest.ProtoReflect.Descriptor instead. func (*CheckMacPermRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{190} + return file_lightning_proto_rawDescGZIP(), []int{191} } func (x *CheckMacPermRequest) GetMacaroon() []byte { @@ -16788,7 +16857,7 @@ type CheckMacPermResponse struct { func (x *CheckMacPermResponse) Reset() { *x = CheckMacPermResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[191] + mi := &file_lightning_proto_msgTypes[192] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16801,7 +16870,7 @@ func (x *CheckMacPermResponse) String() string { func (*CheckMacPermResponse) ProtoMessage() {} func (x *CheckMacPermResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[191] + mi := &file_lightning_proto_msgTypes[192] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16814,7 +16883,7 @@ func (x *CheckMacPermResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckMacPermResponse.ProtoReflect.Descriptor instead. func (*CheckMacPermResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{191} + return file_lightning_proto_rawDescGZIP(), []int{192} } func (x *CheckMacPermResponse) GetValid() bool { @@ -16868,7 +16937,7 @@ type RPCMiddlewareRequest struct { func (x *RPCMiddlewareRequest) Reset() { *x = RPCMiddlewareRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[192] + mi := &file_lightning_proto_msgTypes[193] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16881,7 +16950,7 @@ func (x *RPCMiddlewareRequest) String() string { func (*RPCMiddlewareRequest) ProtoMessage() {} func (x *RPCMiddlewareRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[192] + mi := &file_lightning_proto_msgTypes[193] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16894,7 +16963,7 @@ func (x *RPCMiddlewareRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RPCMiddlewareRequest.ProtoReflect.Descriptor instead. func (*RPCMiddlewareRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{192} + return file_lightning_proto_rawDescGZIP(), []int{193} } func (x *RPCMiddlewareRequest) GetRequestId() uint64 { @@ -17022,7 +17091,7 @@ type StreamAuth struct { func (x *StreamAuth) Reset() { *x = StreamAuth{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[193] + mi := &file_lightning_proto_msgTypes[194] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17035,7 +17104,7 @@ func (x *StreamAuth) String() string { func (*StreamAuth) ProtoMessage() {} func (x *StreamAuth) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[193] + mi := &file_lightning_proto_msgTypes[194] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17048,7 +17117,7 @@ func (x *StreamAuth) ProtoReflect() protoreflect.Message { // Deprecated: Use StreamAuth.ProtoReflect.Descriptor instead. func (*StreamAuth) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{193} + return file_lightning_proto_rawDescGZIP(), []int{194} } func (x *StreamAuth) GetMethodFullUri() string { @@ -17085,7 +17154,7 @@ type RPCMessage struct { func (x *RPCMessage) Reset() { *x = RPCMessage{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[194] + mi := &file_lightning_proto_msgTypes[195] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17098,7 +17167,7 @@ func (x *RPCMessage) String() string { func (*RPCMessage) ProtoMessage() {} func (x *RPCMessage) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[194] + mi := &file_lightning_proto_msgTypes[195] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17111,7 +17180,7 @@ func (x *RPCMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use RPCMessage.ProtoReflect.Descriptor instead. func (*RPCMessage) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{194} + return file_lightning_proto_rawDescGZIP(), []int{195} } func (x *RPCMessage) GetMethodFullUri() string { @@ -17172,7 +17241,7 @@ type RPCMiddlewareResponse struct { func (x *RPCMiddlewareResponse) Reset() { *x = RPCMiddlewareResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[195] + mi := &file_lightning_proto_msgTypes[196] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17185,7 +17254,7 @@ func (x *RPCMiddlewareResponse) String() string { func (*RPCMiddlewareResponse) ProtoMessage() {} func (x *RPCMiddlewareResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[195] + mi := &file_lightning_proto_msgTypes[196] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17198,7 +17267,7 @@ func (x *RPCMiddlewareResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RPCMiddlewareResponse.ProtoReflect.Descriptor instead. func (*RPCMiddlewareResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{195} + return file_lightning_proto_rawDescGZIP(), []int{196} } func (x *RPCMiddlewareResponse) GetRefMsgId() uint64 { @@ -17284,7 +17353,7 @@ type MiddlewareRegistration struct { func (x *MiddlewareRegistration) Reset() { *x = MiddlewareRegistration{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[196] + mi := &file_lightning_proto_msgTypes[197] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17297,7 +17366,7 @@ func (x *MiddlewareRegistration) String() string { func (*MiddlewareRegistration) ProtoMessage() {} func (x *MiddlewareRegistration) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[196] + mi := &file_lightning_proto_msgTypes[197] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17310,7 +17379,7 @@ func (x *MiddlewareRegistration) ProtoReflect() protoreflect.Message { // Deprecated: Use MiddlewareRegistration.ProtoReflect.Descriptor instead. func (*MiddlewareRegistration) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{196} + return file_lightning_proto_rawDescGZIP(), []int{197} } func (x *MiddlewareRegistration) GetMiddlewareName() string { @@ -17357,7 +17426,7 @@ type InterceptFeedback struct { func (x *InterceptFeedback) Reset() { *x = InterceptFeedback{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[197] + mi := &file_lightning_proto_msgTypes[198] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17370,7 +17439,7 @@ func (x *InterceptFeedback) String() string { func (*InterceptFeedback) ProtoMessage() {} func (x *InterceptFeedback) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[197] + mi := &file_lightning_proto_msgTypes[198] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17383,7 +17452,7 @@ func (x *InterceptFeedback) ProtoReflect() protoreflect.Message { // Deprecated: Use InterceptFeedback.ProtoReflect.Descriptor instead. func (*InterceptFeedback) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{197} + return file_lightning_proto_rawDescGZIP(), []int{198} } func (x *InterceptFeedback) GetError() string { @@ -17444,7 +17513,7 @@ type PendingChannelsResponse_PendingChannel struct { func (x *PendingChannelsResponse_PendingChannel) Reset() { *x = PendingChannelsResponse_PendingChannel{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[204] + mi := &file_lightning_proto_msgTypes[205] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17457,7 +17526,7 @@ func (x *PendingChannelsResponse_PendingChannel) String() string { func (*PendingChannelsResponse_PendingChannel) ProtoMessage() {} func (x *PendingChannelsResponse_PendingChannel) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[204] + mi := &file_lightning_proto_msgTypes[205] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17605,7 +17674,7 @@ type PendingChannelsResponse_PendingOpenChannel struct { func (x *PendingChannelsResponse_PendingOpenChannel) Reset() { *x = PendingChannelsResponse_PendingOpenChannel{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[205] + mi := &file_lightning_proto_msgTypes[206] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17618,7 +17687,7 @@ func (x *PendingChannelsResponse_PendingOpenChannel) String() string { func (*PendingChannelsResponse_PendingOpenChannel) ProtoMessage() {} func (x *PendingChannelsResponse_PendingOpenChannel) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[205] + mi := &file_lightning_proto_msgTypes[206] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17691,7 +17760,7 @@ type PendingChannelsResponse_WaitingCloseChannel struct { func (x *PendingChannelsResponse_WaitingCloseChannel) Reset() { *x = PendingChannelsResponse_WaitingCloseChannel{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[206] + mi := &file_lightning_proto_msgTypes[207] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17704,7 +17773,7 @@ func (x *PendingChannelsResponse_WaitingCloseChannel) String() string { func (*PendingChannelsResponse_WaitingCloseChannel) ProtoMessage() {} func (x *PendingChannelsResponse_WaitingCloseChannel) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[206] + mi := &file_lightning_proto_msgTypes[207] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17780,7 +17849,7 @@ type PendingChannelsResponse_Commitments struct { func (x *PendingChannelsResponse_Commitments) Reset() { *x = PendingChannelsResponse_Commitments{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[207] + mi := &file_lightning_proto_msgTypes[208] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17793,7 +17862,7 @@ func (x *PendingChannelsResponse_Commitments) String() string { func (*PendingChannelsResponse_Commitments) ProtoMessage() {} func (x *PendingChannelsResponse_Commitments) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[207] + mi := &file_lightning_proto_msgTypes[208] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17865,7 +17934,7 @@ type PendingChannelsResponse_ClosedChannel struct { func (x *PendingChannelsResponse_ClosedChannel) Reset() { *x = PendingChannelsResponse_ClosedChannel{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[208] + mi := &file_lightning_proto_msgTypes[209] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17878,7 +17947,7 @@ func (x *PendingChannelsResponse_ClosedChannel) String() string { func (*PendingChannelsResponse_ClosedChannel) ProtoMessage() {} func (x *PendingChannelsResponse_ClosedChannel) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[208] + mi := &file_lightning_proto_msgTypes[209] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17934,7 +18003,7 @@ type PendingChannelsResponse_ForceClosedChannel struct { func (x *PendingChannelsResponse_ForceClosedChannel) Reset() { *x = PendingChannelsResponse_ForceClosedChannel{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[209] + mi := &file_lightning_proto_msgTypes[210] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17947,7 +18016,7 @@ func (x *PendingChannelsResponse_ForceClosedChannel) String() string { func (*PendingChannelsResponse_ForceClosedChannel) ProtoMessage() {} func (x *PendingChannelsResponse_ForceClosedChannel) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[209] + mi := &file_lightning_proto_msgTypes[210] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -20140,948 +20209,956 @@ var file_lightning_proto_rawDesc = []byte{ 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x66, 0x61, 0x69, - 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x78, 0x0a, 0x18, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x66, 0x61, 0x69, 0x6c, - 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, - 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, - 0x63, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1b, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbf, 0x01, 0x0a, - 0x15, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, - 0x6e, 0x74, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x12, 0x39, 0x0a, 0x19, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x75, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x68, 0x69, 0x6d, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x75, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x53, 0x68, 0x69, 0x6d, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x31, 0x0a, 0x16, 0x69, - 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x5f, 0x77, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x5f, 0x61, 0x6d, 0x5f, - 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x4b, 0x6e, - 0x6f, 0x77, 0x57, 0x68, 0x61, 0x74, 0x49, 0x41, 0x6d, 0x44, 0x6f, 0x69, 0x6e, 0x67, 0x22, 0x18, - 0x0a, 0x16, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x46, 0x0a, 0x11, 0x44, 0x65, 0x62, 0x75, - 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x68, 0x6f, - 0x77, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x53, 0x70, 0x65, 0x63, - 0x22, 0x35, 0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x73, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, - 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x27, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x52, 0x65, - 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x5f, 0x72, - 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x79, 0x52, 0x65, 0x71, - 0x22, 0xb0, 0x04, 0x0a, 0x06, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, 0x64, - 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, - 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, - 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x61, 0x74, 0x6f, 0x73, - 0x68, 0x69, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, - 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, - 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, - 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x31, 0x0a, 0x0b, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, - 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, - 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6e, 0x75, 0x6d, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x37, 0x0a, - 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x2e, 0x46, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x59, 0x0a, 0x07, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x22, 0x12, - 0x0a, 0x10, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x95, 0x02, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, - 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, - 0x61, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x73, - 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, - 0x0b, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x09, 0x66, 0x65, 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x12, 0x19, 0x0a, - 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, - 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x42, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x69, - 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, - 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, - 0x64, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x22, 0xb5, 0x01, 0x0a, 0x11, 0x46, - 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x3a, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x65, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, - 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0b, - 0x64, 0x61, 0x79, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x09, 0x64, 0x61, 0x79, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x20, 0x0a, 0x0c, - 0x77, 0x65, 0x65, 0x6b, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0a, 0x77, 0x65, 0x65, 0x6b, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x22, - 0x0a, 0x0d, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x46, 0x65, 0x65, 0x53, - 0x75, 0x6d, 0x22, 0xda, 0x03, 0x0a, 0x13, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x06, 0x67, 0x6c, - 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x67, 0x6c, - 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, - 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, - 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x19, - 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, 0x65, - 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0a, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x26, 0x0a, 0x0f, 0x74, - 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, 0x65, - 0x6c, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, - 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, - 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x68, - 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, - 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x35, 0x0a, 0x17, 0x6d, - 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x5f, 0x73, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6d, 0x69, - 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x65, 0x64, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x62, 0x61, - 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x12, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x61, 0x73, 0x65, 0x46, 0x65, - 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2f, 0x0a, 0x14, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x11, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x52, - 0x61, 0x74, 0x65, 0x50, 0x70, 0x6d, 0x42, 0x07, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x22, - 0x8c, 0x01, 0x0a, 0x0c, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x12, 0x2b, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, 0x6f, - 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, - 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x52, - 0x0a, 0x14, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, - 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x73, 0x22, 0xc9, 0x01, 0x0a, 0x18, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, - 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, - 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, - 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4d, 0x61, 0x78, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, - 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x70, - 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x22, 0x85, - 0x03, 0x0a, 0x0f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x12, 0x20, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, - 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x08, 0x63, 0x68, - 0x61, 0x6e, 0x49, 0x64, 0x49, 0x6e, 0x12, 0x22, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, - 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, - 0x09, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x6d, - 0x74, 0x5f, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x61, 0x6d, 0x74, 0x49, - 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, - 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, - 0x66, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x6d, 0x74, 0x5f, 0x69, - 0x6e, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x6d, - 0x74, 0x49, 0x6e, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, 0x5f, 0x6f, - 0x75, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x61, - 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x22, 0x0a, 0x0d, - 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x49, 0x6e, - 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6f, - 0x75, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x4f, 0x75, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x19, 0x46, 0x6f, 0x72, 0x77, 0x61, - 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x11, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, - 0x6e, 0x67, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, - 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, - 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x50, 0x0a, 0x1a, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, - 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x64, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, - 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, - 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x73, 0x0a, - 0x0f, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x12, 0x34, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, - 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9f, 0x01, - 0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, - 0x73, 0x68, 0x6f, 0x74, 0x12, 0x45, 0x0a, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x63, - 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x11, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, - 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x42, 0x0a, 0x11, 0x6d, - 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, - 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0f, - 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, - 0x49, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0b, 0x63, - 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x18, 0x52, - 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, - 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, - 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, - 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x42, 0x08, 0x0a, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x17, 0x0a, 0x15, 0x52, - 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x0a, - 0x12, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0xb0, 0x01, 0x0a, 0x13, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, - 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x70, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x6f, 0x6f, 0x74, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, - 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x32, 0x0a, 0x14, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, - 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, - 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, - 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, - 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x20, 0x0a, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, - 0x73, 0x22, 0x39, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, - 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0b, - 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x34, 0x0a, 0x18, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x64, 0x22, 0x55, 0x0a, 0x16, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, - 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, - 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x64, 0x0a, 0x12, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x63, 0x0a, 0x16, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcc, 0x08, 0x0a, 0x07, 0x46, - 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, - 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, - 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x3b, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, - 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x68, 0x61, 0x5f, 0x32, 0x35, - 0x36, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x53, 0x68, - 0x61, 0x32, 0x35, 0x36, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, - 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45, - 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x66, - 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x66, 0x61, 0x69, 0x6c, 0x75, - 0x72, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, - 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x8b, 0x06, 0x0a, 0x0b, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, - 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, - 0x5f, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x50, 0x41, 0x59, 0x4d, - 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x01, 0x12, 0x1c, 0x0a, - 0x18, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, - 0x4e, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x46, - 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x43, - 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x03, 0x12, 0x1f, 0x0a, 0x1b, - 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, - 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x04, 0x12, 0x19, 0x0a, - 0x15, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, - 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x56, 0x41, - 0x4c, 0x49, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x4c, 0x4d, 0x10, 0x06, 0x12, 0x13, 0x0a, 0x0f, 0x45, - 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x07, - 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, - 0x4e, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, 0x49, - 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x48, 0x4d, 0x41, - 0x43, 0x10, 0x09, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, - 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0a, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x4d, - 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x42, 0x45, 0x4c, 0x4f, 0x57, 0x5f, 0x4d, 0x49, 0x4e, 0x49, 0x4d, - 0x55, 0x4d, 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x55, - 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, - 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, 0x50, - 0x49, 0x52, 0x59, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, - 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x0e, 0x12, 0x1d, 0x0a, 0x19, 0x54, - 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, - 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x0f, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, - 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x45, 0x41, 0x54, - 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x10, 0x12, 0x24, 0x0a, - 0x20, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, - 0x4c, 0x5f, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, - 0x47, 0x10, 0x11, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x4e, - 0x45, 0x58, 0x54, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x12, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, - 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, 0x49, - 0x4c, 0x55, 0x52, 0x45, 0x10, 0x13, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, - 0x45, 0x4e, 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, - 0x10, 0x14, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, 0x45, 0x4e, 0x54, 0x5f, - 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, - 0x15, 0x12, 0x12, 0x0a, 0x0e, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, - 0x46, 0x41, 0x52, 0x10, 0x16, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x50, 0x50, 0x5f, 0x54, 0x49, 0x4d, - 0x45, 0x4f, 0x55, 0x54, 0x10, 0x17, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, - 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, 0x44, 0x10, - 0x18, 0x12, 0x1a, 0x0a, 0x16, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, - 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x19, 0x12, 0x15, 0x0a, - 0x10, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, - 0x45, 0x10, 0xe5, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, - 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0xe6, 0x07, 0x12, 0x17, 0x0a, 0x12, 0x55, 0x4e, - 0x52, 0x45, 0x41, 0x44, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, - 0x10, 0xe7, 0x07, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xb3, 0x03, 0x0a, 0x0d, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, - 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x66, - 0x6c, 0x61, 0x67, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x26, 0x0a, - 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, - 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x69, - 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x4d, 0x73, 0x61, - 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, - 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, - 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x4d, - 0x73, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x6f, 0x70, 0x61, - 0x71, 0x75, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, - 0x65, 0x78, 0x74, 0x72, 0x61, 0x4f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, - 0x5d, 0x0a, 0x0a, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, - 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, - 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, - 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x6f, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x52, 0x03, 0x6f, 0x70, 0x73, 0x22, 0x36, - 0x0a, 0x02, 0x4f, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x13, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, - 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x4d, - 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x75, 0x6c, - 0x6c, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x2c, 0x0a, 0x14, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x22, 0xf4, 0x02, 0x0a, 0x14, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, - 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, - 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, - 0x0c, 0x72, 0x61, 0x77, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x72, 0x61, 0x77, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, - 0x12, 0x36, 0x0a, 0x17, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, - 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x15, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x43, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x0b, 0x73, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, - 0x48, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, 0x2d, - 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, - 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, - 0x0a, 0x0c, 0x72, 0x65, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x67, 0x43, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x34, 0x0a, 0x0a, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, 0x55, - 0x72, 0x69, 0x22, 0xab, 0x01, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x6c, - 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x69, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x5f, 0x72, 0x70, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x70, 0x63, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, - 0x7a, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x22, 0xc0, 0x01, 0x0a, 0x15, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, - 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x72, 0x65, - 0x66, 0x5f, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, - 0x72, 0x65, 0x66, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x08, 0x72, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, - 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, - 0x6b, 0x48, 0x00, 0x52, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x42, 0x14, 0x0a, - 0x12, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x16, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, - 0x72, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, - 0x0a, 0x0f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, - 0x61, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, - 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x63, 0x75, - 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x43, 0x61, 0x76, 0x65, - 0x61, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, - 0x6e, 0x6c, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, - 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x8b, 0x01, 0x0a, - 0x11, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, - 0x63, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x6c, - 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x16, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x2a, 0xcb, 0x02, 0x0a, 0x10, 0x4f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x1b, 0x0a, 0x17, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, - 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, - 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x43, 0x52, 0x49, - 0x50, 0x54, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, - 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, - 0x5f, 0x56, 0x30, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, - 0x02, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x30, 0x5f, 0x53, 0x43, 0x52, 0x49, - 0x50, 0x54, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x43, 0x52, - 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, - 0x04, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x53, 0x49, 0x47, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, 0x53, - 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x55, 0x4c, 0x4c, 0x44, - 0x41, 0x54, 0x41, 0x10, 0x06, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x41, 0x52, - 0x44, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, - 0x57, 0x4e, 0x10, 0x08, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x31, 0x5f, 0x54, - 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x09, 0x2a, 0x62, 0x0a, 0x15, 0x43, 0x6f, 0x69, 0x6e, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x53, - 0x45, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, - 0x00, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x4c, 0x41, - 0x52, 0x47, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x52, 0x41, 0x54, - 0x45, 0x47, 0x59, 0x5f, 0x52, 0x41, 0x4e, 0x44, 0x4f, 0x4d, 0x10, 0x02, 0x2a, 0xac, 0x01, 0x0a, - 0x0b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, - 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, - 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, - 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x1e, 0x0a, - 0x1a, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, - 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, 0x1d, 0x0a, - 0x19, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x50, - 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, - 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x04, - 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, - 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x05, 0x2a, 0x8c, 0x01, 0x0a, 0x0e, - 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, - 0x0a, 0x17, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, - 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, - 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x49, - 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x0b, - 0x0a, 0x07, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x53, - 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x45, 0x4e, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x4c, - 0x45, 0x41, 0x53, 0x45, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, - 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x09, 0x49, 0x6e, - 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x49, 0x54, 0x49, - 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, - 0x0a, 0x0f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x4f, 0x43, 0x41, - 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, - 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x49, - 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, 0x60, 0x0a, - 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, - 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x11, 0x0a, - 0x0d, 0x49, 0x4e, 0x43, 0x4f, 0x4d, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, 0x02, - 0x12, 0x11, 0x0a, 0x0d, 0x4f, 0x55, 0x54, 0x47, 0x4f, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, - 0x43, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04, 0x2a, - 0x71, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, - 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, - 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, 0x41, - 0x49, 0x4d, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x4c, 0x41, 0x49, - 0x4d, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, - 0x45, 0x44, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x53, 0x54, - 0x41, 0x47, 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, - 0x10, 0x05, 0x2a, 0x39, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, - 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x45, 0x54, 0x57, 0x45, 0x45, 0x4e, 0x4e, 0x45, 0x53, 0x53, - 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x52, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x01, 0x2a, 0x3b, 0x0a, - 0x10, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, - 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xd9, 0x01, 0x0a, 0x14, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, - 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, - 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, - 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, - 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x4f, - 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, - 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, - 0x2c, 0x0a, 0x28, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, - 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, - 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x27, 0x0a, - 0x23, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, - 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, - 0x41, 0x4e, 0x43, 0x45, 0x10, 0x05, 0x2a, 0xcf, 0x04, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x42, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, - 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x00, 0x12, - 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, - 0x45, 0x43, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x49, - 0x54, 0x49, 0x41, 0x4c, 0x5f, 0x52, 0x4f, 0x55, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x59, 0x4e, 0x43, - 0x10, 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, - 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x52, 0x45, - 0x51, 0x10, 0x04, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, - 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x4f, - 0x50, 0x54, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, - 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, - 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, - 0x50, 0x54, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, - 0x4e, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, - 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, - 0x54, 0x5f, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, - 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0a, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, - 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, - 0x10, 0x0b, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, - 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0c, 0x12, 0x19, 0x0a, - 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, - 0x45, 0x59, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, - 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0e, 0x12, 0x14, - 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x4f, - 0x50, 0x54, 0x10, 0x0f, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, - 0x10, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x11, 0x12, 0x16, - 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, - 0x5f, 0x52, 0x45, 0x51, 0x10, 0x12, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, - 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x13, 0x12, 0x0f, - 0x0a, 0x0b, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x14, 0x12, - 0x0f, 0x0a, 0x0b, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x15, - 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, - 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x16, 0x12, - 0x1d, 0x0a, 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, - 0x46, 0x45, 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x17, 0x12, 0x0b, - 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x1e, 0x12, 0x0b, 0x0a, 0x07, 0x41, - 0x4d, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x1f, 0x2a, 0xac, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x50, - 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x4b, - 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, - 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, - 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, - 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x02, - 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, - 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x10, - 0x03, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, - 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x50, 0x41, 0x52, 0x41, - 0x4d, 0x45, 0x54, 0x45, 0x52, 0x10, 0x04, 0x32, 0xb9, 0x27, 0x0a, 0x09, 0x4c, 0x69, 0x67, 0x68, - 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x4a, 0x0a, 0x0d, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, - 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x44, 0x0a, - 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, - 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, - 0x6e, 0x74, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, - 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x15, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x12, 0x3b, 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64, 0x4d, - 0x61, 0x6e, 0x79, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, - 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x4d, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, - 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, - 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, - 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x50, 0x65, 0x65, 0x72, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, - 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, - 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, - 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, - 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, - 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, - 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1a, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, - 0x4d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, - 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x79, 0x6e, - 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x10, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, - 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x10, 0x46, - 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x12, - 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x67, 0x1a, 0x1b, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x50, 0x0a, 0x0f, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x1c, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x0c, 0x43, - 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, - 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, - 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, - 0x01, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x19, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, - 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x41, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, - 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x41, 0x64, - 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, - 0x63, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, - 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x0d, 0x4c, 0x6f, - 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x12, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x1a, - 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, - 0x41, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x49, 0x6e, 0x76, 0x6f, - 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, - 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, 0x61, 0x79, 0x52, - 0x65, 0x71, 0x12, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, - 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x1a, 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x4a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, - 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x47, - 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x47, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, - 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x47, 0x65, 0x74, - 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x44, 0x0a, 0x0b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, - 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x35, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x70, - 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x57, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x41, 0x0a, 0x0a, 0x44, 0x65, 0x62, 0x75, - 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, - 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, - 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x46, - 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x46, + 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x9b, 0x01, 0x0a, + 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x66, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x66, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, + 0x6c, 0x63, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6c, 0x6c, 0x5f, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, + 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0xbf, 0x01, 0x0a, 0x15, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x19, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, + 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x68, 0x69, 0x6d, 0x5f, 0x6f, 0x6e, 0x6c, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x69, 0x6d, 0x4f, 0x6e, 0x6c, 0x79, 0x12, + 0x31, 0x0a, 0x16, 0x69, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x5f, 0x77, 0x68, 0x61, 0x74, 0x5f, 0x69, + 0x5f, 0x61, 0x6d, 0x5f, 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x11, 0x69, 0x4b, 0x6e, 0x6f, 0x77, 0x57, 0x68, 0x61, 0x74, 0x49, 0x41, 0x6d, 0x44, 0x6f, 0x69, + 0x6e, 0x67, 0x22, 0x18, 0x0a, 0x16, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x46, 0x0a, 0x11, + 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x5f, 0x73, + 0x70, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x65, 0x76, 0x65, 0x6c, + 0x53, 0x70, 0x65, 0x63, 0x22, 0x35, 0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, + 0x62, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x73, 0x75, 0x62, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x27, 0x0a, 0x0c, 0x50, + 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x70, + 0x61, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, + 0x79, 0x52, 0x65, 0x71, 0x22, 0xb0, 0x04, 0x0a, 0x06, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, + 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x61, 0x74, 0x6f, + 0x73, 0x68, 0x69, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, + 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x20, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x12, + 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, + 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, + 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, + 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x73, + 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6e, 0x75, 0x6d, 0x4d, 0x73, 0x61, + 0x74, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x0d, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, + 0x65, 0x71, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x59, 0x0a, 0x07, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x6b, 0x6e, + 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x4b, 0x6e, 0x6f, + 0x77, 0x6e, 0x22, 0x12, 0x0a, 0x10, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x95, 0x02, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x07, 0x63, + 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, + 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, + 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, + 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x66, 0x65, 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, + 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x31, 0x0a, 0x15, + 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, + 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x69, 0x6e, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, + 0x2d, 0x0a, 0x13, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, + 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x69, 0x6e, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x22, 0xb5, + 0x01, 0x0a, 0x11, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, + 0x66, 0x65, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x73, + 0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x61, 0x79, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x64, 0x61, 0x79, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, + 0x12, 0x20, 0x0a, 0x0c, 0x77, 0x65, 0x65, 0x6b, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x77, 0x65, 0x65, 0x6b, 0x46, 0x65, 0x65, 0x53, + 0x75, 0x6d, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, + 0x73, 0x75, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x6f, 0x6e, 0x74, 0x68, + 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x22, 0x52, 0x0a, 0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x46, 0x65, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, + 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x61, 0x73, + 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, + 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, + 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x50, 0x70, 0x6d, 0x22, 0xaa, 0x03, 0x0a, 0x13, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x18, 0x0a, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x34, 0x0a, 0x0a, + 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, + 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, + 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, + 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, + 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, + 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, + 0x50, 0x70, 0x6d, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, + 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0d, 0x6d, + 0x61, 0x78, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, + 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, + 0x73, 0x61, 0x74, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, + 0x6d, 0x73, 0x61, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, + 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x69, 0x6e, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, + 0x65, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x42, 0x07, + 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x22, 0x8c, 0x01, 0x0a, 0x0c, 0x46, 0x61, 0x69, 0x6c, + 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x06, 0x72, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x52, 0x0a, 0x14, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, + 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x66, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x22, 0xc9, 0x01, 0x0a, 0x18, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, - 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x21, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x12, 0x54, 0x0a, 0x17, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x6c, 0x6c, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1e, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x4e, 0x0a, 0x10, 0x56, 0x65, 0x72, - 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x19, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x20, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x30, 0x01, 0x12, 0x47, 0x0a, 0x0c, 0x42, - 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, - 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, + 0x6d, 0x4d, 0x61, 0x78, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x65, + 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x22, 0x85, 0x03, 0x0a, 0x0f, 0x46, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x20, 0x0a, 0x0a, + 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x42, 0x02, 0x30, 0x01, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x49, 0x6e, 0x12, 0x22, + 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x4f, + 0x75, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x6d, 0x74, 0x5f, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x05, 0x61, 0x6d, 0x74, 0x49, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x6d, 0x74, + 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x74, 0x4f, + 0x75, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x03, 0x66, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, + 0x1e, 0x0a, 0x0b, 0x61, 0x6d, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x6d, 0x74, 0x49, 0x6e, 0x4d, 0x73, 0x61, 0x74, 0x12, + 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x4d, 0x73, 0x61, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, + 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, + 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x65, 0x65, + 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x49, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, + 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4f, 0x75, 0x74, 0x22, 0x8c, + 0x01, 0x0a, 0x19, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x11, + 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x6c, 0x61, + 0x73, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x50, 0x0a, + 0x1a, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x63, + 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, + 0x64, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x73, 0x0a, 0x0f, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, + 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x34, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, + 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x2a, + 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, + 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x68, + 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9f, 0x01, 0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x45, 0x0a, 0x13, + 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, + 0x52, 0x11, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x73, 0x12, 0x42, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, + 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x49, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x63, 0x68, 0x61, + 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x3a, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x48, 0x00, 0x52, 0x0b, + 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x6d, + 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, + 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x42, 0x08, 0x0a, 0x06, 0x62, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x22, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x0a, 0x12, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, + 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb0, 0x01, 0x0a, 0x13, + 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, + 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x32, + 0x0a, 0x14, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, - 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, - 0x18, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x50, - 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x5f, + 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0a, 0x72, + 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x73, 0x22, 0x39, 0x0a, 0x17, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x4b, + 0x65, 0x79, 0x49, 0x64, 0x22, 0x34, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, + 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x55, 0x0a, 0x16, 0x4d, 0x61, + 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x17, + 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x12, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x63, 0x0a, + 0x16, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0xcc, 0x08, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x2e, + 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x3b, + 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x68, + 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, + 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x6e, 0x69, 0x6f, + 0x6e, 0x5f, 0x73, 0x68, 0x61, 0x5f, 0x32, 0x35, 0x36, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0b, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x1f, 0x0a, 0x0b, + 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, + 0x61, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x12, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x8b, 0x06, + 0x0a, 0x0b, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x0c, 0x0a, + 0x08, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x44, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x49, + 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, + 0x4f, 0x57, 0x4e, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, + 0x49, 0x4c, 0x53, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, + 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, + 0x54, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, + 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, 0x50, 0x49, + 0x52, 0x59, 0x10, 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, + 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x4d, 0x4f, + 0x55, 0x4e, 0x54, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x45, + 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x05, + 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x4c, + 0x4d, 0x10, 0x06, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, + 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x07, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, + 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, + 0x4e, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, + 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x10, 0x09, 0x12, 0x15, 0x0a, 0x11, 0x49, + 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x4b, 0x45, 0x59, + 0x10, 0x0a, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x42, 0x45, 0x4c, + 0x4f, 0x57, 0x5f, 0x4d, 0x49, 0x4e, 0x49, 0x4d, 0x55, 0x4d, 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, + 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, + 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, + 0x43, 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x0d, 0x12, 0x14, 0x0a, + 0x10, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, + 0x44, 0x10, 0x0e, 0x12, 0x1d, 0x0a, 0x19, 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, + 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, + 0x10, 0x0f, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x4e, + 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, + 0x49, 0x4e, 0x47, 0x10, 0x10, 0x12, 0x24, 0x0a, 0x20, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, + 0x44, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, + 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x11, 0x12, 0x15, 0x0a, 0x11, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x4e, 0x45, 0x58, 0x54, 0x5f, 0x50, 0x45, 0x45, 0x52, + 0x10, 0x12, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, + 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x13, 0x12, 0x1a, + 0x0a, 0x16, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45, + 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x14, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x45, + 0x52, 0x4d, 0x41, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, + 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x15, 0x12, 0x12, 0x0a, 0x0e, 0x45, 0x58, 0x50, + 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x46, 0x41, 0x52, 0x10, 0x16, 0x12, 0x0f, 0x0a, + 0x0b, 0x4d, 0x50, 0x50, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x17, 0x12, 0x19, + 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, + 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, 0x44, 0x10, 0x18, 0x12, 0x1a, 0x0a, 0x16, 0x49, 0x4e, 0x56, + 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, + 0x49, 0x4e, 0x47, 0x10, 0x19, 0x12, 0x15, 0x0a, 0x10, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, + 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0xe5, 0x07, 0x12, 0x14, 0x0a, 0x0f, + 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, + 0xe6, 0x07, 0x12, 0x17, 0x0a, 0x12, 0x55, 0x4e, 0x52, 0x45, 0x41, 0x44, 0x41, 0x42, 0x4c, 0x45, + 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0xe7, 0x07, 0x4a, 0x04, 0x08, 0x02, 0x10, + 0x03, 0x22, 0xb3, 0x03, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x48, 0x61, 0x73, 0x68, + 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, + 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, + 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, + 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x2a, 0x0a, + 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, + 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x69, + 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, + 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x62, 0x61, 0x73, + 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, + 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, + 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, + 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x65, + 0x78, 0x74, 0x72, 0x61, 0x5f, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x4f, 0x70, 0x61, + 0x71, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x0a, 0x0a, 0x4d, 0x61, 0x63, 0x61, 0x72, + 0x6f, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x6f, 0x70, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, + 0x70, 0x52, 0x03, 0x6f, 0x70, 0x73, 0x22, 0x36, 0x0a, 0x02, 0x4f, 0x70, 0x12, 0x16, 0x0a, 0x06, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, + 0x01, 0x0a, 0x13, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x1e, 0x0a, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, + 0x2c, 0x0a, 0x14, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x22, 0xf4, 0x02, + 0x0a, 0x14, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x61, 0x77, 0x5f, 0x6d, 0x61, 0x63, + 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x72, 0x61, 0x77, + 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x34, 0x0a, 0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, 0x2d, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x72, 0x65, 0x67, 0x5f, 0x63, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0b, + 0x72, 0x65, 0x67, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6d, + 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6d, 0x73, 0x67, + 0x49, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x22, 0x34, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, + 0x74, 0x68, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, + 0x6c, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x69, 0x22, 0xab, 0x01, 0x0a, 0x0a, 0x52, + 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, 0x55, 0x72, + 0x69, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x72, 0x70, 0x63, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x70, 0x63, + 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, + 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x19, 0x0a, + 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xc0, 0x01, 0x0a, 0x15, 0x52, 0x50, 0x43, + 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x5f, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x66, 0x4d, 0x73, 0x67, 0x49, 0x64, + 0x12, 0x3b, 0x0a, 0x08, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x64, 0x64, 0x6c, + 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x36, 0x0a, + 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, + 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x66, 0x65, 0x65, + 0x64, 0x62, 0x61, 0x63, 0x6b, 0x42, 0x14, 0x0a, 0x12, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, + 0x61, 0x72, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x16, + 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, + 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x3d, 0x0a, 0x1b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x61, 0x63, 0x61, + 0x72, 0x6f, 0x6f, 0x6e, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, + 0x0a, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, + 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, + 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x16, 0x72, + 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x72, 0x65, 0x70, + 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x64, 0x2a, 0xcb, 0x02, 0x0a, 0x10, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x43, 0x52, 0x49, 0x50, + 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, + 0x53, 0x48, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, + 0x01, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x30, 0x5f, 0x50, 0x55, 0x42, 0x4b, + 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, + 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, + 0x5f, 0x56, 0x30, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, + 0x03, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x43, 0x52, + 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x53, 0x49, + 0x47, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x4e, 0x55, 0x4c, 0x4c, 0x44, 0x41, 0x54, 0x41, 0x10, 0x06, 0x12, 0x1c, 0x0a, + 0x18, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, + 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x41, 0x52, 0x44, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, + 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, + 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x08, 0x12, 0x22, 0x0a, 0x1e, + 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, + 0x45, 0x53, 0x53, 0x5f, 0x56, 0x31, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x09, + 0x2a, 0x62, 0x0a, 0x15, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x54, 0x52, + 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x53, 0x45, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, + 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x52, + 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x4c, 0x41, 0x52, 0x47, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, + 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x52, 0x41, 0x4e, 0x44, + 0x4f, 0x4d, 0x10, 0x02, 0x2a, 0xac, 0x01, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, + 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x16, 0x0a, + 0x12, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, + 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, + 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, + 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, + 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, + 0x53, 0x48, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, + 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x55, 0x53, + 0x45, 0x44, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, + 0x59, 0x10, 0x05, 0x2a, 0x8c, 0x01, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, + 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, + 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, + 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, + 0x53, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x45, 0x4e, + 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x10, 0x04, 0x12, 0x12, + 0x0a, 0x0e, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, + 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x09, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, + 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, + 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, + 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x49, + 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, + 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x42, + 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, 0x60, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, + 0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x43, 0x4f, 0x4d, 0x49, 0x4e, + 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4f, 0x55, 0x54, 0x47, + 0x4f, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, + 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04, 0x2a, 0x71, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x6c, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x0f, + 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, + 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, + 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, + 0x09, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, + 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, + 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x05, 0x2a, 0x39, 0x0a, 0x0e, 0x4e, 0x6f, + 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, + 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x45, 0x54, + 0x57, 0x45, 0x45, 0x4e, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x52, 0x41, 0x4c, + 0x49, 0x54, 0x59, 0x10, 0x01, 0x2a, 0x3b, 0x0a, 0x10, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x43, 0x43, + 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, + 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, + 0x10, 0x02, 0x2a, 0xd9, 0x01, 0x0a, 0x14, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, + 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, + 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, + 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, + 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, + 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, + 0x14, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x2c, 0x0a, 0x28, 0x46, 0x41, 0x49, 0x4c, 0x55, + 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, + 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, + 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x27, 0x0a, 0x23, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, + 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, + 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x05, 0x2a, 0x89, + 0x05, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, 0x74, 0x12, 0x18, 0x0a, + 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, + 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, + 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, + 0x01, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x5f, 0x52, 0x4f, 0x55, + 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, + 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, + 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x04, 0x12, 0x1f, 0x0a, 0x1b, 0x55, + 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, + 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, + 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, + 0x45, 0x51, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, + 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, + 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x08, 0x12, + 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x50, 0x54, + 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, + 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0a, 0x12, 0x1a, + 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, + 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0b, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, + 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, + 0x52, 0x45, 0x51, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, + 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0d, + 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, + 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, + 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0f, 0x12, 0x0b, 0x0a, 0x07, + 0x4d, 0x50, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x10, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, + 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x11, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, + 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x12, 0x12, 0x16, + 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, + 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x13, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, + 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x14, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x4e, 0x43, 0x48, 0x4f, + 0x52, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x15, 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x4e, 0x43, 0x48, + 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x48, 0x54, 0x4c, + 0x43, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x16, 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, + 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, + 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x17, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, + 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, + 0x44, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, + 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x19, + 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x1e, 0x12, 0x0b, 0x0a, + 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x1f, 0x2a, 0xac, 0x01, 0x0a, 0x0d, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x16, + 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, + 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, + 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, + 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, + 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, + 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x52, + 0x52, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, + 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x50, 0x41, + 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x10, 0x04, 0x32, 0xb9, 0x27, 0x0a, 0x09, 0x4c, 0x69, + 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x4a, 0x0a, 0x0d, 0x57, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, + 0x44, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, + 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, + 0x6e, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, + 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, + 0x70, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x15, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x12, 0x3b, 0x0a, 0x08, 0x53, 0x65, 0x6e, + 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x53, 0x69, 0x67, + 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x4a, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, + 0x65, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x69, 0x73, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3e, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x17, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x47, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x50, 0x65, 0x65, + 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, + 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, + 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, + 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, + 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, + 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, + 0x0a, 0x0f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, + 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x16, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, + 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x41, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, + 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x10, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1e, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, + 0x10, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, + 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x67, 0x1a, 0x1b, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x50, 0x0a, 0x0f, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x1c, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x1b, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x46, 0x0a, + 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, + 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, + 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x41, 0x0a, 0x0f, 0x53, 0x65, 0x6e, + 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, + 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x0d, + 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x12, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, + 0x68, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x12, 0x41, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x49, 0x6e, + 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, + 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, + 0x63, 0x65, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, 0x61, + 0x79, 0x52, 0x65, 0x71, 0x12, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, + 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x1a, 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, + 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x47, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x47, + 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x0b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0e, 0x47, 0x65, 0x74, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x35, 0x0a, 0x0a, 0x53, 0x74, + 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x57, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x1a, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, + 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x41, 0x0a, 0x0a, 0x44, 0x65, + 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, + 0x09, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, + 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, + 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, + 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x21, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x54, 0x0a, 0x17, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x41, + 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, + 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x4e, 0x0a, 0x10, 0x56, + 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, + 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, + 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x20, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x30, 0x01, 0x12, 0x47, 0x0a, + 0x0c, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, + 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1e, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, + 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, + 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, + 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x53, 0x0a, 0x18, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, + 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x12, 0x1c, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, + 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x11, 0x53, 0x65, - 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, - 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, - 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x25, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x73, - 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x30, 0x01, 0x12, 0x44, 0x0a, 0x0b, - 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, - 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, - 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, - 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 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, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x11, + 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, + 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, + 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x30, 0x01, 0x12, 0x44, + 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x19, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, + 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, + 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x23, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, + 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 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, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -21097,7 +21174,7 @@ func file_lightning_proto_rawDescGZIP() []byte { } var file_lightning_proto_enumTypes = make([]protoimpl.EnumInfo, 21) -var file_lightning_proto_msgTypes = make([]protoimpl.MessageInfo, 225) +var file_lightning_proto_msgTypes = make([]protoimpl.MessageInfo, 226) var file_lightning_proto_goTypes = []interface{}{ (OutputScriptType)(0), // 0: lnrpc.OutputScriptType (CoinSelectionStrategy)(0), // 1: lnrpc.CoinSelectionStrategy @@ -21280,71 +21357,72 @@ var file_lightning_proto_goTypes = []interface{}{ (*FeeReportRequest)(nil), // 178: lnrpc.FeeReportRequest (*ChannelFeeReport)(nil), // 179: lnrpc.ChannelFeeReport (*FeeReportResponse)(nil), // 180: lnrpc.FeeReportResponse - (*PolicyUpdateRequest)(nil), // 181: lnrpc.PolicyUpdateRequest - (*FailedUpdate)(nil), // 182: lnrpc.FailedUpdate - (*PolicyUpdateResponse)(nil), // 183: lnrpc.PolicyUpdateResponse - (*ForwardingHistoryRequest)(nil), // 184: lnrpc.ForwardingHistoryRequest - (*ForwardingEvent)(nil), // 185: lnrpc.ForwardingEvent - (*ForwardingHistoryResponse)(nil), // 186: lnrpc.ForwardingHistoryResponse - (*ExportChannelBackupRequest)(nil), // 187: lnrpc.ExportChannelBackupRequest - (*ChannelBackup)(nil), // 188: lnrpc.ChannelBackup - (*MultiChanBackup)(nil), // 189: lnrpc.MultiChanBackup - (*ChanBackupExportRequest)(nil), // 190: lnrpc.ChanBackupExportRequest - (*ChanBackupSnapshot)(nil), // 191: lnrpc.ChanBackupSnapshot - (*ChannelBackups)(nil), // 192: lnrpc.ChannelBackups - (*RestoreChanBackupRequest)(nil), // 193: lnrpc.RestoreChanBackupRequest - (*RestoreBackupResponse)(nil), // 194: lnrpc.RestoreBackupResponse - (*ChannelBackupSubscription)(nil), // 195: lnrpc.ChannelBackupSubscription - (*VerifyChanBackupResponse)(nil), // 196: lnrpc.VerifyChanBackupResponse - (*MacaroonPermission)(nil), // 197: lnrpc.MacaroonPermission - (*BakeMacaroonRequest)(nil), // 198: lnrpc.BakeMacaroonRequest - (*BakeMacaroonResponse)(nil), // 199: lnrpc.BakeMacaroonResponse - (*ListMacaroonIDsRequest)(nil), // 200: lnrpc.ListMacaroonIDsRequest - (*ListMacaroonIDsResponse)(nil), // 201: lnrpc.ListMacaroonIDsResponse - (*DeleteMacaroonIDRequest)(nil), // 202: lnrpc.DeleteMacaroonIDRequest - (*DeleteMacaroonIDResponse)(nil), // 203: lnrpc.DeleteMacaroonIDResponse - (*MacaroonPermissionList)(nil), // 204: lnrpc.MacaroonPermissionList - (*ListPermissionsRequest)(nil), // 205: lnrpc.ListPermissionsRequest - (*ListPermissionsResponse)(nil), // 206: lnrpc.ListPermissionsResponse - (*Failure)(nil), // 207: lnrpc.Failure - (*ChannelUpdate)(nil), // 208: lnrpc.ChannelUpdate - (*MacaroonId)(nil), // 209: lnrpc.MacaroonId - (*Op)(nil), // 210: lnrpc.Op - (*CheckMacPermRequest)(nil), // 211: lnrpc.CheckMacPermRequest - (*CheckMacPermResponse)(nil), // 212: lnrpc.CheckMacPermResponse - (*RPCMiddlewareRequest)(nil), // 213: lnrpc.RPCMiddlewareRequest - (*StreamAuth)(nil), // 214: lnrpc.StreamAuth - (*RPCMessage)(nil), // 215: lnrpc.RPCMessage - (*RPCMiddlewareResponse)(nil), // 216: lnrpc.RPCMiddlewareResponse - (*MiddlewareRegistration)(nil), // 217: lnrpc.MiddlewareRegistration - (*InterceptFeedback)(nil), // 218: lnrpc.InterceptFeedback - nil, // 219: lnrpc.SendRequest.DestCustomRecordsEntry - nil, // 220: lnrpc.EstimateFeeRequest.AddrToAmountEntry - nil, // 221: lnrpc.SendManyRequest.AddrToAmountEntry - nil, // 222: lnrpc.Peer.FeaturesEntry - nil, // 223: lnrpc.GetInfoResponse.FeaturesEntry - nil, // 224: lnrpc.GetDebugInfoResponse.ConfigEntry - (*PendingChannelsResponse_PendingChannel)(nil), // 225: lnrpc.PendingChannelsResponse.PendingChannel - (*PendingChannelsResponse_PendingOpenChannel)(nil), // 226: lnrpc.PendingChannelsResponse.PendingOpenChannel - (*PendingChannelsResponse_WaitingCloseChannel)(nil), // 227: lnrpc.PendingChannelsResponse.WaitingCloseChannel - (*PendingChannelsResponse_Commitments)(nil), // 228: lnrpc.PendingChannelsResponse.Commitments - (*PendingChannelsResponse_ClosedChannel)(nil), // 229: lnrpc.PendingChannelsResponse.ClosedChannel - (*PendingChannelsResponse_ForceClosedChannel)(nil), // 230: lnrpc.PendingChannelsResponse.ForceClosedChannel - nil, // 231: lnrpc.WalletBalanceResponse.AccountBalanceEntry - nil, // 232: lnrpc.QueryRoutesRequest.DestCustomRecordsEntry - nil, // 233: lnrpc.Hop.CustomRecordsEntry - nil, // 234: lnrpc.LightningNode.FeaturesEntry - nil, // 235: lnrpc.LightningNode.CustomRecordsEntry - nil, // 236: lnrpc.RoutingPolicy.CustomRecordsEntry - nil, // 237: lnrpc.ChannelEdge.CustomRecordsEntry - nil, // 238: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry - nil, // 239: lnrpc.NodeUpdate.FeaturesEntry - nil, // 240: lnrpc.Invoice.FeaturesEntry - nil, // 241: lnrpc.Invoice.AmpInvoiceStateEntry - nil, // 242: lnrpc.InvoiceHTLC.CustomRecordsEntry - nil, // 243: lnrpc.InvoiceHTLC.WireCustomRecordsEntry - nil, // 244: lnrpc.PayReq.FeaturesEntry - nil, // 245: lnrpc.ListPermissionsResponse.MethodPermissionsEntry + (*InboundFee)(nil), // 181: lnrpc.InboundFee + (*PolicyUpdateRequest)(nil), // 182: lnrpc.PolicyUpdateRequest + (*FailedUpdate)(nil), // 183: lnrpc.FailedUpdate + (*PolicyUpdateResponse)(nil), // 184: lnrpc.PolicyUpdateResponse + (*ForwardingHistoryRequest)(nil), // 185: lnrpc.ForwardingHistoryRequest + (*ForwardingEvent)(nil), // 186: lnrpc.ForwardingEvent + (*ForwardingHistoryResponse)(nil), // 187: lnrpc.ForwardingHistoryResponse + (*ExportChannelBackupRequest)(nil), // 188: lnrpc.ExportChannelBackupRequest + (*ChannelBackup)(nil), // 189: lnrpc.ChannelBackup + (*MultiChanBackup)(nil), // 190: lnrpc.MultiChanBackup + (*ChanBackupExportRequest)(nil), // 191: lnrpc.ChanBackupExportRequest + (*ChanBackupSnapshot)(nil), // 192: lnrpc.ChanBackupSnapshot + (*ChannelBackups)(nil), // 193: lnrpc.ChannelBackups + (*RestoreChanBackupRequest)(nil), // 194: lnrpc.RestoreChanBackupRequest + (*RestoreBackupResponse)(nil), // 195: lnrpc.RestoreBackupResponse + (*ChannelBackupSubscription)(nil), // 196: lnrpc.ChannelBackupSubscription + (*VerifyChanBackupResponse)(nil), // 197: lnrpc.VerifyChanBackupResponse + (*MacaroonPermission)(nil), // 198: lnrpc.MacaroonPermission + (*BakeMacaroonRequest)(nil), // 199: lnrpc.BakeMacaroonRequest + (*BakeMacaroonResponse)(nil), // 200: lnrpc.BakeMacaroonResponse + (*ListMacaroonIDsRequest)(nil), // 201: lnrpc.ListMacaroonIDsRequest + (*ListMacaroonIDsResponse)(nil), // 202: lnrpc.ListMacaroonIDsResponse + (*DeleteMacaroonIDRequest)(nil), // 203: lnrpc.DeleteMacaroonIDRequest + (*DeleteMacaroonIDResponse)(nil), // 204: lnrpc.DeleteMacaroonIDResponse + (*MacaroonPermissionList)(nil), // 205: lnrpc.MacaroonPermissionList + (*ListPermissionsRequest)(nil), // 206: lnrpc.ListPermissionsRequest + (*ListPermissionsResponse)(nil), // 207: lnrpc.ListPermissionsResponse + (*Failure)(nil), // 208: lnrpc.Failure + (*ChannelUpdate)(nil), // 209: lnrpc.ChannelUpdate + (*MacaroonId)(nil), // 210: lnrpc.MacaroonId + (*Op)(nil), // 211: lnrpc.Op + (*CheckMacPermRequest)(nil), // 212: lnrpc.CheckMacPermRequest + (*CheckMacPermResponse)(nil), // 213: lnrpc.CheckMacPermResponse + (*RPCMiddlewareRequest)(nil), // 214: lnrpc.RPCMiddlewareRequest + (*StreamAuth)(nil), // 215: lnrpc.StreamAuth + (*RPCMessage)(nil), // 216: lnrpc.RPCMessage + (*RPCMiddlewareResponse)(nil), // 217: lnrpc.RPCMiddlewareResponse + (*MiddlewareRegistration)(nil), // 218: lnrpc.MiddlewareRegistration + (*InterceptFeedback)(nil), // 219: lnrpc.InterceptFeedback + nil, // 220: lnrpc.SendRequest.DestCustomRecordsEntry + nil, // 221: lnrpc.EstimateFeeRequest.AddrToAmountEntry + nil, // 222: lnrpc.SendManyRequest.AddrToAmountEntry + nil, // 223: lnrpc.Peer.FeaturesEntry + nil, // 224: lnrpc.GetInfoResponse.FeaturesEntry + nil, // 225: lnrpc.GetDebugInfoResponse.ConfigEntry + (*PendingChannelsResponse_PendingChannel)(nil), // 226: lnrpc.PendingChannelsResponse.PendingChannel + (*PendingChannelsResponse_PendingOpenChannel)(nil), // 227: lnrpc.PendingChannelsResponse.PendingOpenChannel + (*PendingChannelsResponse_WaitingCloseChannel)(nil), // 228: lnrpc.PendingChannelsResponse.WaitingCloseChannel + (*PendingChannelsResponse_Commitments)(nil), // 229: lnrpc.PendingChannelsResponse.Commitments + (*PendingChannelsResponse_ClosedChannel)(nil), // 230: lnrpc.PendingChannelsResponse.ClosedChannel + (*PendingChannelsResponse_ForceClosedChannel)(nil), // 231: lnrpc.PendingChannelsResponse.ForceClosedChannel + nil, // 232: lnrpc.WalletBalanceResponse.AccountBalanceEntry + nil, // 233: lnrpc.QueryRoutesRequest.DestCustomRecordsEntry + nil, // 234: lnrpc.Hop.CustomRecordsEntry + nil, // 235: lnrpc.LightningNode.FeaturesEntry + nil, // 236: lnrpc.LightningNode.CustomRecordsEntry + nil, // 237: lnrpc.RoutingPolicy.CustomRecordsEntry + nil, // 238: lnrpc.ChannelEdge.CustomRecordsEntry + nil, // 239: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry + nil, // 240: lnrpc.NodeUpdate.FeaturesEntry + nil, // 241: lnrpc.Invoice.FeaturesEntry + nil, // 242: lnrpc.Invoice.AmpInvoiceStateEntry + nil, // 243: lnrpc.InvoiceHTLC.CustomRecordsEntry + nil, // 244: lnrpc.InvoiceHTLC.WireCustomRecordsEntry + nil, // 245: lnrpc.PayReq.FeaturesEntry + nil, // 246: lnrpc.ListPermissionsResponse.MethodPermissionsEntry } var file_lightning_proto_depIdxs = []int32{ 2, // 0: lnrpc.Utxo.address_type:type_name -> lnrpc.AddressType @@ -21354,14 +21432,14 @@ var file_lightning_proto_depIdxs = []int32{ 40, // 4: lnrpc.Transaction.previous_outpoints:type_name -> lnrpc.PreviousOutPoint 29, // 5: lnrpc.TransactionDetails.transactions:type_name -> lnrpc.Transaction 32, // 6: lnrpc.SendRequest.fee_limit:type_name -> lnrpc.FeeLimit - 219, // 7: lnrpc.SendRequest.dest_custom_records:type_name -> lnrpc.SendRequest.DestCustomRecordsEntry + 220, // 7: lnrpc.SendRequest.dest_custom_records:type_name -> lnrpc.SendRequest.DestCustomRecordsEntry 10, // 8: lnrpc.SendRequest.dest_features:type_name -> lnrpc.FeatureBit 126, // 9: lnrpc.SendResponse.payment_route:type_name -> lnrpc.Route 126, // 10: lnrpc.SendToRouteRequest.route:type_name -> lnrpc.Route 3, // 11: lnrpc.ChannelAcceptRequest.commitment_type:type_name -> lnrpc.CommitmentType - 220, // 12: lnrpc.EstimateFeeRequest.AddrToAmount:type_name -> lnrpc.EstimateFeeRequest.AddrToAmountEntry + 221, // 12: lnrpc.EstimateFeeRequest.AddrToAmount:type_name -> lnrpc.EstimateFeeRequest.AddrToAmountEntry 1, // 13: lnrpc.EstimateFeeRequest.coin_selection_strategy:type_name -> lnrpc.CoinSelectionStrategy - 221, // 14: lnrpc.SendManyRequest.AddrToAmount:type_name -> lnrpc.SendManyRequest.AddrToAmountEntry + 222, // 14: lnrpc.SendManyRequest.AddrToAmount:type_name -> lnrpc.SendManyRequest.AddrToAmountEntry 1, // 15: lnrpc.SendManyRequest.coin_selection_strategy:type_name -> lnrpc.CoinSelectionStrategy 1, // 16: lnrpc.SendCoinsRequest.coin_selection_strategy:type_name -> lnrpc.CoinSelectionStrategy 27, // 17: lnrpc.ListUnspentResponse.utxos:type_name -> lnrpc.Utxo @@ -21382,13 +21460,13 @@ var file_lightning_proto_depIdxs = []int32{ 39, // 32: lnrpc.Resolution.outpoint:type_name -> lnrpc.OutPoint 68, // 33: lnrpc.ClosedChannelsResponse.channels:type_name -> lnrpc.ChannelCloseSummary 13, // 34: lnrpc.Peer.sync_type:type_name -> lnrpc.Peer.SyncType - 222, // 35: lnrpc.Peer.features:type_name -> lnrpc.Peer.FeaturesEntry + 223, // 35: lnrpc.Peer.features:type_name -> lnrpc.Peer.FeaturesEntry 73, // 36: lnrpc.Peer.errors:type_name -> lnrpc.TimestampedError 72, // 37: lnrpc.ListPeersResponse.peers:type_name -> lnrpc.Peer 14, // 38: lnrpc.PeerEvent.type:type_name -> lnrpc.PeerEvent.EventType 84, // 39: lnrpc.GetInfoResponse.chains:type_name -> lnrpc.Chain - 223, // 40: lnrpc.GetInfoResponse.features:type_name -> lnrpc.GetInfoResponse.FeaturesEntry - 224, // 41: lnrpc.GetDebugInfoResponse.config:type_name -> lnrpc.GetDebugInfoResponse.ConfigEntry + 224, // 40: lnrpc.GetInfoResponse.features:type_name -> lnrpc.GetInfoResponse.FeaturesEntry + 225, // 41: lnrpc.GetDebugInfoResponse.config:type_name -> lnrpc.GetDebugInfoResponse.ConfigEntry 38, // 42: lnrpc.ChannelOpenUpdate.channel_point:type_name -> lnrpc.ChannelPoint 38, // 43: lnrpc.CloseChannelRequest.channel_point:type_name -> lnrpc.ChannelPoint 90, // 44: lnrpc.CloseStatusUpdate.close_pending:type_name -> lnrpc.PendingUpdate @@ -21413,10 +21491,10 @@ var file_lightning_proto_depIdxs = []int32{ 103, // 63: lnrpc.FundingTransitionMsg.shim_cancel:type_name -> lnrpc.FundingShimCancel 104, // 64: lnrpc.FundingTransitionMsg.psbt_verify:type_name -> lnrpc.FundingPsbtVerify 105, // 65: lnrpc.FundingTransitionMsg.psbt_finalize:type_name -> lnrpc.FundingPsbtFinalize - 226, // 66: lnrpc.PendingChannelsResponse.pending_open_channels:type_name -> lnrpc.PendingChannelsResponse.PendingOpenChannel - 229, // 67: lnrpc.PendingChannelsResponse.pending_closing_channels:type_name -> lnrpc.PendingChannelsResponse.ClosedChannel - 230, // 68: lnrpc.PendingChannelsResponse.pending_force_closing_channels:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel - 227, // 69: lnrpc.PendingChannelsResponse.waiting_close_channels:type_name -> lnrpc.PendingChannelsResponse.WaitingCloseChannel + 227, // 66: lnrpc.PendingChannelsResponse.pending_open_channels:type_name -> lnrpc.PendingChannelsResponse.PendingOpenChannel + 230, // 67: lnrpc.PendingChannelsResponse.pending_closing_channels:type_name -> lnrpc.PendingChannelsResponse.ClosedChannel + 231, // 68: lnrpc.PendingChannelsResponse.pending_force_closing_channels:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel + 228, // 69: lnrpc.PendingChannelsResponse.waiting_close_channels:type_name -> lnrpc.PendingChannelsResponse.WaitingCloseChannel 62, // 70: lnrpc.ChannelEventUpdate.open_channel:type_name -> lnrpc.Channel 68, // 71: lnrpc.ChannelEventUpdate.closed_channel:type_name -> lnrpc.ChannelCloseSummary 38, // 72: lnrpc.ChannelEventUpdate.active_channel:type_name -> lnrpc.ChannelPoint @@ -21424,7 +21502,7 @@ var file_lightning_proto_depIdxs = []int32{ 90, // 74: lnrpc.ChannelEventUpdate.pending_open_channel:type_name -> lnrpc.PendingUpdate 38, // 75: lnrpc.ChannelEventUpdate.fully_resolved_channel:type_name -> lnrpc.ChannelPoint 16, // 76: lnrpc.ChannelEventUpdate.type:type_name -> lnrpc.ChannelEventUpdate.UpdateType - 231, // 77: lnrpc.WalletBalanceResponse.account_balance:type_name -> lnrpc.WalletBalanceResponse.AccountBalanceEntry + 232, // 77: lnrpc.WalletBalanceResponse.account_balance:type_name -> lnrpc.WalletBalanceResponse.AccountBalanceEntry 116, // 78: lnrpc.ChannelBalanceResponse.local_balance:type_name -> lnrpc.Amount 116, // 79: lnrpc.ChannelBalanceResponse.remote_balance:type_name -> lnrpc.Amount 116, // 80: lnrpc.ChannelBalanceResponse.unsettled_local_balance:type_name -> lnrpc.Amount @@ -21434,33 +21512,33 @@ var file_lightning_proto_depIdxs = []int32{ 32, // 84: lnrpc.QueryRoutesRequest.fee_limit:type_name -> lnrpc.FeeLimit 121, // 85: lnrpc.QueryRoutesRequest.ignored_edges:type_name -> lnrpc.EdgeLocator 120, // 86: lnrpc.QueryRoutesRequest.ignored_pairs:type_name -> lnrpc.NodePair - 232, // 87: lnrpc.QueryRoutesRequest.dest_custom_records:type_name -> lnrpc.QueryRoutesRequest.DestCustomRecordsEntry + 233, // 87: lnrpc.QueryRoutesRequest.dest_custom_records:type_name -> lnrpc.QueryRoutesRequest.DestCustomRecordsEntry 150, // 88: lnrpc.QueryRoutesRequest.route_hints:type_name -> lnrpc.RouteHint 151, // 89: lnrpc.QueryRoutesRequest.blinded_payment_paths:type_name -> lnrpc.BlindedPaymentPath 10, // 90: lnrpc.QueryRoutesRequest.dest_features:type_name -> lnrpc.FeatureBit 126, // 91: lnrpc.QueryRoutesResponse.routes:type_name -> lnrpc.Route 124, // 92: lnrpc.Hop.mpp_record:type_name -> lnrpc.MPPRecord 125, // 93: lnrpc.Hop.amp_record:type_name -> lnrpc.AMPRecord - 233, // 94: lnrpc.Hop.custom_records:type_name -> lnrpc.Hop.CustomRecordsEntry + 234, // 94: lnrpc.Hop.custom_records:type_name -> lnrpc.Hop.CustomRecordsEntry 123, // 95: lnrpc.Route.hops:type_name -> lnrpc.Hop 129, // 96: lnrpc.NodeInfo.node:type_name -> lnrpc.LightningNode 132, // 97: lnrpc.NodeInfo.channels:type_name -> lnrpc.ChannelEdge 130, // 98: lnrpc.LightningNode.addresses:type_name -> lnrpc.NodeAddress - 234, // 99: lnrpc.LightningNode.features:type_name -> lnrpc.LightningNode.FeaturesEntry - 235, // 100: lnrpc.LightningNode.custom_records:type_name -> lnrpc.LightningNode.CustomRecordsEntry - 236, // 101: lnrpc.RoutingPolicy.custom_records:type_name -> lnrpc.RoutingPolicy.CustomRecordsEntry + 235, // 99: lnrpc.LightningNode.features:type_name -> lnrpc.LightningNode.FeaturesEntry + 236, // 100: lnrpc.LightningNode.custom_records:type_name -> lnrpc.LightningNode.CustomRecordsEntry + 237, // 101: lnrpc.RoutingPolicy.custom_records:type_name -> lnrpc.RoutingPolicy.CustomRecordsEntry 131, // 102: lnrpc.ChannelEdge.node1_policy:type_name -> lnrpc.RoutingPolicy 131, // 103: lnrpc.ChannelEdge.node2_policy:type_name -> lnrpc.RoutingPolicy - 237, // 104: lnrpc.ChannelEdge.custom_records:type_name -> lnrpc.ChannelEdge.CustomRecordsEntry + 238, // 104: lnrpc.ChannelEdge.custom_records:type_name -> lnrpc.ChannelEdge.CustomRecordsEntry 129, // 105: lnrpc.ChannelGraph.nodes:type_name -> lnrpc.LightningNode 132, // 106: lnrpc.ChannelGraph.edges:type_name -> lnrpc.ChannelEdge 7, // 107: lnrpc.NodeMetricsRequest.types:type_name -> lnrpc.NodeMetricType - 238, // 108: lnrpc.NodeMetricsResponse.betweenness_centrality:type_name -> lnrpc.NodeMetricsResponse.BetweennessCentralityEntry + 239, // 108: lnrpc.NodeMetricsResponse.betweenness_centrality:type_name -> lnrpc.NodeMetricsResponse.BetweennessCentralityEntry 145, // 109: lnrpc.GraphTopologyUpdate.node_updates:type_name -> lnrpc.NodeUpdate 146, // 110: lnrpc.GraphTopologyUpdate.channel_updates:type_name -> lnrpc.ChannelEdgeUpdate 147, // 111: lnrpc.GraphTopologyUpdate.closed_chans:type_name -> lnrpc.ClosedChannelUpdate 130, // 112: lnrpc.NodeUpdate.node_addresses:type_name -> lnrpc.NodeAddress - 239, // 113: lnrpc.NodeUpdate.features:type_name -> lnrpc.NodeUpdate.FeaturesEntry + 240, // 113: lnrpc.NodeUpdate.features:type_name -> lnrpc.NodeUpdate.FeaturesEntry 38, // 114: lnrpc.ChannelEdgeUpdate.chan_point:type_name -> lnrpc.ChannelPoint 131, // 115: lnrpc.ChannelEdgeUpdate.routing_policy:type_name -> lnrpc.RoutingPolicy 38, // 116: lnrpc.ClosedChannelUpdate.chan_point:type_name -> lnrpc.ChannelPoint @@ -21472,208 +21550,209 @@ var file_lightning_proto_depIdxs = []int32{ 150, // 122: lnrpc.Invoice.route_hints:type_name -> lnrpc.RouteHint 17, // 123: lnrpc.Invoice.state:type_name -> lnrpc.Invoice.InvoiceState 156, // 124: lnrpc.Invoice.htlcs:type_name -> lnrpc.InvoiceHTLC - 240, // 125: lnrpc.Invoice.features:type_name -> lnrpc.Invoice.FeaturesEntry - 241, // 126: lnrpc.Invoice.amp_invoice_state:type_name -> lnrpc.Invoice.AmpInvoiceStateEntry + 241, // 125: lnrpc.Invoice.features:type_name -> lnrpc.Invoice.FeaturesEntry + 242, // 126: lnrpc.Invoice.amp_invoice_state:type_name -> lnrpc.Invoice.AmpInvoiceStateEntry 8, // 127: lnrpc.InvoiceHTLC.state:type_name -> lnrpc.InvoiceHTLCState - 242, // 128: lnrpc.InvoiceHTLC.custom_records:type_name -> lnrpc.InvoiceHTLC.CustomRecordsEntry + 243, // 128: lnrpc.InvoiceHTLC.custom_records:type_name -> lnrpc.InvoiceHTLC.CustomRecordsEntry 157, // 129: lnrpc.InvoiceHTLC.amp:type_name -> lnrpc.AMP - 243, // 130: lnrpc.InvoiceHTLC.wire_custom_records:type_name -> lnrpc.InvoiceHTLC.WireCustomRecordsEntry + 244, // 130: lnrpc.InvoiceHTLC.wire_custom_records:type_name -> lnrpc.InvoiceHTLC.WireCustomRecordsEntry 155, // 131: lnrpc.ListInvoiceResponse.invoices:type_name -> lnrpc.Invoice 18, // 132: lnrpc.Payment.status:type_name -> lnrpc.Payment.PaymentStatus 164, // 133: lnrpc.Payment.htlcs:type_name -> lnrpc.HTLCAttempt 9, // 134: lnrpc.Payment.failure_reason:type_name -> lnrpc.PaymentFailureReason 19, // 135: lnrpc.HTLCAttempt.status:type_name -> lnrpc.HTLCAttempt.HTLCStatus 126, // 136: lnrpc.HTLCAttempt.route:type_name -> lnrpc.Route - 207, // 137: lnrpc.HTLCAttempt.failure:type_name -> lnrpc.Failure + 208, // 137: lnrpc.HTLCAttempt.failure:type_name -> lnrpc.Failure 163, // 138: lnrpc.ListPaymentsResponse.payments:type_name -> lnrpc.Payment 38, // 139: lnrpc.AbandonChannelRequest.channel_point:type_name -> lnrpc.ChannelPoint 150, // 140: lnrpc.PayReq.route_hints:type_name -> lnrpc.RouteHint - 244, // 141: lnrpc.PayReq.features:type_name -> lnrpc.PayReq.FeaturesEntry + 245, // 141: lnrpc.PayReq.features:type_name -> lnrpc.PayReq.FeaturesEntry 179, // 142: lnrpc.FeeReportResponse.channel_fees:type_name -> lnrpc.ChannelFeeReport 38, // 143: lnrpc.PolicyUpdateRequest.chan_point:type_name -> lnrpc.ChannelPoint - 39, // 144: lnrpc.FailedUpdate.outpoint:type_name -> lnrpc.OutPoint - 11, // 145: lnrpc.FailedUpdate.reason:type_name -> lnrpc.UpdateFailure - 182, // 146: lnrpc.PolicyUpdateResponse.failed_updates:type_name -> lnrpc.FailedUpdate - 185, // 147: lnrpc.ForwardingHistoryResponse.forwarding_events:type_name -> lnrpc.ForwardingEvent - 38, // 148: lnrpc.ExportChannelBackupRequest.chan_point:type_name -> lnrpc.ChannelPoint - 38, // 149: lnrpc.ChannelBackup.chan_point:type_name -> lnrpc.ChannelPoint - 38, // 150: lnrpc.MultiChanBackup.chan_points:type_name -> lnrpc.ChannelPoint - 192, // 151: lnrpc.ChanBackupSnapshot.single_chan_backups:type_name -> lnrpc.ChannelBackups - 189, // 152: lnrpc.ChanBackupSnapshot.multi_chan_backup:type_name -> lnrpc.MultiChanBackup - 188, // 153: lnrpc.ChannelBackups.chan_backups:type_name -> lnrpc.ChannelBackup - 192, // 154: lnrpc.RestoreChanBackupRequest.chan_backups:type_name -> lnrpc.ChannelBackups - 197, // 155: lnrpc.BakeMacaroonRequest.permissions:type_name -> lnrpc.MacaroonPermission - 197, // 156: lnrpc.MacaroonPermissionList.permissions:type_name -> lnrpc.MacaroonPermission - 245, // 157: lnrpc.ListPermissionsResponse.method_permissions:type_name -> lnrpc.ListPermissionsResponse.MethodPermissionsEntry - 20, // 158: lnrpc.Failure.code:type_name -> lnrpc.Failure.FailureCode - 208, // 159: lnrpc.Failure.channel_update:type_name -> lnrpc.ChannelUpdate - 210, // 160: lnrpc.MacaroonId.ops:type_name -> lnrpc.Op - 197, // 161: lnrpc.CheckMacPermRequest.permissions:type_name -> lnrpc.MacaroonPermission - 214, // 162: lnrpc.RPCMiddlewareRequest.stream_auth:type_name -> lnrpc.StreamAuth - 215, // 163: lnrpc.RPCMiddlewareRequest.request:type_name -> lnrpc.RPCMessage - 215, // 164: lnrpc.RPCMiddlewareRequest.response:type_name -> lnrpc.RPCMessage - 217, // 165: lnrpc.RPCMiddlewareResponse.register:type_name -> lnrpc.MiddlewareRegistration - 218, // 166: lnrpc.RPCMiddlewareResponse.feedback:type_name -> lnrpc.InterceptFeedback - 177, // 167: lnrpc.Peer.FeaturesEntry.value:type_name -> lnrpc.Feature - 177, // 168: lnrpc.GetInfoResponse.FeaturesEntry.value:type_name -> lnrpc.Feature - 4, // 169: lnrpc.PendingChannelsResponse.PendingChannel.initiator:type_name -> lnrpc.Initiator - 3, // 170: lnrpc.PendingChannelsResponse.PendingChannel.commitment_type:type_name -> lnrpc.CommitmentType - 225, // 171: lnrpc.PendingChannelsResponse.PendingOpenChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel - 225, // 172: lnrpc.PendingChannelsResponse.WaitingCloseChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel - 228, // 173: lnrpc.PendingChannelsResponse.WaitingCloseChannel.commitments:type_name -> lnrpc.PendingChannelsResponse.Commitments - 225, // 174: lnrpc.PendingChannelsResponse.ClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel - 225, // 175: lnrpc.PendingChannelsResponse.ForceClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel - 108, // 176: lnrpc.PendingChannelsResponse.ForceClosedChannel.pending_htlcs:type_name -> lnrpc.PendingHTLC - 15, // 177: lnrpc.PendingChannelsResponse.ForceClosedChannel.anchor:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel.AnchorState - 113, // 178: lnrpc.WalletBalanceResponse.AccountBalanceEntry.value:type_name -> lnrpc.WalletAccountBalance - 177, // 179: lnrpc.LightningNode.FeaturesEntry.value:type_name -> lnrpc.Feature - 137, // 180: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry.value:type_name -> lnrpc.FloatMetric - 177, // 181: lnrpc.NodeUpdate.FeaturesEntry.value:type_name -> lnrpc.Feature - 177, // 182: lnrpc.Invoice.FeaturesEntry.value:type_name -> lnrpc.Feature - 154, // 183: lnrpc.Invoice.AmpInvoiceStateEntry.value:type_name -> lnrpc.AMPInvoiceState - 177, // 184: lnrpc.PayReq.FeaturesEntry.value:type_name -> lnrpc.Feature - 204, // 185: lnrpc.ListPermissionsResponse.MethodPermissionsEntry.value:type_name -> lnrpc.MacaroonPermissionList - 114, // 186: lnrpc.Lightning.WalletBalance:input_type -> lnrpc.WalletBalanceRequest - 117, // 187: lnrpc.Lightning.ChannelBalance:input_type -> lnrpc.ChannelBalanceRequest - 30, // 188: lnrpc.Lightning.GetTransactions:input_type -> lnrpc.GetTransactionsRequest - 42, // 189: lnrpc.Lightning.EstimateFee:input_type -> lnrpc.EstimateFeeRequest - 46, // 190: lnrpc.Lightning.SendCoins:input_type -> lnrpc.SendCoinsRequest - 48, // 191: lnrpc.Lightning.ListUnspent:input_type -> lnrpc.ListUnspentRequest - 30, // 192: lnrpc.Lightning.SubscribeTransactions:input_type -> lnrpc.GetTransactionsRequest - 44, // 193: lnrpc.Lightning.SendMany:input_type -> lnrpc.SendManyRequest - 50, // 194: lnrpc.Lightning.NewAddress:input_type -> lnrpc.NewAddressRequest - 52, // 195: lnrpc.Lightning.SignMessage:input_type -> lnrpc.SignMessageRequest - 54, // 196: lnrpc.Lightning.VerifyMessage:input_type -> lnrpc.VerifyMessageRequest - 56, // 197: lnrpc.Lightning.ConnectPeer:input_type -> lnrpc.ConnectPeerRequest - 58, // 198: lnrpc.Lightning.DisconnectPeer:input_type -> lnrpc.DisconnectPeerRequest - 74, // 199: lnrpc.Lightning.ListPeers:input_type -> lnrpc.ListPeersRequest - 76, // 200: lnrpc.Lightning.SubscribePeerEvents:input_type -> lnrpc.PeerEventSubscription - 78, // 201: lnrpc.Lightning.GetInfo:input_type -> lnrpc.GetInfoRequest - 80, // 202: lnrpc.Lightning.GetDebugInfo:input_type -> lnrpc.GetDebugInfoRequest - 82, // 203: lnrpc.Lightning.GetRecoveryInfo:input_type -> lnrpc.GetRecoveryInfoRequest - 109, // 204: lnrpc.Lightning.PendingChannels:input_type -> lnrpc.PendingChannelsRequest - 63, // 205: lnrpc.Lightning.ListChannels:input_type -> lnrpc.ListChannelsRequest - 111, // 206: lnrpc.Lightning.SubscribeChannelEvents:input_type -> lnrpc.ChannelEventSubscription - 70, // 207: lnrpc.Lightning.ClosedChannels:input_type -> lnrpc.ClosedChannelsRequest - 96, // 208: lnrpc.Lightning.OpenChannelSync:input_type -> lnrpc.OpenChannelRequest - 96, // 209: lnrpc.Lightning.OpenChannel:input_type -> lnrpc.OpenChannelRequest - 93, // 210: lnrpc.Lightning.BatchOpenChannel:input_type -> lnrpc.BatchOpenChannelRequest - 106, // 211: lnrpc.Lightning.FundingStateStep:input_type -> lnrpc.FundingTransitionMsg - 37, // 212: lnrpc.Lightning.ChannelAcceptor:input_type -> lnrpc.ChannelAcceptResponse - 88, // 213: lnrpc.Lightning.CloseChannel:input_type -> lnrpc.CloseChannelRequest - 171, // 214: lnrpc.Lightning.AbandonChannel:input_type -> lnrpc.AbandonChannelRequest - 33, // 215: lnrpc.Lightning.SendPayment:input_type -> lnrpc.SendRequest - 33, // 216: lnrpc.Lightning.SendPaymentSync:input_type -> lnrpc.SendRequest - 35, // 217: lnrpc.Lightning.SendToRoute:input_type -> lnrpc.SendToRouteRequest - 35, // 218: lnrpc.Lightning.SendToRouteSync:input_type -> lnrpc.SendToRouteRequest - 155, // 219: lnrpc.Lightning.AddInvoice:input_type -> lnrpc.Invoice - 160, // 220: lnrpc.Lightning.ListInvoices:input_type -> lnrpc.ListInvoiceRequest - 159, // 221: lnrpc.Lightning.LookupInvoice:input_type -> lnrpc.PaymentHash - 162, // 222: lnrpc.Lightning.SubscribeInvoices:input_type -> lnrpc.InvoiceSubscription - 175, // 223: lnrpc.Lightning.DecodePayReq:input_type -> lnrpc.PayReqString - 165, // 224: lnrpc.Lightning.ListPayments:input_type -> lnrpc.ListPaymentsRequest - 167, // 225: lnrpc.Lightning.DeletePayment:input_type -> lnrpc.DeletePaymentRequest - 168, // 226: lnrpc.Lightning.DeleteAllPayments:input_type -> lnrpc.DeleteAllPaymentsRequest - 133, // 227: lnrpc.Lightning.DescribeGraph:input_type -> lnrpc.ChannelGraphRequest - 135, // 228: lnrpc.Lightning.GetNodeMetrics:input_type -> lnrpc.NodeMetricsRequest - 138, // 229: lnrpc.Lightning.GetChanInfo:input_type -> lnrpc.ChanInfoRequest - 127, // 230: lnrpc.Lightning.GetNodeInfo:input_type -> lnrpc.NodeInfoRequest - 119, // 231: lnrpc.Lightning.QueryRoutes:input_type -> lnrpc.QueryRoutesRequest - 139, // 232: lnrpc.Lightning.GetNetworkInfo:input_type -> lnrpc.NetworkInfoRequest - 141, // 233: lnrpc.Lightning.StopDaemon:input_type -> lnrpc.StopRequest - 143, // 234: lnrpc.Lightning.SubscribeChannelGraph:input_type -> lnrpc.GraphTopologySubscription - 173, // 235: lnrpc.Lightning.DebugLevel:input_type -> lnrpc.DebugLevelRequest - 178, // 236: lnrpc.Lightning.FeeReport:input_type -> lnrpc.FeeReportRequest - 181, // 237: lnrpc.Lightning.UpdateChannelPolicy:input_type -> lnrpc.PolicyUpdateRequest - 184, // 238: lnrpc.Lightning.ForwardingHistory:input_type -> lnrpc.ForwardingHistoryRequest - 187, // 239: lnrpc.Lightning.ExportChannelBackup:input_type -> lnrpc.ExportChannelBackupRequest - 190, // 240: lnrpc.Lightning.ExportAllChannelBackups:input_type -> lnrpc.ChanBackupExportRequest - 191, // 241: lnrpc.Lightning.VerifyChanBackup:input_type -> lnrpc.ChanBackupSnapshot - 193, // 242: lnrpc.Lightning.RestoreChannelBackups:input_type -> lnrpc.RestoreChanBackupRequest - 195, // 243: lnrpc.Lightning.SubscribeChannelBackups:input_type -> lnrpc.ChannelBackupSubscription - 198, // 244: lnrpc.Lightning.BakeMacaroon:input_type -> lnrpc.BakeMacaroonRequest - 200, // 245: lnrpc.Lightning.ListMacaroonIDs:input_type -> lnrpc.ListMacaroonIDsRequest - 202, // 246: lnrpc.Lightning.DeleteMacaroonID:input_type -> lnrpc.DeleteMacaroonIDRequest - 205, // 247: lnrpc.Lightning.ListPermissions:input_type -> lnrpc.ListPermissionsRequest - 211, // 248: lnrpc.Lightning.CheckMacaroonPermissions:input_type -> lnrpc.CheckMacPermRequest - 216, // 249: lnrpc.Lightning.RegisterRPCMiddleware:input_type -> lnrpc.RPCMiddlewareResponse - 25, // 250: lnrpc.Lightning.SendCustomMessage:input_type -> lnrpc.SendCustomMessageRequest - 23, // 251: lnrpc.Lightning.SubscribeCustomMessages:input_type -> lnrpc.SubscribeCustomMessagesRequest - 66, // 252: lnrpc.Lightning.ListAliases:input_type -> lnrpc.ListAliasesRequest - 21, // 253: lnrpc.Lightning.LookupHtlcResolution:input_type -> lnrpc.LookupHtlcResolutionRequest - 115, // 254: lnrpc.Lightning.WalletBalance:output_type -> lnrpc.WalletBalanceResponse - 118, // 255: lnrpc.Lightning.ChannelBalance:output_type -> lnrpc.ChannelBalanceResponse - 31, // 256: lnrpc.Lightning.GetTransactions:output_type -> lnrpc.TransactionDetails - 43, // 257: lnrpc.Lightning.EstimateFee:output_type -> lnrpc.EstimateFeeResponse - 47, // 258: lnrpc.Lightning.SendCoins:output_type -> lnrpc.SendCoinsResponse - 49, // 259: lnrpc.Lightning.ListUnspent:output_type -> lnrpc.ListUnspentResponse - 29, // 260: lnrpc.Lightning.SubscribeTransactions:output_type -> lnrpc.Transaction - 45, // 261: lnrpc.Lightning.SendMany:output_type -> lnrpc.SendManyResponse - 51, // 262: lnrpc.Lightning.NewAddress:output_type -> lnrpc.NewAddressResponse - 53, // 263: lnrpc.Lightning.SignMessage:output_type -> lnrpc.SignMessageResponse - 55, // 264: lnrpc.Lightning.VerifyMessage:output_type -> lnrpc.VerifyMessageResponse - 57, // 265: lnrpc.Lightning.ConnectPeer:output_type -> lnrpc.ConnectPeerResponse - 59, // 266: lnrpc.Lightning.DisconnectPeer:output_type -> lnrpc.DisconnectPeerResponse - 75, // 267: lnrpc.Lightning.ListPeers:output_type -> lnrpc.ListPeersResponse - 77, // 268: lnrpc.Lightning.SubscribePeerEvents:output_type -> lnrpc.PeerEvent - 79, // 269: lnrpc.Lightning.GetInfo:output_type -> lnrpc.GetInfoResponse - 81, // 270: lnrpc.Lightning.GetDebugInfo:output_type -> lnrpc.GetDebugInfoResponse - 83, // 271: lnrpc.Lightning.GetRecoveryInfo:output_type -> lnrpc.GetRecoveryInfoResponse - 110, // 272: lnrpc.Lightning.PendingChannels:output_type -> lnrpc.PendingChannelsResponse - 64, // 273: lnrpc.Lightning.ListChannels:output_type -> lnrpc.ListChannelsResponse - 112, // 274: lnrpc.Lightning.SubscribeChannelEvents:output_type -> lnrpc.ChannelEventUpdate - 71, // 275: lnrpc.Lightning.ClosedChannels:output_type -> lnrpc.ClosedChannelsResponse - 38, // 276: lnrpc.Lightning.OpenChannelSync:output_type -> lnrpc.ChannelPoint - 97, // 277: lnrpc.Lightning.OpenChannel:output_type -> lnrpc.OpenStatusUpdate - 95, // 278: lnrpc.Lightning.BatchOpenChannel:output_type -> lnrpc.BatchOpenChannelResponse - 107, // 279: lnrpc.Lightning.FundingStateStep:output_type -> lnrpc.FundingStateStepResp - 36, // 280: lnrpc.Lightning.ChannelAcceptor:output_type -> lnrpc.ChannelAcceptRequest - 89, // 281: lnrpc.Lightning.CloseChannel:output_type -> lnrpc.CloseStatusUpdate - 172, // 282: lnrpc.Lightning.AbandonChannel:output_type -> lnrpc.AbandonChannelResponse - 34, // 283: lnrpc.Lightning.SendPayment:output_type -> lnrpc.SendResponse - 34, // 284: lnrpc.Lightning.SendPaymentSync:output_type -> lnrpc.SendResponse - 34, // 285: lnrpc.Lightning.SendToRoute:output_type -> lnrpc.SendResponse - 34, // 286: lnrpc.Lightning.SendToRouteSync:output_type -> lnrpc.SendResponse - 158, // 287: lnrpc.Lightning.AddInvoice:output_type -> lnrpc.AddInvoiceResponse - 161, // 288: lnrpc.Lightning.ListInvoices:output_type -> lnrpc.ListInvoiceResponse - 155, // 289: lnrpc.Lightning.LookupInvoice:output_type -> lnrpc.Invoice - 155, // 290: lnrpc.Lightning.SubscribeInvoices:output_type -> lnrpc.Invoice - 176, // 291: lnrpc.Lightning.DecodePayReq:output_type -> lnrpc.PayReq - 166, // 292: lnrpc.Lightning.ListPayments:output_type -> lnrpc.ListPaymentsResponse - 169, // 293: lnrpc.Lightning.DeletePayment:output_type -> lnrpc.DeletePaymentResponse - 170, // 294: lnrpc.Lightning.DeleteAllPayments:output_type -> lnrpc.DeleteAllPaymentsResponse - 134, // 295: lnrpc.Lightning.DescribeGraph:output_type -> lnrpc.ChannelGraph - 136, // 296: lnrpc.Lightning.GetNodeMetrics:output_type -> lnrpc.NodeMetricsResponse - 132, // 297: lnrpc.Lightning.GetChanInfo:output_type -> lnrpc.ChannelEdge - 128, // 298: lnrpc.Lightning.GetNodeInfo:output_type -> lnrpc.NodeInfo - 122, // 299: lnrpc.Lightning.QueryRoutes:output_type -> lnrpc.QueryRoutesResponse - 140, // 300: lnrpc.Lightning.GetNetworkInfo:output_type -> lnrpc.NetworkInfo - 142, // 301: lnrpc.Lightning.StopDaemon:output_type -> lnrpc.StopResponse - 144, // 302: lnrpc.Lightning.SubscribeChannelGraph:output_type -> lnrpc.GraphTopologyUpdate - 174, // 303: lnrpc.Lightning.DebugLevel:output_type -> lnrpc.DebugLevelResponse - 180, // 304: lnrpc.Lightning.FeeReport:output_type -> lnrpc.FeeReportResponse - 183, // 305: lnrpc.Lightning.UpdateChannelPolicy:output_type -> lnrpc.PolicyUpdateResponse - 186, // 306: lnrpc.Lightning.ForwardingHistory:output_type -> lnrpc.ForwardingHistoryResponse - 188, // 307: lnrpc.Lightning.ExportChannelBackup:output_type -> lnrpc.ChannelBackup - 191, // 308: lnrpc.Lightning.ExportAllChannelBackups:output_type -> lnrpc.ChanBackupSnapshot - 196, // 309: lnrpc.Lightning.VerifyChanBackup:output_type -> lnrpc.VerifyChanBackupResponse - 194, // 310: lnrpc.Lightning.RestoreChannelBackups:output_type -> lnrpc.RestoreBackupResponse - 191, // 311: lnrpc.Lightning.SubscribeChannelBackups:output_type -> lnrpc.ChanBackupSnapshot - 199, // 312: lnrpc.Lightning.BakeMacaroon:output_type -> lnrpc.BakeMacaroonResponse - 201, // 313: lnrpc.Lightning.ListMacaroonIDs:output_type -> lnrpc.ListMacaroonIDsResponse - 203, // 314: lnrpc.Lightning.DeleteMacaroonID:output_type -> lnrpc.DeleteMacaroonIDResponse - 206, // 315: lnrpc.Lightning.ListPermissions:output_type -> lnrpc.ListPermissionsResponse - 212, // 316: lnrpc.Lightning.CheckMacaroonPermissions:output_type -> lnrpc.CheckMacPermResponse - 213, // 317: lnrpc.Lightning.RegisterRPCMiddleware:output_type -> lnrpc.RPCMiddlewareRequest - 26, // 318: lnrpc.Lightning.SendCustomMessage:output_type -> lnrpc.SendCustomMessageResponse - 24, // 319: lnrpc.Lightning.SubscribeCustomMessages:output_type -> lnrpc.CustomMessage - 67, // 320: lnrpc.Lightning.ListAliases:output_type -> lnrpc.ListAliasesResponse - 22, // 321: lnrpc.Lightning.LookupHtlcResolution:output_type -> lnrpc.LookupHtlcResolutionResponse - 254, // [254:322] is the sub-list for method output_type - 186, // [186:254] is the sub-list for method input_type - 186, // [186:186] is the sub-list for extension type_name - 186, // [186:186] is the sub-list for extension extendee - 0, // [0:186] is the sub-list for field type_name + 181, // 144: lnrpc.PolicyUpdateRequest.inbound_fee:type_name -> lnrpc.InboundFee + 39, // 145: lnrpc.FailedUpdate.outpoint:type_name -> lnrpc.OutPoint + 11, // 146: lnrpc.FailedUpdate.reason:type_name -> lnrpc.UpdateFailure + 183, // 147: lnrpc.PolicyUpdateResponse.failed_updates:type_name -> lnrpc.FailedUpdate + 186, // 148: lnrpc.ForwardingHistoryResponse.forwarding_events:type_name -> lnrpc.ForwardingEvent + 38, // 149: lnrpc.ExportChannelBackupRequest.chan_point:type_name -> lnrpc.ChannelPoint + 38, // 150: lnrpc.ChannelBackup.chan_point:type_name -> lnrpc.ChannelPoint + 38, // 151: lnrpc.MultiChanBackup.chan_points:type_name -> lnrpc.ChannelPoint + 193, // 152: lnrpc.ChanBackupSnapshot.single_chan_backups:type_name -> lnrpc.ChannelBackups + 190, // 153: lnrpc.ChanBackupSnapshot.multi_chan_backup:type_name -> lnrpc.MultiChanBackup + 189, // 154: lnrpc.ChannelBackups.chan_backups:type_name -> lnrpc.ChannelBackup + 193, // 155: lnrpc.RestoreChanBackupRequest.chan_backups:type_name -> lnrpc.ChannelBackups + 198, // 156: lnrpc.BakeMacaroonRequest.permissions:type_name -> lnrpc.MacaroonPermission + 198, // 157: lnrpc.MacaroonPermissionList.permissions:type_name -> lnrpc.MacaroonPermission + 246, // 158: lnrpc.ListPermissionsResponse.method_permissions:type_name -> lnrpc.ListPermissionsResponse.MethodPermissionsEntry + 20, // 159: lnrpc.Failure.code:type_name -> lnrpc.Failure.FailureCode + 209, // 160: lnrpc.Failure.channel_update:type_name -> lnrpc.ChannelUpdate + 211, // 161: lnrpc.MacaroonId.ops:type_name -> lnrpc.Op + 198, // 162: lnrpc.CheckMacPermRequest.permissions:type_name -> lnrpc.MacaroonPermission + 215, // 163: lnrpc.RPCMiddlewareRequest.stream_auth:type_name -> lnrpc.StreamAuth + 216, // 164: lnrpc.RPCMiddlewareRequest.request:type_name -> lnrpc.RPCMessage + 216, // 165: lnrpc.RPCMiddlewareRequest.response:type_name -> lnrpc.RPCMessage + 218, // 166: lnrpc.RPCMiddlewareResponse.register:type_name -> lnrpc.MiddlewareRegistration + 219, // 167: lnrpc.RPCMiddlewareResponse.feedback:type_name -> lnrpc.InterceptFeedback + 177, // 168: lnrpc.Peer.FeaturesEntry.value:type_name -> lnrpc.Feature + 177, // 169: lnrpc.GetInfoResponse.FeaturesEntry.value:type_name -> lnrpc.Feature + 4, // 170: lnrpc.PendingChannelsResponse.PendingChannel.initiator:type_name -> lnrpc.Initiator + 3, // 171: lnrpc.PendingChannelsResponse.PendingChannel.commitment_type:type_name -> lnrpc.CommitmentType + 226, // 172: lnrpc.PendingChannelsResponse.PendingOpenChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel + 226, // 173: lnrpc.PendingChannelsResponse.WaitingCloseChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel + 229, // 174: lnrpc.PendingChannelsResponse.WaitingCloseChannel.commitments:type_name -> lnrpc.PendingChannelsResponse.Commitments + 226, // 175: lnrpc.PendingChannelsResponse.ClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel + 226, // 176: lnrpc.PendingChannelsResponse.ForceClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel + 108, // 177: lnrpc.PendingChannelsResponse.ForceClosedChannel.pending_htlcs:type_name -> lnrpc.PendingHTLC + 15, // 178: lnrpc.PendingChannelsResponse.ForceClosedChannel.anchor:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel.AnchorState + 113, // 179: lnrpc.WalletBalanceResponse.AccountBalanceEntry.value:type_name -> lnrpc.WalletAccountBalance + 177, // 180: lnrpc.LightningNode.FeaturesEntry.value:type_name -> lnrpc.Feature + 137, // 181: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry.value:type_name -> lnrpc.FloatMetric + 177, // 182: lnrpc.NodeUpdate.FeaturesEntry.value:type_name -> lnrpc.Feature + 177, // 183: lnrpc.Invoice.FeaturesEntry.value:type_name -> lnrpc.Feature + 154, // 184: lnrpc.Invoice.AmpInvoiceStateEntry.value:type_name -> lnrpc.AMPInvoiceState + 177, // 185: lnrpc.PayReq.FeaturesEntry.value:type_name -> lnrpc.Feature + 205, // 186: lnrpc.ListPermissionsResponse.MethodPermissionsEntry.value:type_name -> lnrpc.MacaroonPermissionList + 114, // 187: lnrpc.Lightning.WalletBalance:input_type -> lnrpc.WalletBalanceRequest + 117, // 188: lnrpc.Lightning.ChannelBalance:input_type -> lnrpc.ChannelBalanceRequest + 30, // 189: lnrpc.Lightning.GetTransactions:input_type -> lnrpc.GetTransactionsRequest + 42, // 190: lnrpc.Lightning.EstimateFee:input_type -> lnrpc.EstimateFeeRequest + 46, // 191: lnrpc.Lightning.SendCoins:input_type -> lnrpc.SendCoinsRequest + 48, // 192: lnrpc.Lightning.ListUnspent:input_type -> lnrpc.ListUnspentRequest + 30, // 193: lnrpc.Lightning.SubscribeTransactions:input_type -> lnrpc.GetTransactionsRequest + 44, // 194: lnrpc.Lightning.SendMany:input_type -> lnrpc.SendManyRequest + 50, // 195: lnrpc.Lightning.NewAddress:input_type -> lnrpc.NewAddressRequest + 52, // 196: lnrpc.Lightning.SignMessage:input_type -> lnrpc.SignMessageRequest + 54, // 197: lnrpc.Lightning.VerifyMessage:input_type -> lnrpc.VerifyMessageRequest + 56, // 198: lnrpc.Lightning.ConnectPeer:input_type -> lnrpc.ConnectPeerRequest + 58, // 199: lnrpc.Lightning.DisconnectPeer:input_type -> lnrpc.DisconnectPeerRequest + 74, // 200: lnrpc.Lightning.ListPeers:input_type -> lnrpc.ListPeersRequest + 76, // 201: lnrpc.Lightning.SubscribePeerEvents:input_type -> lnrpc.PeerEventSubscription + 78, // 202: lnrpc.Lightning.GetInfo:input_type -> lnrpc.GetInfoRequest + 80, // 203: lnrpc.Lightning.GetDebugInfo:input_type -> lnrpc.GetDebugInfoRequest + 82, // 204: lnrpc.Lightning.GetRecoveryInfo:input_type -> lnrpc.GetRecoveryInfoRequest + 109, // 205: lnrpc.Lightning.PendingChannels:input_type -> lnrpc.PendingChannelsRequest + 63, // 206: lnrpc.Lightning.ListChannels:input_type -> lnrpc.ListChannelsRequest + 111, // 207: lnrpc.Lightning.SubscribeChannelEvents:input_type -> lnrpc.ChannelEventSubscription + 70, // 208: lnrpc.Lightning.ClosedChannels:input_type -> lnrpc.ClosedChannelsRequest + 96, // 209: lnrpc.Lightning.OpenChannelSync:input_type -> lnrpc.OpenChannelRequest + 96, // 210: lnrpc.Lightning.OpenChannel:input_type -> lnrpc.OpenChannelRequest + 93, // 211: lnrpc.Lightning.BatchOpenChannel:input_type -> lnrpc.BatchOpenChannelRequest + 106, // 212: lnrpc.Lightning.FundingStateStep:input_type -> lnrpc.FundingTransitionMsg + 37, // 213: lnrpc.Lightning.ChannelAcceptor:input_type -> lnrpc.ChannelAcceptResponse + 88, // 214: lnrpc.Lightning.CloseChannel:input_type -> lnrpc.CloseChannelRequest + 171, // 215: lnrpc.Lightning.AbandonChannel:input_type -> lnrpc.AbandonChannelRequest + 33, // 216: lnrpc.Lightning.SendPayment:input_type -> lnrpc.SendRequest + 33, // 217: lnrpc.Lightning.SendPaymentSync:input_type -> lnrpc.SendRequest + 35, // 218: lnrpc.Lightning.SendToRoute:input_type -> lnrpc.SendToRouteRequest + 35, // 219: lnrpc.Lightning.SendToRouteSync:input_type -> lnrpc.SendToRouteRequest + 155, // 220: lnrpc.Lightning.AddInvoice:input_type -> lnrpc.Invoice + 160, // 221: lnrpc.Lightning.ListInvoices:input_type -> lnrpc.ListInvoiceRequest + 159, // 222: lnrpc.Lightning.LookupInvoice:input_type -> lnrpc.PaymentHash + 162, // 223: lnrpc.Lightning.SubscribeInvoices:input_type -> lnrpc.InvoiceSubscription + 175, // 224: lnrpc.Lightning.DecodePayReq:input_type -> lnrpc.PayReqString + 165, // 225: lnrpc.Lightning.ListPayments:input_type -> lnrpc.ListPaymentsRequest + 167, // 226: lnrpc.Lightning.DeletePayment:input_type -> lnrpc.DeletePaymentRequest + 168, // 227: lnrpc.Lightning.DeleteAllPayments:input_type -> lnrpc.DeleteAllPaymentsRequest + 133, // 228: lnrpc.Lightning.DescribeGraph:input_type -> lnrpc.ChannelGraphRequest + 135, // 229: lnrpc.Lightning.GetNodeMetrics:input_type -> lnrpc.NodeMetricsRequest + 138, // 230: lnrpc.Lightning.GetChanInfo:input_type -> lnrpc.ChanInfoRequest + 127, // 231: lnrpc.Lightning.GetNodeInfo:input_type -> lnrpc.NodeInfoRequest + 119, // 232: lnrpc.Lightning.QueryRoutes:input_type -> lnrpc.QueryRoutesRequest + 139, // 233: lnrpc.Lightning.GetNetworkInfo:input_type -> lnrpc.NetworkInfoRequest + 141, // 234: lnrpc.Lightning.StopDaemon:input_type -> lnrpc.StopRequest + 143, // 235: lnrpc.Lightning.SubscribeChannelGraph:input_type -> lnrpc.GraphTopologySubscription + 173, // 236: lnrpc.Lightning.DebugLevel:input_type -> lnrpc.DebugLevelRequest + 178, // 237: lnrpc.Lightning.FeeReport:input_type -> lnrpc.FeeReportRequest + 182, // 238: lnrpc.Lightning.UpdateChannelPolicy:input_type -> lnrpc.PolicyUpdateRequest + 185, // 239: lnrpc.Lightning.ForwardingHistory:input_type -> lnrpc.ForwardingHistoryRequest + 188, // 240: lnrpc.Lightning.ExportChannelBackup:input_type -> lnrpc.ExportChannelBackupRequest + 191, // 241: lnrpc.Lightning.ExportAllChannelBackups:input_type -> lnrpc.ChanBackupExportRequest + 192, // 242: lnrpc.Lightning.VerifyChanBackup:input_type -> lnrpc.ChanBackupSnapshot + 194, // 243: lnrpc.Lightning.RestoreChannelBackups:input_type -> lnrpc.RestoreChanBackupRequest + 196, // 244: lnrpc.Lightning.SubscribeChannelBackups:input_type -> lnrpc.ChannelBackupSubscription + 199, // 245: lnrpc.Lightning.BakeMacaroon:input_type -> lnrpc.BakeMacaroonRequest + 201, // 246: lnrpc.Lightning.ListMacaroonIDs:input_type -> lnrpc.ListMacaroonIDsRequest + 203, // 247: lnrpc.Lightning.DeleteMacaroonID:input_type -> lnrpc.DeleteMacaroonIDRequest + 206, // 248: lnrpc.Lightning.ListPermissions:input_type -> lnrpc.ListPermissionsRequest + 212, // 249: lnrpc.Lightning.CheckMacaroonPermissions:input_type -> lnrpc.CheckMacPermRequest + 217, // 250: lnrpc.Lightning.RegisterRPCMiddleware:input_type -> lnrpc.RPCMiddlewareResponse + 25, // 251: lnrpc.Lightning.SendCustomMessage:input_type -> lnrpc.SendCustomMessageRequest + 23, // 252: lnrpc.Lightning.SubscribeCustomMessages:input_type -> lnrpc.SubscribeCustomMessagesRequest + 66, // 253: lnrpc.Lightning.ListAliases:input_type -> lnrpc.ListAliasesRequest + 21, // 254: lnrpc.Lightning.LookupHtlcResolution:input_type -> lnrpc.LookupHtlcResolutionRequest + 115, // 255: lnrpc.Lightning.WalletBalance:output_type -> lnrpc.WalletBalanceResponse + 118, // 256: lnrpc.Lightning.ChannelBalance:output_type -> lnrpc.ChannelBalanceResponse + 31, // 257: lnrpc.Lightning.GetTransactions:output_type -> lnrpc.TransactionDetails + 43, // 258: lnrpc.Lightning.EstimateFee:output_type -> lnrpc.EstimateFeeResponse + 47, // 259: lnrpc.Lightning.SendCoins:output_type -> lnrpc.SendCoinsResponse + 49, // 260: lnrpc.Lightning.ListUnspent:output_type -> lnrpc.ListUnspentResponse + 29, // 261: lnrpc.Lightning.SubscribeTransactions:output_type -> lnrpc.Transaction + 45, // 262: lnrpc.Lightning.SendMany:output_type -> lnrpc.SendManyResponse + 51, // 263: lnrpc.Lightning.NewAddress:output_type -> lnrpc.NewAddressResponse + 53, // 264: lnrpc.Lightning.SignMessage:output_type -> lnrpc.SignMessageResponse + 55, // 265: lnrpc.Lightning.VerifyMessage:output_type -> lnrpc.VerifyMessageResponse + 57, // 266: lnrpc.Lightning.ConnectPeer:output_type -> lnrpc.ConnectPeerResponse + 59, // 267: lnrpc.Lightning.DisconnectPeer:output_type -> lnrpc.DisconnectPeerResponse + 75, // 268: lnrpc.Lightning.ListPeers:output_type -> lnrpc.ListPeersResponse + 77, // 269: lnrpc.Lightning.SubscribePeerEvents:output_type -> lnrpc.PeerEvent + 79, // 270: lnrpc.Lightning.GetInfo:output_type -> lnrpc.GetInfoResponse + 81, // 271: lnrpc.Lightning.GetDebugInfo:output_type -> lnrpc.GetDebugInfoResponse + 83, // 272: lnrpc.Lightning.GetRecoveryInfo:output_type -> lnrpc.GetRecoveryInfoResponse + 110, // 273: lnrpc.Lightning.PendingChannels:output_type -> lnrpc.PendingChannelsResponse + 64, // 274: lnrpc.Lightning.ListChannels:output_type -> lnrpc.ListChannelsResponse + 112, // 275: lnrpc.Lightning.SubscribeChannelEvents:output_type -> lnrpc.ChannelEventUpdate + 71, // 276: lnrpc.Lightning.ClosedChannels:output_type -> lnrpc.ClosedChannelsResponse + 38, // 277: lnrpc.Lightning.OpenChannelSync:output_type -> lnrpc.ChannelPoint + 97, // 278: lnrpc.Lightning.OpenChannel:output_type -> lnrpc.OpenStatusUpdate + 95, // 279: lnrpc.Lightning.BatchOpenChannel:output_type -> lnrpc.BatchOpenChannelResponse + 107, // 280: lnrpc.Lightning.FundingStateStep:output_type -> lnrpc.FundingStateStepResp + 36, // 281: lnrpc.Lightning.ChannelAcceptor:output_type -> lnrpc.ChannelAcceptRequest + 89, // 282: lnrpc.Lightning.CloseChannel:output_type -> lnrpc.CloseStatusUpdate + 172, // 283: lnrpc.Lightning.AbandonChannel:output_type -> lnrpc.AbandonChannelResponse + 34, // 284: lnrpc.Lightning.SendPayment:output_type -> lnrpc.SendResponse + 34, // 285: lnrpc.Lightning.SendPaymentSync:output_type -> lnrpc.SendResponse + 34, // 286: lnrpc.Lightning.SendToRoute:output_type -> lnrpc.SendResponse + 34, // 287: lnrpc.Lightning.SendToRouteSync:output_type -> lnrpc.SendResponse + 158, // 288: lnrpc.Lightning.AddInvoice:output_type -> lnrpc.AddInvoiceResponse + 161, // 289: lnrpc.Lightning.ListInvoices:output_type -> lnrpc.ListInvoiceResponse + 155, // 290: lnrpc.Lightning.LookupInvoice:output_type -> lnrpc.Invoice + 155, // 291: lnrpc.Lightning.SubscribeInvoices:output_type -> lnrpc.Invoice + 176, // 292: lnrpc.Lightning.DecodePayReq:output_type -> lnrpc.PayReq + 166, // 293: lnrpc.Lightning.ListPayments:output_type -> lnrpc.ListPaymentsResponse + 169, // 294: lnrpc.Lightning.DeletePayment:output_type -> lnrpc.DeletePaymentResponse + 170, // 295: lnrpc.Lightning.DeleteAllPayments:output_type -> lnrpc.DeleteAllPaymentsResponse + 134, // 296: lnrpc.Lightning.DescribeGraph:output_type -> lnrpc.ChannelGraph + 136, // 297: lnrpc.Lightning.GetNodeMetrics:output_type -> lnrpc.NodeMetricsResponse + 132, // 298: lnrpc.Lightning.GetChanInfo:output_type -> lnrpc.ChannelEdge + 128, // 299: lnrpc.Lightning.GetNodeInfo:output_type -> lnrpc.NodeInfo + 122, // 300: lnrpc.Lightning.QueryRoutes:output_type -> lnrpc.QueryRoutesResponse + 140, // 301: lnrpc.Lightning.GetNetworkInfo:output_type -> lnrpc.NetworkInfo + 142, // 302: lnrpc.Lightning.StopDaemon:output_type -> lnrpc.StopResponse + 144, // 303: lnrpc.Lightning.SubscribeChannelGraph:output_type -> lnrpc.GraphTopologyUpdate + 174, // 304: lnrpc.Lightning.DebugLevel:output_type -> lnrpc.DebugLevelResponse + 180, // 305: lnrpc.Lightning.FeeReport:output_type -> lnrpc.FeeReportResponse + 184, // 306: lnrpc.Lightning.UpdateChannelPolicy:output_type -> lnrpc.PolicyUpdateResponse + 187, // 307: lnrpc.Lightning.ForwardingHistory:output_type -> lnrpc.ForwardingHistoryResponse + 189, // 308: lnrpc.Lightning.ExportChannelBackup:output_type -> lnrpc.ChannelBackup + 192, // 309: lnrpc.Lightning.ExportAllChannelBackups:output_type -> lnrpc.ChanBackupSnapshot + 197, // 310: lnrpc.Lightning.VerifyChanBackup:output_type -> lnrpc.VerifyChanBackupResponse + 195, // 311: lnrpc.Lightning.RestoreChannelBackups:output_type -> lnrpc.RestoreBackupResponse + 192, // 312: lnrpc.Lightning.SubscribeChannelBackups:output_type -> lnrpc.ChanBackupSnapshot + 200, // 313: lnrpc.Lightning.BakeMacaroon:output_type -> lnrpc.BakeMacaroonResponse + 202, // 314: lnrpc.Lightning.ListMacaroonIDs:output_type -> lnrpc.ListMacaroonIDsResponse + 204, // 315: lnrpc.Lightning.DeleteMacaroonID:output_type -> lnrpc.DeleteMacaroonIDResponse + 207, // 316: lnrpc.Lightning.ListPermissions:output_type -> lnrpc.ListPermissionsResponse + 213, // 317: lnrpc.Lightning.CheckMacaroonPermissions:output_type -> lnrpc.CheckMacPermResponse + 214, // 318: lnrpc.Lightning.RegisterRPCMiddleware:output_type -> lnrpc.RPCMiddlewareRequest + 26, // 319: lnrpc.Lightning.SendCustomMessage:output_type -> lnrpc.SendCustomMessageResponse + 24, // 320: lnrpc.Lightning.SubscribeCustomMessages:output_type -> lnrpc.CustomMessage + 67, // 321: lnrpc.Lightning.ListAliases:output_type -> lnrpc.ListAliasesResponse + 22, // 322: lnrpc.Lightning.LookupHtlcResolution:output_type -> lnrpc.LookupHtlcResolutionResponse + 255, // [255:323] is the sub-list for method output_type + 187, // [187:255] is the sub-list for method input_type + 187, // [187:187] is the sub-list for extension type_name + 187, // [187:187] is the sub-list for extension extendee + 0, // [0:187] is the sub-list for field type_name } func init() { file_lightning_proto_init() } @@ -23603,7 +23682,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[160].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyUpdateRequest); i { + switch v := v.(*InboundFee); i { case 0: return &v.state case 1: @@ -23615,7 +23694,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[161].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FailedUpdate); i { + switch v := v.(*PolicyUpdateRequest); i { case 0: return &v.state case 1: @@ -23627,7 +23706,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[162].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyUpdateResponse); i { + switch v := v.(*FailedUpdate); i { case 0: return &v.state case 1: @@ -23639,7 +23718,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[163].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ForwardingHistoryRequest); i { + switch v := v.(*PolicyUpdateResponse); i { case 0: return &v.state case 1: @@ -23651,7 +23730,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[164].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ForwardingEvent); i { + switch v := v.(*ForwardingHistoryRequest); i { case 0: return &v.state case 1: @@ -23663,7 +23742,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[165].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ForwardingHistoryResponse); i { + switch v := v.(*ForwardingEvent); i { case 0: return &v.state case 1: @@ -23675,7 +23754,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[166].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExportChannelBackupRequest); i { + switch v := v.(*ForwardingHistoryResponse); i { case 0: return &v.state case 1: @@ -23687,7 +23766,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[167].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelBackup); i { + switch v := v.(*ExportChannelBackupRequest); i { case 0: return &v.state case 1: @@ -23699,7 +23778,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[168].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MultiChanBackup); i { + switch v := v.(*ChannelBackup); i { case 0: return &v.state case 1: @@ -23711,7 +23790,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[169].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChanBackupExportRequest); i { + switch v := v.(*MultiChanBackup); i { case 0: return &v.state case 1: @@ -23723,7 +23802,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[170].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChanBackupSnapshot); i { + switch v := v.(*ChanBackupExportRequest); i { case 0: return &v.state case 1: @@ -23735,7 +23814,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[171].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelBackups); i { + switch v := v.(*ChanBackupSnapshot); i { case 0: return &v.state case 1: @@ -23747,7 +23826,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[172].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RestoreChanBackupRequest); i { + switch v := v.(*ChannelBackups); i { case 0: return &v.state case 1: @@ -23759,7 +23838,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[173].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RestoreBackupResponse); i { + switch v := v.(*RestoreChanBackupRequest); i { case 0: return &v.state case 1: @@ -23771,7 +23850,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[174].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelBackupSubscription); i { + switch v := v.(*RestoreBackupResponse); i { case 0: return &v.state case 1: @@ -23783,7 +23862,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[175].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VerifyChanBackupResponse); i { + switch v := v.(*ChannelBackupSubscription); i { case 0: return &v.state case 1: @@ -23795,7 +23874,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[176].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MacaroonPermission); i { + switch v := v.(*VerifyChanBackupResponse); i { case 0: return &v.state case 1: @@ -23807,7 +23886,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[177].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BakeMacaroonRequest); i { + switch v := v.(*MacaroonPermission); i { case 0: return &v.state case 1: @@ -23819,7 +23898,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[178].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BakeMacaroonResponse); i { + switch v := v.(*BakeMacaroonRequest); i { case 0: return &v.state case 1: @@ -23831,7 +23910,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[179].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListMacaroonIDsRequest); i { + switch v := v.(*BakeMacaroonResponse); i { case 0: return &v.state case 1: @@ -23843,7 +23922,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[180].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListMacaroonIDsResponse); i { + switch v := v.(*ListMacaroonIDsRequest); i { case 0: return &v.state case 1: @@ -23855,7 +23934,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[181].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteMacaroonIDRequest); i { + switch v := v.(*ListMacaroonIDsResponse); i { case 0: return &v.state case 1: @@ -23867,7 +23946,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[182].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteMacaroonIDResponse); i { + switch v := v.(*DeleteMacaroonIDRequest); i { case 0: return &v.state case 1: @@ -23879,7 +23958,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[183].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MacaroonPermissionList); i { + switch v := v.(*DeleteMacaroonIDResponse); i { case 0: return &v.state case 1: @@ -23891,7 +23970,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[184].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListPermissionsRequest); i { + switch v := v.(*MacaroonPermissionList); i { case 0: return &v.state case 1: @@ -23903,7 +23982,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[185].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListPermissionsResponse); i { + switch v := v.(*ListPermissionsRequest); i { case 0: return &v.state case 1: @@ -23915,7 +23994,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[186].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Failure); i { + switch v := v.(*ListPermissionsResponse); i { case 0: return &v.state case 1: @@ -23927,7 +24006,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[187].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelUpdate); i { + switch v := v.(*Failure); i { case 0: return &v.state case 1: @@ -23939,7 +24018,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[188].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MacaroonId); i { + switch v := v.(*ChannelUpdate); i { case 0: return &v.state case 1: @@ -23951,7 +24030,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[189].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Op); i { + switch v := v.(*MacaroonId); i { case 0: return &v.state case 1: @@ -23963,7 +24042,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[190].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CheckMacPermRequest); i { + switch v := v.(*Op); i { case 0: return &v.state case 1: @@ -23975,7 +24054,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[191].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CheckMacPermResponse); i { + switch v := v.(*CheckMacPermRequest); i { case 0: return &v.state case 1: @@ -23987,7 +24066,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[192].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RPCMiddlewareRequest); i { + switch v := v.(*CheckMacPermResponse); i { case 0: return &v.state case 1: @@ -23999,7 +24078,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[193].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StreamAuth); i { + switch v := v.(*RPCMiddlewareRequest); i { case 0: return &v.state case 1: @@ -24011,7 +24090,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[194].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RPCMessage); i { + switch v := v.(*StreamAuth); i { case 0: return &v.state case 1: @@ -24023,7 +24102,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[195].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RPCMiddlewareResponse); i { + switch v := v.(*RPCMessage); i { case 0: return &v.state case 1: @@ -24035,7 +24114,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[196].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MiddlewareRegistration); i { + switch v := v.(*RPCMiddlewareResponse); i { case 0: return &v.state case 1: @@ -24047,6 +24126,18 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[197].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MiddlewareRegistration); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_lightning_proto_msgTypes[198].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*InterceptFeedback); i { case 0: return &v.state @@ -24058,7 +24149,7 @@ func file_lightning_proto_init() { return nil } } - file_lightning_proto_msgTypes[204].Exporter = func(v interface{}, i int) interface{} { + file_lightning_proto_msgTypes[205].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PendingChannelsResponse_PendingChannel); i { case 0: return &v.state @@ -24070,7 +24161,7 @@ func file_lightning_proto_init() { return nil } } - file_lightning_proto_msgTypes[205].Exporter = func(v interface{}, i int) interface{} { + file_lightning_proto_msgTypes[206].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PendingChannelsResponse_PendingOpenChannel); i { case 0: return &v.state @@ -24082,7 +24173,7 @@ func file_lightning_proto_init() { return nil } } - file_lightning_proto_msgTypes[206].Exporter = func(v interface{}, i int) interface{} { + file_lightning_proto_msgTypes[207].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PendingChannelsResponse_WaitingCloseChannel); i { case 0: return &v.state @@ -24094,7 +24185,7 @@ func file_lightning_proto_init() { return nil } } - file_lightning_proto_msgTypes[207].Exporter = func(v interface{}, i int) interface{} { + file_lightning_proto_msgTypes[208].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PendingChannelsResponse_Commitments); i { case 0: return &v.state @@ -24106,7 +24197,7 @@ func file_lightning_proto_init() { return nil } } - file_lightning_proto_msgTypes[208].Exporter = func(v interface{}, i int) interface{} { + file_lightning_proto_msgTypes[209].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PendingChannelsResponse_ClosedChannel); i { case 0: return &v.state @@ -24118,7 +24209,7 @@ func file_lightning_proto_init() { return nil } } - file_lightning_proto_msgTypes[209].Exporter = func(v interface{}, i int) interface{} { + file_lightning_proto_msgTypes[210].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PendingChannelsResponse_ForceClosedChannel); i { case 0: return &v.state @@ -24168,21 +24259,21 @@ func file_lightning_proto_init() { (*ChannelEventUpdate_PendingOpenChannel)(nil), (*ChannelEventUpdate_FullyResolvedChannel)(nil), } - file_lightning_proto_msgTypes[160].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[161].OneofWrappers = []interface{}{ (*PolicyUpdateRequest_Global)(nil), (*PolicyUpdateRequest_ChanPoint)(nil), } - file_lightning_proto_msgTypes[172].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[173].OneofWrappers = []interface{}{ (*RestoreChanBackupRequest_ChanBackups)(nil), (*RestoreChanBackupRequest_MultiChanBackup)(nil), } - file_lightning_proto_msgTypes[192].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[193].OneofWrappers = []interface{}{ (*RPCMiddlewareRequest_StreamAuth)(nil), (*RPCMiddlewareRequest_Request)(nil), (*RPCMiddlewareRequest_Response)(nil), (*RPCMiddlewareRequest_RegComplete)(nil), } - file_lightning_proto_msgTypes[195].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[196].OneofWrappers = []interface{}{ (*RPCMiddlewareResponse_Register)(nil), (*RPCMiddlewareResponse_Feedback)(nil), } @@ -24192,7 +24283,7 @@ func file_lightning_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_lightning_proto_rawDesc, NumEnums: 21, - NumMessages: 225, + NumMessages: 226, NumExtensions: 0, NumServices: 1, }, diff --git a/lnrpc/lightning.proto b/lnrpc/lightning.proto index 3bd7c4030..8e3e645b7 100644 --- a/lnrpc/lightning.proto +++ b/lnrpc/lightning.proto @@ -4251,6 +4251,10 @@ message DeleteAllPaymentsRequest { Only delete failed HTLCs from payments, not the payment itself. */ bool failed_htlcs_only = 2; + + // Delete all payments. NOTE: Using this option requires careful + // consideration as it is a destructive operation. + bool all_payments = 3; } message DeletePaymentResponse { @@ -4327,6 +4331,8 @@ enum FeatureBit { ANCHORS_OPT = 21; ANCHORS_ZERO_FEE_HTLC_REQ = 22; ANCHORS_ZERO_FEE_HTLC_OPT = 23; + ROUTE_BLINDING_REQUIRED = 24; + ROUTE_BLINDING_OPTIONAL = 25; AMP_REQ = 30; AMP_OPT = 31; } @@ -4383,6 +4389,16 @@ message FeeReportResponse { uint64 month_fee_sum = 4; } +message InboundFee { + // The inbound base fee charged regardless of the number of milli-satoshis + // received in the channel. By default, only negative values are accepted. + int32 base_fee_msat = 1; + + // The effective inbound fee rate in micro-satoshis (parts per million). + // By default, only negative values are accepted. + int32 fee_rate_ppm = 2; +} + message PolicyUpdateRequest { oneof scope { // If set, then this update applies to all currently active channels. @@ -4416,8 +4432,9 @@ message PolicyUpdateRequest { // If true, min_htlc_msat is applied. bool min_htlc_msat_specified = 8; - int32 inbound_base_fee_msat = 10; - int32 inbound_fee_rate_ppm = 11; + // Optional inbound fee. If unset, the previously set value will be + // retained [EXPERIMENTAL]. + InboundFee inbound_fee = 10; } enum UpdateFailure { diff --git a/lnrpc/lightning.swagger.json b/lnrpc/lightning.swagger.json index 22e3a1246..a1321ae0e 100644 --- a/lnrpc/lightning.swagger.json +++ b/lnrpc/lightning.swagger.json @@ -1470,6 +1470,8 @@ "ANCHORS_OPT", "ANCHORS_ZERO_FEE_HTLC_REQ", "ANCHORS_ZERO_FEE_HTLC_OPT", + "ROUTE_BLINDING_REQUIRED", + "ROUTE_BLINDING_OPTIONAL", "AMP_REQ", "AMP_OPT" ] @@ -2268,6 +2270,13 @@ "in": "query", "required": false, "type": "boolean" + }, + { + "name": "all_payments", + "description": "Delete all payments. NOTE: Using this option requires careful\nconsideration as it is a destructive operation.", + "in": "query", + "required": false, + "type": "boolean" } ], "tags": [ @@ -4702,6 +4711,8 @@ "ANCHORS_OPT", "ANCHORS_ZERO_FEE_HTLC_REQ", "ANCHORS_ZERO_FEE_HTLC_OPT", + "ROUTE_BLINDING_REQUIRED", + "ROUTE_BLINDING_OPTIONAL", "AMP_REQ", "AMP_OPT" ], @@ -5301,6 +5312,21 @@ } } }, + "lnrpcInboundFee": { + "type": "object", + "properties": { + "base_fee_msat": { + "type": "integer", + "format": "int32", + "description": "The inbound base fee charged regardless of the number of milli-satoshis\nreceived in the channel. By default, only negative values are accepted." + }, + "fee_rate_ppm": { + "type": "integer", + "format": "int32", + "description": "The effective inbound fee rate in micro-satoshis (parts per million).\nBy default, only negative values are accepted." + } + } + }, "lnrpcInitiator": { "type": "string", "enum": [ @@ -6597,13 +6623,9 @@ "type": "boolean", "description": "If true, min_htlc_msat is applied." }, - "inbound_base_fee_msat": { - "type": "integer", - "format": "int32" - }, - "inbound_fee_rate_ppm": { - "type": "integer", - "format": "int32" + "inbound_fee": { + "$ref": "#/definitions/lnrpcInboundFee", + "description": "Optional inbound fee. If unset, the previously set value will be\nretained [EXPERIMENTAL]." } } }, diff --git a/lnrpc/peersrpc/peers.swagger.json b/lnrpc/peersrpc/peers.swagger.json index c7b9970cf..b667994a4 100644 --- a/lnrpc/peersrpc/peers.swagger.json +++ b/lnrpc/peersrpc/peers.swagger.json @@ -77,6 +77,8 @@ "ANCHORS_OPT", "ANCHORS_ZERO_FEE_HTLC_REQ", "ANCHORS_ZERO_FEE_HTLC_OPT", + "ROUTE_BLINDING_REQUIRED", + "ROUTE_BLINDING_OPTIONAL", "AMP_REQ", "AMP_OPT" ], diff --git a/lnrpc/routerrpc/router.swagger.json b/lnrpc/routerrpc/router.swagger.json index 98781af7a..ee161996b 100644 --- a/lnrpc/routerrpc/router.swagger.json +++ b/lnrpc/routerrpc/router.swagger.json @@ -856,6 +856,8 @@ "ANCHORS_OPT", "ANCHORS_ZERO_FEE_HTLC_REQ", "ANCHORS_ZERO_FEE_HTLC_OPT", + "ROUTE_BLINDING_REQUIRED", + "ROUTE_BLINDING_OPTIONAL", "AMP_REQ", "AMP_OPT" ], diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 3c4e4c037..de108b6de 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -946,6 +946,14 @@ func (r *RouterBackend) extractIntentFromSendRequest( payAddr := payReq.PaymentAddr if payReq.Features.HasFeature(lnwire.AMPOptional) { + // The opt-in AMP flag is required to pay an AMP + // invoice. + if !rpcPayReq.Amp { + return nil, fmt.Errorf("the AMP flag (--amp " + + "or SendPaymentRequest.Amp) must be " + + "set to pay an AMP invoice") + } + // Generate random SetID and root share. var setID [32]byte _, err = rand.Read(setID[:]) diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 2f5204695..cccfb4d8b 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -6,7 +6,6 @@ import ( crand "crypto/rand" "errors" "fmt" - "io/ioutil" "os" "path/filepath" "sync/atomic" @@ -224,7 +223,7 @@ func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) { if err != nil { return nil, nil, err } - err = ioutil.WriteFile(macFilePath, routerMacBytes, 0644) + err = os.WriteFile(macFilePath, routerMacBytes, 0644) if err != nil { _ = os.Remove(macFilePath) return nil, nil, err diff --git a/lnrpc/signrpc/signer_server.go b/lnrpc/signrpc/signer_server.go index c29d495eb..4b2ac2c6a 100644 --- a/lnrpc/signrpc/signer_server.go +++ b/lnrpc/signrpc/signer_server.go @@ -8,7 +8,6 @@ import ( "context" "crypto/sha256" "fmt" - "io/ioutil" "os" "path/filepath" @@ -167,7 +166,7 @@ func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) { if err != nil { return nil, nil, err } - err = ioutil.WriteFile(macFilePath, signerMacBytes, 0644) + err = os.WriteFile(macFilePath, signerMacBytes, 0644) if err != nil { _ = os.Remove(macFilePath) return nil, nil, err diff --git a/lnrpc/walletrpc/walletkit.pb.go b/lnrpc/walletrpc/walletkit.pb.go index 554b27dd5..bc01442e5 100644 --- a/lnrpc/walletrpc/walletkit.pb.go +++ b/lnrpc/walletrpc/walletkit.pb.go @@ -3068,7 +3068,7 @@ type BumpFeeRequest struct { Outpoint *lnrpc.OutPoint `protobuf:"bytes,1,opt,name=outpoint,proto3" json:"outpoint,omitempty"` // Optional. The deadline in number of blocks that the input should be spent // within. When not set, for new inputs, the default value (1008) is used; - // for exiting inputs, their current values will be retained. + // for existing inputs, their current values will be retained. TargetConf uint32 `protobuf:"varint,2,opt,name=target_conf,json=targetConf,proto3" json:"target_conf,omitempty"` // Deprecated, use sat_per_vbyte. // The fee rate, expressed in sat/vbyte, that should be used to spend the input diff --git a/lnrpc/walletrpc/walletkit.proto b/lnrpc/walletrpc/walletkit.proto index b6d09c64b..b86180344 100644 --- a/lnrpc/walletrpc/walletkit.proto +++ b/lnrpc/walletrpc/walletkit.proto @@ -283,7 +283,7 @@ service WalletKit { /* lncli: `wallet labeltx` LabelTransaction adds a label to a transaction. If the transaction already has a label the call will fail unless the overwrite bool is set. This will - overwrite the exiting transaction label. Labels must not be empty, and + overwrite the existing transaction label. Labels must not be empty, and cannot exceed 500 characters. */ rpc LabelTransaction (LabelTransactionRequest) @@ -1176,7 +1176,7 @@ message BumpFeeRequest { // Optional. The deadline in number of blocks that the input should be spent // within. When not set, for new inputs, the default value (1008) is used; - // for exiting inputs, their current values will be retained. + // for existing inputs, their current values will be retained. uint32 target_conf = 2; /* diff --git a/lnrpc/walletrpc/walletkit.swagger.json b/lnrpc/walletrpc/walletkit.swagger.json index f21894dd2..1e8286b1b 100644 --- a/lnrpc/walletrpc/walletkit.swagger.json +++ b/lnrpc/walletrpc/walletkit.swagger.json @@ -768,7 +768,7 @@ }, "/v2/wallet/tx/label": { "post": { - "summary": "lncli: `wallet labeltx`\nLabelTransaction adds a label to a transaction. If the transaction already\nhas a label the call will fail unless the overwrite bool is set. This will\noverwrite the exiting transaction label. Labels must not be empty, and\ncannot exceed 500 characters.", + "summary": "lncli: `wallet labeltx`\nLabelTransaction adds a label to a transaction. If the transaction already\nhas a label the call will fail unless the overwrite bool is set. This will\noverwrite the existing transaction label. Labels must not be empty, and\ncannot exceed 500 characters.", "operationId": "WalletKit_LabelTransaction", "responses": { "200": { @@ -1360,7 +1360,7 @@ "target_conf": { "type": "integer", "format": "int64", - "description": "Optional. The deadline in number of blocks that the input should be spent\nwithin. When not set, for new inputs, the default value (1008) is used;\nfor exiting inputs, their current values will be retained." + "description": "Optional. The deadline in number of blocks that the input should be spent\nwithin. When not set, for new inputs, the default value (1008) is used;\nfor existing inputs, their current values will be retained." }, "sat_per_byte": { "type": "integer", diff --git a/lnrpc/walletrpc/walletkit_grpc.pb.go b/lnrpc/walletrpc/walletkit_grpc.pb.go index 148457162..c49e2bdb7 100644 --- a/lnrpc/walletrpc/walletkit_grpc.pb.go +++ b/lnrpc/walletrpc/walletkit_grpc.pb.go @@ -216,7 +216,7 @@ type WalletKitClient interface { // lncli: `wallet labeltx` // LabelTransaction adds a label to a transaction. If the transaction already // has a label the call will fail unless the overwrite bool is set. This will - // overwrite the exiting transaction label. Labels must not be empty, and + // overwrite the existing transaction label. Labels must not be empty, and // cannot exceed 500 characters. LabelTransaction(ctx context.Context, in *LabelTransactionRequest, opts ...grpc.CallOption) (*LabelTransactionResponse, error) // lncli: `wallet psbt fund` @@ -727,7 +727,7 @@ type WalletKitServer interface { // lncli: `wallet labeltx` // LabelTransaction adds a label to a transaction. If the transaction already // has a label the call will fail unless the overwrite bool is set. This will - // overwrite the exiting transaction label. Labels must not be empty, and + // overwrite the existing transaction label. Labels must not be empty, and // cannot exceed 500 characters. LabelTransaction(context.Context, *LabelTransactionRequest) (*LabelTransactionResponse, error) // lncli: `wallet psbt fund` diff --git a/lnrpc/walletrpc/walletkit_server.go b/lnrpc/walletrpc/walletkit_server.go index b8fcbc776..4c3996515 100644 --- a/lnrpc/walletrpc/walletkit_server.go +++ b/lnrpc/walletrpc/walletkit_server.go @@ -10,7 +10,6 @@ import ( "encoding/binary" "errors" "fmt" - "io/ioutil" "math" "os" "path/filepath" @@ -289,7 +288,7 @@ func New(cfg *Config) (*WalletKit, lnrpc.MacaroonPerms, error) { if err != nil { return nil, nil, err } - err = ioutil.WriteFile(macFilePath, walletKitMacBytes, 0644) + err = os.WriteFile(macFilePath, walletKitMacBytes, 0644) if err != nil { _ = os.Remove(macFilePath) return nil, nil, err @@ -1551,21 +1550,82 @@ func (w *WalletKit) fundPsbtInternalWallet(account string, return err } + // filterFn makes sure utxos which are unconfirmed and + // still used by the sweeper are not used. + filterFn := func(u *lnwallet.Utxo) bool { + // Confirmed utxos are always allowed. + if u.Confirmations > 0 { + return true + } + + // Unconfirmed utxos in use by the sweeper are + // not stable to use because they can be + // replaced. + if w.cfg.Sweeper.IsSweeperOutpoint(u.OutPoint) { + log.Warnf("Cannot use unconfirmed "+ + "utxo=%v because it is "+ + "unstable and could be "+ + "replaced", u.OutPoint) + + return false + } + + return true + } + + eligibleUtxos := fn.Filter(filterFn, utxos) + // Validate all inputs against our known list of UTXOs // now. - err = verifyInputsUnspent(packet.UnsignedTx.TxIn, utxos) + err = verifyInputsUnspent( + packet.UnsignedTx.TxIn, eligibleUtxos, + ) if err != nil { return err } } + // currentHeight is needed to determine whether the internal + // wallet utxo is still unconfirmed. + _, currentHeight, err := w.cfg.Chain.GetBestBlock() + if err != nil { + return fmt.Errorf("unable to retrieve current "+ + "height: %v", err) + } + + // restrictUnstableUtxos is a filter function which disallows + // the usage of unconfirmed outputs published (still in use) by + // the sweeper. + restrictUnstableUtxos := func(utxo wtxmgr.Credit) bool { + // Wallet utxos which are unmined have a height + // of -1. + if utxo.Height != -1 && utxo.Height <= currentHeight { + // Confirmed utxos are always allowed. + return true + } + + // Utxos used by the sweeper are not used for + // channel openings. + allowed := !w.cfg.Sweeper.IsSweeperOutpoint( + utxo.OutPoint, + ) + if !allowed { + log.Warnf("Cannot use unconfirmed "+ + "utxo=%v because it is "+ + "unstable and could be "+ + "replaced", utxo.OutPoint) + } + + return allowed + } + // We made sure the input from the user is as sane as possible. // We can now ask the wallet to fund the TX. This will not yet // lock any coins but might still change the wallet DB by // generating a new change address. changeIndex, err := w.cfg.Wallet.FundPsbt( - packet, minConfs, feeSatPerKW, account, - keyScope, strategy, + packet, minConfs, feeSatPerKW, account, keyScope, + strategy, restrictUnstableUtxos, ) if err != nil { return fmt.Errorf("wallet couldn't fund PSBT: %w", err) @@ -1600,8 +1660,6 @@ func (w *WalletKit) fundPsbtInternalWallet(account string, // fundPsbtCoinSelect uses the "new" PSBT funding method using the channel // funding coin selection algorithm that allows specifying custom inputs while // selecting coins. -// -//nolint:funlen func (w *WalletKit) fundPsbtCoinSelect(account string, changeIndex int32, packet *psbt.Packet, minConfs int32, changeType chanfunding.ChangeAddressType, @@ -1677,7 +1735,7 @@ func (w *WalletKit) fundPsbtCoinSelect(account string, changeIndex int32, // Do we already have enough inputs specified to pay for the TX as it // is? In that case we only need to allocate any change, if there is // any. - packetFeeNoChange := feeRate.FeeForWeight(int64(estimator.Weight())) + packetFeeNoChange := feeRate.FeeForWeight(estimator.Weight()) if inputSum >= outputSum+packetFeeNoChange { // Calculate the packet's fee with a change output so, so we can // let the coin selection algorithm decide whether to use a @@ -1689,9 +1747,7 @@ func (w *WalletKit) fundPsbtCoinSelect(account string, changeIndex int32, case chanfunding.P2WKHChangeAddress: estimator.AddP2WKHOutput() } - packetFeeWithChange := feeRate.FeeForWeight( - int64(estimator.Weight()), - ) + packetFeeWithChange := feeRate.FeeForWeight(estimator.Weight()) changeAmt, needMore, err := chanfunding.CalculateChangeAmount( inputSum, outputSum, packetFeeNoChange, diff --git a/lnrpc/walletrpc/walletkit_server_test.go b/lnrpc/walletrpc/walletkit_server_test.go index d47a55981..115ccf96e 100644 --- a/lnrpc/walletrpc/walletkit_server_test.go +++ b/lnrpc/walletrpc/walletkit_server_test.go @@ -132,7 +132,7 @@ func TestFundPsbtCoinSelect(t *testing.T) { } weight := estimator.Weight() - fee := chainfee.FeePerKwFloor.FeeForWeight(int64(weight)) + fee := chainfee.FeePerKwFloor.FeeForWeight(weight) return fee + dust } diff --git a/lntest/bitcoind_common.go b/lntest/bitcoind_common.go index 657da7cd3..9cbcd23fd 100644 --- a/lntest/bitcoind_common.go +++ b/lntest/bitcoind_common.go @@ -6,7 +6,6 @@ package lntest import ( "errors" "fmt" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -105,7 +104,7 @@ func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string, return nil, nil, err } - tempBitcoindDir, err := ioutil.TempDir("", "bitcoind") + tempBitcoindDir, err := os.MkdirTemp("", "bitcoind") if err != nil { return nil, nil, fmt.Errorf("unable to create temp directory: %w", err) diff --git a/lntest/btcd.go b/lntest/btcd.go index 09f33472b..2d735fa2b 100644 --- a/lntest/btcd.go +++ b/lntest/btcd.go @@ -7,7 +7,6 @@ import ( "encoding/hex" "errors" "fmt" - "io/ioutil" "os" "strings" @@ -135,7 +134,7 @@ func NewBackend(miner string, netParams *chaincfg.Params) ( // the log files, including any compressed log files from // logrorate, before deleting the temporary log dir. logDir := fmt.Sprintf("%s/%s", baseLogDir, netParams.Name) - files, err := ioutil.ReadDir(logDir) + files, err := os.ReadDir(logDir) if err != nil { errStr += fmt.Sprintf( "unable to read log directory: %v\n", err, diff --git a/lntest/fee_service.go b/lntest/fee_service.go index d96bd7588..cee9ae0ab 100644 --- a/lntest/fee_service.go +++ b/lntest/fee_service.go @@ -17,7 +17,7 @@ import ( // WebFeeService defines an interface that's used to provide fee estimation // service used in the integration tests. It must provide an URL so that a lnd -// node can be started with the flag `--feeurl` and uses the customized fee +// node can be started with the flag `--fee.url` and uses the customized fee // estimator. type WebFeeService interface { // Start starts the service. diff --git a/lntest/harness.go b/lntest/harness.go index 3674dcfe7..af4eaba4b 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -22,6 +22,7 @@ import ( "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/rpc" "github.com/lightningnetwork/lnd/lntest/wait" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/stretchr/testify/require" @@ -106,6 +107,32 @@ type HarnessTest struct { cleaned bool } +// harnessOpts contains functional option to modify the behavior of the various +// harness calls. +type harnessOpts struct { + useAMP bool +} + +// defaultHarnessOpts returns a new instance of the harnessOpts with default +// values specified. +func defaultHarnessOpts() harnessOpts { + return harnessOpts{ + useAMP: false, + } +} + +// HarnessOpt is a functional option that can be used to modify the behavior of +// harness functionality. +type HarnessOpt func(*harnessOpts) + +// WithAMP is a functional option that can be used to enable the AMP feature +// for sending payments. +func WithAMP() HarnessOpt { + return func(h *harnessOpts) { + h.useAMP = true + } +} + // NewHarnessTest creates a new instance of a harnessTest from a regular // testing.T instance. func NewHarnessTest(t *testing.T, lndBinary string, feeService WebFeeService, @@ -1206,6 +1233,7 @@ func (h *HarnessTest) OpenChannelAssertErr(srcNode, destNode *node.HarnessNode, // Receive an error to be sent from the stream. _, err := h.receiveOpenChannelUpdate(respStream) + require.NotNil(h, err, "expected channel opening to fail") // Use string comparison here as we haven't codified all the RPC errors // yet. @@ -1438,7 +1466,13 @@ func (h *HarnessTest) FundCoinsP2TR(amt btcutil.Amount, // all payment requests. This function does not return until all payments // have reached the specified status. func (h *HarnessTest) completePaymentRequestsAssertStatus(hn *node.HarnessNode, - paymentRequests []string, status lnrpc.Payment_PaymentStatus) { + paymentRequests []string, status lnrpc.Payment_PaymentStatus, + opts ...HarnessOpt) { + + payOpts := defaultHarnessOpts() + for _, opt := range opts { + opt(&payOpts) + } // Create a buffered chan to signal the results. results := make(chan rpc.PaymentClient, len(paymentRequests)) @@ -1449,6 +1483,7 @@ func (h *HarnessTest) completePaymentRequestsAssertStatus(hn *node.HarnessNode, PaymentRequest: payReq, TimeoutSeconds: int32(wait.PaymentTimeout.Seconds()), FeeLimitMsat: noFeeLimitMsat, + Amp: payOpts.useAMP, } stream := hn.RPC.SendPayment(req) @@ -1477,10 +1512,10 @@ func (h *HarnessTest) completePaymentRequestsAssertStatus(hn *node.HarnessNode, // requests. This function does not return until all payments successfully // complete without errors. func (h *HarnessTest) CompletePaymentRequests(hn *node.HarnessNode, - paymentRequests []string) { + paymentRequests []string, opts ...HarnessOpt) { h.completePaymentRequestsAssertStatus( - hn, paymentRequests, lnrpc.Payment_SUCCEEDED, + hn, paymentRequests, lnrpc.Payment_SUCCEEDED, opts..., ) } @@ -2008,9 +2043,9 @@ func (h *HarnessTest) CalculateTxFee(tx *wire.MsgTx) btcutil.Amount { // CalculateTxWeight calculates the weight for a given tx. // // TODO(yy): use weight estimator to get more accurate result. -func (h *HarnessTest) CalculateTxWeight(tx *wire.MsgTx) int64 { +func (h *HarnessTest) CalculateTxWeight(tx *wire.MsgTx) lntypes.WeightUnit { utx := btcutil.NewTx(tx) - return blockchain.GetTransactionWeight(utx) + return lntypes.WeightUnit(blockchain.GetTransactionWeight(utx)) } // CalculateTxFeeRate calculates the fee rate for a given tx. @@ -2020,7 +2055,7 @@ func (h *HarnessTest) CalculateTxFeeRate( w := h.CalculateTxWeight(tx) fee := h.CalculateTxFee(tx) - return chainfee.NewSatPerKWeight(fee, uint64(w)) + return chainfee.NewSatPerKWeight(fee, w) } // CalculateTxesFeeRate takes a list of transactions and estimates the fee rate diff --git a/lntest/harness_miner.go b/lntest/harness_miner.go index a28715321..f6348c078 100644 --- a/lntest/harness_miner.go +++ b/lntest/harness_miner.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "io/ioutil" "os" "path/filepath" "strings" @@ -121,7 +120,7 @@ func (h *HarnessMiner) saveLogs() { // After shutting down the miner, we'll make a copy of the log files // before deleting the temporary log dir. path := fmt.Sprintf("%s/%s", h.logPath, harnessNetParams.Name) - files, err := ioutil.ReadDir(path) + files, err := os.ReadDir(path) require.NoError(h, err, "unable to read log directory") for _, file := range files { diff --git a/lntest/mock/walletcontroller.go b/lntest/mock/walletcontroller.go index 21d78add3..52aecb382 100644 --- a/lntest/mock/walletcontroller.go +++ b/lntest/mock/walletcontroller.go @@ -208,7 +208,8 @@ func (w *WalletController) ListLeasedOutputs() ([]*base.ListLeasedOutputResult, // FundPsbt currently does nothing. func (w *WalletController) FundPsbt(*psbt.Packet, int32, chainfee.SatPerKWeight, - string, *waddrmgr.KeyScope, base.CoinSelectionStrategy) (int32, error) { + string, *waddrmgr.KeyScope, base.CoinSelectionStrategy, + func(utxo wtxmgr.Credit) bool) (int32, error) { return 0, nil } diff --git a/lntest/node/config.go b/lntest/node/config.go index d0de2fdd4..de1ea92ec 100644 --- a/lntest/node/config.go +++ b/lntest/node/config.go @@ -279,7 +279,7 @@ func (cfg *BaseNodeConfig) GenArgs() []string { } if cfg.FeeURL != "" { - args = append(args, "--feeurl="+cfg.FeeURL) + args = append(args, "--fee.url="+cfg.FeeURL) } // Put extra args in the end so the args can be overwritten. diff --git a/lntest/node/harness_node.go b/lntest/node/harness_node.go index 00424184e..3df5a8e64 100644 --- a/lntest/node/harness_node.go +++ b/lntest/node/harness_node.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -97,7 +96,12 @@ type HarnessNode struct { func NewHarnessNode(t *testing.T, cfg *BaseNodeConfig) (*HarnessNode, error) { if cfg.BaseDir == "" { var err error - cfg.BaseDir, err = ioutil.TempDir("", "lndtest-node") + + // Create a temporary directory for the node's data and logs. + // Use dash suffix as a separator between base name and random + // suffix. + dirBaseName := fmt.Sprintf("lndtest-node-%s-", cfg.Name) + cfg.BaseDir, err = os.MkdirTemp("", dirBaseName) if err != nil { return nil, err } @@ -292,7 +296,7 @@ func (hn *HarnessNode) ReadMacaroon(macPath string, timeout time.Duration) ( // using it. var mac *macaroon.Macaroon err := wait.NoError(func() error { - macBytes, err := ioutil.ReadFile(macPath) + macBytes, err := os.ReadFile(macPath) if err != nil { return fmt.Errorf("error reading macaroon file: %w", err) @@ -819,7 +823,7 @@ func (hn *HarnessNode) BackupDB() error { } } else { // Backup files. - tempDir, err := ioutil.TempDir("", "past-state") + tempDir, err := os.MkdirTemp("", "past-state") if err != nil { return fmt.Errorf("unable to create temp db folder: %w", err) @@ -1027,7 +1031,7 @@ func addLogFile(hn *HarnessNode) error { // copyAll copies all files and directories from srcDir to dstDir recursively. // Note that this function does not support links. func copyAll(dstDir, srcDir string) error { - entries, err := ioutil.ReadDir(srcDir) + entries, err := os.ReadDir(srcDir) if err != nil { return err } diff --git a/lntest/node/watcher.go b/lntest/node/watcher.go index a87e6c2ba..7656cb3c5 100644 --- a/lntest/node/watcher.go +++ b/lntest/node/watcher.go @@ -681,6 +681,18 @@ func CheckChannelPolicy(policy, expectedPolicy *lnrpc.RoutingPolicy) error { return fmt.Errorf("expected max htlc %v, got %v", expectedPolicy.MaxHtlcMsat, policy.MaxHtlcMsat) } + if policy.InboundFeeBaseMsat != expectedPolicy.InboundFeeBaseMsat { + return fmt.Errorf("expected inbound base fee %v, got %v", + expectedPolicy.InboundFeeBaseMsat, + policy.InboundFeeBaseMsat) + } + if policy.InboundFeeRateMilliMsat != + expectedPolicy.InboundFeeRateMilliMsat { + + return fmt.Errorf("expected inbound fee rate %v, got %v", + expectedPolicy.InboundFeeRateMilliMsat, + policy.InboundFeeRateMilliMsat) + } if policy.Disabled != expectedPolicy.Disabled { return errors.New("edge should be disabled but isn't") } diff --git a/lntest/rpc/lnd.go b/lntest/rpc/lnd.go index 8f55271f8..e800b9ca4 100644 --- a/lntest/rpc/lnd.go +++ b/lntest/rpc/lnd.go @@ -70,7 +70,7 @@ func (h *HarnessRPC) DeleteAllPayments() { ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) defer cancel() - req := &lnrpc.DeleteAllPaymentsRequest{} + req := &lnrpc.DeleteAllPaymentsRequest{AllPayments: true} _, err := h.LN.DeleteAllPayments(ctxt, req) h.NoError(err, "DeleteAllPayments") } diff --git a/lntest/rpc/wallet_kit.go b/lntest/rpc/wallet_kit.go index 79ccdcbdf..967679d66 100644 --- a/lntest/rpc/wallet_kit.go +++ b/lntest/rpc/wallet_kit.go @@ -68,6 +68,16 @@ func (h *HarnessRPC) FundPsbt( return resp } +// FundPsbtAssertErr makes a RPC call to the node's FundPsbt and asserts an +// error is returned. +func (h *HarnessRPC) FundPsbtAssertErr(req *walletrpc.FundPsbtRequest) { + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + _, err := h.WalletKit.FundPsbt(ctxt, req) + require.Error(h, err, "expected error returned") +} + // FinalizePsbt makes a RPC call to node's FinalizePsbt and asserts. func (h *HarnessRPC) FinalizePsbt( req *walletrpc.FinalizePsbtRequest) *walletrpc.FinalizePsbtResponse { diff --git a/lntest/utils.go b/lntest/utils.go index 660c42ef9..d230b6b61 100644 --- a/lntest/utils.go +++ b/lntest/utils.go @@ -13,6 +13,7 @@ import ( "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntest/wait" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" @@ -212,8 +213,9 @@ func CalcStaticFee(c lnrpc.CommitmentType, numHTLCs int) btcutil.Amount { anchors = anchorSize } - return feePerKw.FeeForWeight(int64(commitWeight+htlcWeight*numHTLCs)) + - anchors + totalWeight := commitWeight + htlcWeight*numHTLCs + + return feePerKw.FeeForWeight(lntypes.WeightUnit(totalWeight)) + anchors } // CalculateMaxHtlc re-implements the RequiredRemoteChannelReserve of the @@ -266,8 +268,10 @@ func CalcStaticFeeBuffer(c lnrpc.CommitmentType, numHTLCs int) btcutil.Amount { // Account for the HTLC which will be required when sending an htlc. numHTLCs++ + + totalWeight := commitWeight + numHTLCs*htlcWeight feeBuffer := lnwallet.CalcFeeBuffer( - feePerKw, int64(commitWeight+numHTLCs*htlcWeight), + feePerKw, lntypes.WeightUnit(totalWeight), ) return feeBuffer.ToSatoshis() diff --git a/lntypes/txsize.go b/lntypes/txsize.go new file mode 100644 index 000000000..0618bfc51 --- /dev/null +++ b/lntypes/txsize.go @@ -0,0 +1,42 @@ +package lntypes + +import ( + "fmt" + "math" +) + +// WeightUnit defines a unit to express the transaction size. One weight unit +// is 1/4_000_000 of the max block size. The tx weight is calculated using +// `Base tx size * 3 + Total tx size`. +// - Base tx size is size of the transaction serialized without the witness +// data. +// - Total tx size is the transaction size in bytes serialized according +// #BIP144. +type WeightUnit uint64 + +// ToVB converts a value expressed in weight units to virtual bytes. +func (wu WeightUnit) ToVB() VByte { + // According to BIP141: Virtual transaction size is defined as + // Transaction weight / 4 (rounded up to the next integer). + return VByte(math.Ceil(float64(wu) / 4)) +} + +// String returns the string representation of the weight unit. +func (wu WeightUnit) String() string { + return fmt.Sprintf("%d wu", wu) +} + +// VByte defines a unit to express the transaction size. One virtual byte is +// 1/4th of a weight unit. The tx virtual bytes is calculated using `TxWeight / +// 4`. +type VByte uint64 + +// ToWU converts a value expressed in virtual bytes to weight units. +func (vb VByte) ToWU() WeightUnit { + return WeightUnit(vb * 4) +} + +// String returns the string representation of the virtual byte. +func (vb VByte) String() string { + return fmt.Sprintf("%d vb", vb) +} diff --git a/lntypes/txsize_test.go b/lntypes/txsize_test.go new file mode 100644 index 000000000..a68a45b23 --- /dev/null +++ b/lntypes/txsize_test.go @@ -0,0 +1,28 @@ +package lntypes + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +// TestTxSizeUnit tests the conversion of tx size to different units. +func TestTxSizeUnit(t *testing.T) { + t.Parallel() + + // Test normal conversion from 100wu to 25vb. + wu := WeightUnit(100) + vb := VByte(25) + require.Equal(t, vb, wu.ToVB(), "wu -> vb conversion "+ + "failed: want %v, got %v", vb, wu.ToVB()) + require.Equal(t, wu, vb.ToWU(), "vb -> wu conversion "+ + "failed: want %v, got %v", wu, vb.ToWU()) + + // Test rounding up conversion from 99wu to 25vb. + wu = WeightUnit(99) + vb = VByte(25) + require.Equal(t, vb, wu.ToVB(), "wu -> vb conversion "+ + "failed: want %v, got %v", vb, wu.ToVB()) + require.Equal(t, WeightUnit(100), vb.ToWU(), "vb -> wu conversion "+ + "failed: want %v, got %v", 100, vb.ToWU()) +} diff --git a/lnwallet/btcwallet/psbt.go b/lnwallet/btcwallet/psbt.go index 0fdf76c38..ec88cd92f 100644 --- a/lnwallet/btcwallet/psbt.go +++ b/lnwallet/btcwallet/psbt.go @@ -15,6 +15,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/waddrmgr" "github.com/btcsuite/btcwallet/wallet" + "github.com/btcsuite/btcwallet/wtxmgr" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" @@ -60,6 +61,9 @@ var ( // imported public keys. For custom account, no key scope should be provided // as the coin selection key scope will always be used to generate the change // address. +// The function argument `allowUtxo` specifies a filter function for utxos +// during coin selection. It should return true for utxos that can be used and +// false for those that should be excluded. // // NOTE: If the packet doesn't contain any inputs, coin selection is performed // automatically. The account parameter must be non-empty as it determines which @@ -74,7 +78,8 @@ var ( func (b *BtcWallet) FundPsbt(packet *psbt.Packet, minConfs int32, feeRate chainfee.SatPerKWeight, accountName string, changeScope *waddrmgr.KeyScope, - strategy wallet.CoinSelectionStrategy) (int32, error) { + strategy wallet.CoinSelectionStrategy, + allowUtxo func(wtxmgr.Credit) bool) (int32, error) { // The fee rate is passed in using units of sat/kw, so we'll convert // this to sat/KB as the CreateSimpleTx method requires this unit. @@ -130,6 +135,9 @@ func (b *BtcWallet) FundPsbt(packet *psbt.Packet, minConfs int32, if changeScope != nil { opts = append(opts, wallet.WithCustomChangeScope(changeScope)) } + if allowUtxo != nil { + opts = append(opts, wallet.WithUtxoFilter(allowUtxo)) + } // Let the wallet handle coin selection and/or fee estimation based on // the partial TX information in the packet. diff --git a/lnwallet/chainfee/estimator.go b/lnwallet/chainfee/estimator.go index 76c235d1b..0ce9ae9b8 100644 --- a/lnwallet/chainfee/estimator.go +++ b/lnwallet/chainfee/estimator.go @@ -28,14 +28,6 @@ const ( // less than this will result in an error. minBlockTarget uint32 = 1 - // minFeeUpdateTimeout represents the minimum interval in which a - // WebAPIEstimator will request fresh fees from its API. - minFeeUpdateTimeout = 5 * time.Minute - - // maxFeeUpdateTimeout represents the maximum interval in which a - // WebAPIEstimator will request fresh fees from its API. - maxFeeUpdateTimeout = 20 * time.Minute - // WebAPIConnectionTimeout specifies the timeout value for connecting // to the api source. WebAPIConnectionTimeout = 5 * time.Second @@ -739,19 +731,43 @@ type WebAPIEstimator struct { // estimates. noCache bool + // minFeeUpdateTimeout represents the minimum interval in which the + // web estimator will request fresh fees from its API. + minFeeUpdateTimeout time.Duration + + // minFeeUpdateTimeout represents the maximum interval in which the + // web estimator will request fresh fees from its API. + maxFeeUpdateTimeout time.Duration + quit chan struct{} wg sync.WaitGroup } // NewWebAPIEstimator creates a new WebAPIEstimator from a given URL and a // fallback default fee. The fees are updated whenever a new block is mined. -func NewWebAPIEstimator(api WebAPIFeeSource, noCache bool) *WebAPIEstimator { - return &WebAPIEstimator{ - apiSource: api, - feeByBlockTarget: make(map[uint32]uint32), - noCache: noCache, - quit: make(chan struct{}), +func NewWebAPIEstimator(api WebAPIFeeSource, noCache bool, + minFeeUpdateTimeout time.Duration, + maxFeeUpdateTimeout time.Duration) (*WebAPIEstimator, error) { + + if minFeeUpdateTimeout == 0 || maxFeeUpdateTimeout == 0 { + return nil, fmt.Errorf("minFeeUpdateTimeout and " + + "maxFeeUpdateTimeout must be greater than 0") } + + if minFeeUpdateTimeout >= maxFeeUpdateTimeout { + return nil, fmt.Errorf("minFeeUpdateTimeout target of %v "+ + "cannot be greater than maxFeeUpdateTimeout of %v", + minFeeUpdateTimeout, maxFeeUpdateTimeout) + } + + return &WebAPIEstimator{ + apiSource: api, + feeByBlockTarget: make(map[uint32]uint32), + noCache: noCache, + quit: make(chan struct{}), + minFeeUpdateTimeout: minFeeUpdateTimeout, + maxFeeUpdateTimeout: maxFeeUpdateTimeout, + }, nil } // EstimateFeePerKW takes in a target for the number of blocks until an initial @@ -809,7 +825,12 @@ func (w *WebAPIEstimator) Start() error { w.started.Do(func() { log.Infof("Starting web API fee estimator") - w.updateFeeTicker = time.NewTicker(w.randomFeeUpdateTimeout()) + feeUpdateTimeout := w.randomFeeUpdateTimeout() + + log.Infof("Web API fee estimator using update timeout of %v", + feeUpdateTimeout) + + w.updateFeeTicker = time.NewTicker(feeUpdateTimeout) w.updateFeeEstimates() w.wg.Add(1) @@ -852,9 +873,11 @@ func (w *WebAPIEstimator) RelayFeePerKW() SatPerKWeight { // and maxFeeUpdateTimeout that will be used to determine how often the Estimator // should retrieve fresh fees from its API. func (w *WebAPIEstimator) randomFeeUpdateTimeout() time.Duration { - lower := int64(minFeeUpdateTimeout) - upper := int64(maxFeeUpdateTimeout) - return time.Duration(prand.Int63n(upper-lower) + lower) + lower := int64(w.minFeeUpdateTimeout) + upper := int64(w.maxFeeUpdateTimeout) + return time.Duration( + prand.Int63n(upper-lower) + lower, //nolint:gosec + ).Round(time.Second) } // getCachedFee takes a conf target and returns the cached fee rate. When the diff --git a/lnwallet/chainfee/estimator_test.go b/lnwallet/chainfee/estimator_test.go index fc16c9b12..51ab5bf29 100644 --- a/lnwallet/chainfee/estimator_test.go +++ b/lnwallet/chainfee/estimator_test.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "testing" + "time" "github.com/btcsuite/btcd/btcutil" "github.com/stretchr/testify/require" @@ -142,6 +143,9 @@ func TestWebAPIFeeEstimator(t *testing.T) { // Fee rates are in sat/kb. minFeeRate uint32 = 2000 // 500 sat/kw maxFeeRate uint32 = 4000 // 1000 sat/kw + + minFeeUpdateTimeout = 5 * time.Minute + maxFeeUpdateTimeout = 20 * time.Minute ) testCases := []struct { @@ -199,7 +203,9 @@ func TestWebAPIFeeEstimator(t *testing.T) { feeSource := &mockFeeSource{} feeSource.On("GetFeeMap").Return(feeRateResp, nil) - estimator := NewWebAPIEstimator(feeSource, false) + estimator, _ := NewWebAPIEstimator( + feeSource, false, minFeeUpdateTimeout, maxFeeUpdateTimeout, + ) // Test that requesting a fee when no fees have been cached won't fail. feeRate, err := estimator.EstimateFeePerKW(5) @@ -247,10 +253,15 @@ func TestGetCachedFee(t *testing.T) { minFeeRate uint32 = 100 maxFeeRate uint32 = 1000 + + minFeeUpdateTimeout = 5 * time.Minute + maxFeeUpdateTimeout = 20 * time.Minute ) // Create a dummy estimator without WebAPIFeeSource. - estimator := NewWebAPIEstimator(nil, false) + estimator, _ := NewWebAPIEstimator( + nil, false, minFeeUpdateTimeout, maxFeeUpdateTimeout, + ) // When the cache is empty, an error should be returned. cachedFee, err := estimator.getCachedFee(minTarget) @@ -315,3 +326,38 @@ func TestGetCachedFee(t *testing.T) { }) } } + +func TestRandomFeeUpdateTimeout(t *testing.T) { + t.Parallel() + + var ( + minFeeUpdateTimeout = 1 * time.Minute + maxFeeUpdateTimeout = 2 * time.Minute + ) + + estimator, _ := NewWebAPIEstimator( + nil, false, minFeeUpdateTimeout, maxFeeUpdateTimeout, + ) + + for i := 0; i < 1000; i++ { + timeout := estimator.randomFeeUpdateTimeout() + + require.GreaterOrEqual(t, timeout, minFeeUpdateTimeout) + require.LessOrEqual(t, timeout, maxFeeUpdateTimeout) + } +} + +func TestInvalidFeeUpdateTimeout(t *testing.T) { + t.Parallel() + + var ( + minFeeUpdateTimeout = 2 * time.Minute + maxFeeUpdateTimeout = 1 * time.Minute + ) + + _, err := NewWebAPIEstimator( + nil, false, minFeeUpdateTimeout, maxFeeUpdateTimeout, + ) + require.Error(t, err, "NewWebAPIEstimator should return an error "+ + "when minFeeUpdateTimeout > maxFeeUpdateTimeout") +} diff --git a/lnwallet/chainfee/rates.go b/lnwallet/chainfee/rates.go index 98cefc13b..f5294e4d1 100644 --- a/lnwallet/chainfee/rates.go +++ b/lnwallet/chainfee/rates.go @@ -5,6 +5,7 @@ import ( "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcutil" + "github.com/lightningnetwork/lnd/lntypes" ) const ( @@ -41,7 +42,7 @@ type SatPerKVByte btcutil.Amount // FeeForVSize calculates the fee resulting from this fee rate and the given // vsize in vbytes. -func (s SatPerKVByte) FeeForVSize(vbytes int64) btcutil.Amount { +func (s SatPerKVByte) FeeForVSize(vbytes lntypes.VByte) btcutil.Amount { return btcutil.Amount(s) * btcutil.Amount(vbytes) / 1000 } @@ -59,17 +60,23 @@ func (s SatPerKVByte) String() string { type SatPerKWeight btcutil.Amount // NewSatPerKWeight creates a new fee rate in sat/kw. -func NewSatPerKWeight(fee btcutil.Amount, weight uint64) SatPerKWeight { - return SatPerKWeight(fee.MulF64(1000 / float64(weight))) +func NewSatPerKWeight(fee btcutil.Amount, wu lntypes.WeightUnit) SatPerKWeight { + return SatPerKWeight(fee.MulF64(1000 / float64(wu))) } // FeeForWeight calculates the fee resulting from this fee rate and the given // weight in weight units (wu). -func (s SatPerKWeight) FeeForWeight(wu int64) btcutil.Amount { +func (s SatPerKWeight) FeeForWeight(wu lntypes.WeightUnit) btcutil.Amount { // The resulting fee is rounded down, as specified in BOLT#03. return btcutil.Amount(s) * btcutil.Amount(wu) / 1000 } +// FeeForVByte calculates the fee resulting from this fee rate and the given +// size in vbytes (vb). +func (s SatPerKWeight) FeeForVByte(vb lntypes.VByte) btcutil.Amount { + return s.FeePerKVByte().FeeForVSize(vb) +} + // FeePerKVByte converts the current fee rate from sat/kw to sat/kb. func (s SatPerKWeight) FeePerKVByte() SatPerKVByte { return SatPerKVByte(s * blockchain.WitnessScaleFactor) diff --git a/lnwallet/chancloser/chancloser.go b/lnwallet/chancloser/chancloser.go index bc9a1c903..47635c3af 100644 --- a/lnwallet/chancloser/chancloser.go +++ b/lnwallet/chancloser/chancloser.go @@ -240,7 +240,7 @@ func calcCoopCloseFee(chanType channeldb.ChannelType, weightEstimator.AddTxOutput(remoteOutput) } - totalWeight := int64(weightEstimator.Weight()) + totalWeight := weightEstimator.Weight() return idealFeeRate.FeeForWeight(totalWeight) } diff --git a/lnwallet/chanfunding/coin_select.go b/lnwallet/chanfunding/coin_select.go index 4cf50c574..9bf7f2dc0 100644 --- a/lnwallet/chanfunding/coin_select.go +++ b/lnwallet/chanfunding/coin_select.go @@ -116,7 +116,7 @@ func calculateFees(utxos []wallet.Coin, feeRate chainfee.SatPerKWeight, // Estimate the fee required for a transaction without a change // output. - totalWeight := int64(weightEstimate.Weight()) + totalWeight := weightEstimate.Weight() requiredFeeNoChange := feeRate.FeeForWeight(totalWeight) // Estimate the fee required for a transaction with a change output. @@ -137,7 +137,7 @@ func calculateFees(utxos []wallet.Coin, feeRate chainfee.SatPerKWeight, // Now that we have added the change output, redo the fee // estimate. - totalWeight = int64(weightEstimate.Weight()) + totalWeight = weightEstimate.Weight() requiredFeeWithChange := feeRate.FeeForWeight(totalWeight) return requiredFeeNoChange, requiredFeeWithChange, nil diff --git a/lnwallet/chanfunding/coin_select_test.go b/lnwallet/chanfunding/coin_select_test.go index 43027f32f..a6ff18bae 100644 --- a/lnwallet/chanfunding/coin_select_test.go +++ b/lnwallet/chanfunding/coin_select_test.go @@ -50,7 +50,7 @@ func fundingFee(feeRate chainfee.SatPerKWeight, numInput int, // nolint:unparam weightEstimate.AddP2TROutput() } - totalWeight := int64(weightEstimate.Weight()) + totalWeight := weightEstimate.Weight() return feeRate.FeeForWeight(totalWeight) } diff --git a/lnwallet/chanfunding/wallet_assembler.go b/lnwallet/chanfunding/wallet_assembler.go index a3ac49f3a..da78df49f 100644 --- a/lnwallet/chanfunding/wallet_assembler.go +++ b/lnwallet/chanfunding/wallet_assembler.go @@ -334,7 +334,8 @@ func (w *WalletAssembler) ProvisionChannel(r *Request) (Intent, error) { } for _, coin := range manuallySelectedCoins { if _, ok := unspent[coin.OutPoint]; !ok { - return fmt.Errorf("outpoint already spent: %v", + return fmt.Errorf("outpoint already spent or "+ + "locked by another subsystem: %v", coin.OutPoint) } } diff --git a/lnwallet/channel.go b/lnwallet/channel.go index fae68ff6c..3a28623c1 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -29,6 +29,7 @@ import ( "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" @@ -4062,7 +4063,7 @@ func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate { // an htlc even if our channel is drained locally. // See: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md func CalcFeeBuffer(feePerKw chainfee.SatPerKWeight, - commitWeight int64) lnwire.MilliSatoshi { + commitWeight lntypes.WeightUnit) lnwire.MilliSatoshi { // Account for a 100% in fee rate increase. bufferFeePerKw := 2 * feePerKw @@ -4135,7 +4136,7 @@ func (b BufferType) String() string { // and verifies that it does not become negative. This function returns the new // balance and the exact buffer amount (excluding the commitment fee). func (lc *LightningChannel) applyCommitFee( - balance lnwire.MilliSatoshi, commitWeight int64, + balance lnwire.MilliSatoshi, commitWeight lntypes.WeightUnit, feePerKw chainfee.SatPerKWeight, buffer BufferType) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, error) { @@ -5031,8 +5032,8 @@ func (lc *LightningChannel) ProcessChanSyncMsg( // If the updateState boolean is set true, the add and remove heights of the // HTLCs will be set to the next commitment height. func (lc *LightningChannel) computeView(view *HtlcView, remoteChain bool, - updateState bool) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, int64, - *HtlcView, error) { + updateState bool) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, + lntypes.WeightUnit, *HtlcView, error) { commitChain := lc.localCommitChain dustLimit := lc.channelState.LocalChanCfg.DustLimit @@ -5098,7 +5099,7 @@ func (lc *LightningChannel) computeView(view *HtlcView, remoteChain bool, // Now go through all HTLCs at this stage, to calculate the total // weight, needed to calculate the transaction fee. - var totalHtlcWeight int64 + var totalHtlcWeight lntypes.WeightUnit for _, htlc := range filteredHTLCView.OurUpdates { if HtlcIsDust( lc.channelState.ChanType, false, !remoteChain, @@ -7963,7 +7964,7 @@ type AnchorResolution struct { CommitFee btcutil.Amount // CommitWeight is the weight of the commit tx. - CommitWeight int64 + CommitWeight lntypes.WeightUnit } // LocalForceCloseSummary describes the final commitment state before the @@ -8652,7 +8653,7 @@ func NewAnchorResolution(chanState *channeldb.OpenChannel, return &AnchorResolution{ CommitAnchor: *outPoint, AnchorSignDescriptor: *signDesc, - CommitWeight: weight, + CommitWeight: lntypes.WeightUnit(weight), CommitFee: fee, }, nil } @@ -8678,7 +8679,7 @@ func (lc *LightningChannel) AvailableBalance() lnwire.MilliSatoshi { // this method. Additionally, the total weight of the next to be created // commitment is returned for accounting purposes. func (lc *LightningChannel) availableBalance( - buffer BufferType) (lnwire.MilliSatoshi, int64) { + buffer BufferType) (lnwire.MilliSatoshi, lntypes.WeightUnit) { // We'll grab the current set of log updates that the remote has // ACKed. @@ -8718,7 +8719,8 @@ func (lc *LightningChannel) availableBalance( // eating into our balance. It will make sure we won't violate the channel // reserve constraints for this amount. func (lc *LightningChannel) availableCommitmentBalance(view *HtlcView, - remoteChain bool, buffer BufferType) (lnwire.MilliSatoshi, int64) { + remoteChain bool, buffer BufferType) (lnwire.MilliSatoshi, + lntypes.WeightUnit) { // Compute the current balances for this commitment. This will take // into account HTLCs to determine the commit weight, which the diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index b3546b60a..d29d58dc3 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -849,7 +849,7 @@ func TestForceClose(t *testing.T) { type forceCloseTestCase struct { chanType channeldb.ChannelType - expectedCommitWeight int64 + expectedCommitWeight lntypes.WeightUnit anchorAmt btcutil.Amount } @@ -4820,7 +4820,7 @@ func TestChanAvailableBandwidth(t *testing.T) { ) assertBandwidthEstimateCorrect := func(aliceInitiate bool, - numNonDustHtlcsOnCommit int64) { + numNonDustHtlcsOnCommit lntypes.WeightUnit) { // With the HTLC's added, we'll now query the AvailableBalance // method for the current available channel bandwidth from @@ -5206,7 +5206,7 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { // fromt the given channel's POV. // When sending htlcs we enforce the feebuffer on the commitment // transaction. - remoteCommitWeight := func(lc *LightningChannel) int64 { + remoteCommitWeight := func(lc *LightningChannel) lntypes.WeightUnit { remoteACKedIndex := lc.localCommitChain.tip().theirMessageIndex htlcView := lc.fetchHTLCView(remoteACKedIndex, lc.localUpdateLog.logIndex) @@ -10492,8 +10492,8 @@ func TestApplyCommitmentFee(t *testing.T) { balanceBelowReserve = lnwire.NewMSatFromSatoshis(5_000) // commitment weight with an additional htlc. - commitWeight int64 = input.BaseAnchorCommitmentTxWeight + - input.HTLCWeight + commitWeight lntypes.WeightUnit = input. + BaseAnchorCommitmentTxWeight + input.HTLCWeight // fee rate of 10 sat/vbyte. feePerKw = chainfee.SatPerKWeight(2500) diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 082aca631..f2e4ecf1f 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -13,6 +13,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/tlv" @@ -472,7 +473,7 @@ func SecondLevelHtlcScript(chanType channeldb.ChannelType, initiator bool, } // CommitWeight returns the base commitment weight before adding HTLCs. -func CommitWeight(chanType channeldb.ChannelType) int64 { +func CommitWeight(chanType channeldb.ChannelType) lntypes.WeightUnit { switch { case chanType.IsTaproot(): return input.TaprootCommitWeight @@ -913,7 +914,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, // by the current fee-per-kw, then divide by 1000 to get the proper // fee. totalCommitWeight := CommitWeight(cb.chanState.ChanType) + - input.HTLCWeight*numHTLCs + lntypes.WeightUnit(input.HTLCWeight*numHTLCs) // With the weight known, we can now calculate the commitment fee, // ensuring that we account for any dust outputs trimmed above. diff --git a/lnwallet/interface.go b/lnwallet/interface.go index 59e6f5aab..a48e92560 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -468,7 +468,8 @@ type WalletController interface { FundPsbt(packet *psbt.Packet, minConfs int32, feeRate chainfee.SatPerKWeight, account string, changeScope *waddrmgr.KeyScope, - strategy base.CoinSelectionStrategy) (int32, error) + strategy base.CoinSelectionStrategy, + allowUtxo func(wtxmgr.Credit) bool) (int32, error) // SignPsbt expects a partial transaction with all inputs and outputs // fully declared and tries to sign all unsigned inputs that have all diff --git a/lnwallet/mock.go b/lnwallet/mock.go index 0146df57e..1873de79a 100644 --- a/lnwallet/mock.go +++ b/lnwallet/mock.go @@ -217,7 +217,8 @@ func (w *mockWalletController) ListLeasedOutputs() ( // FundPsbt currently does nothing. func (w *mockWalletController) FundPsbt(*psbt.Packet, int32, chainfee.SatPerKWeight, string, *waddrmgr.KeyScope, - base.CoinSelectionStrategy) (int32, error) { + base.CoinSelectionStrategy, func(utxo wtxmgr.Credit) bool) (int32, + error) { return 0, nil } diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index d6c6da77b..ae449ea02 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -14,6 +14,7 @@ import ( "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chanfunding" "github.com/lightningnetwork/lnd/lnwire" ) @@ -242,13 +243,15 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, // Based on the channel type, we determine the initial commit weight // and fee. - commitWeight := int64(input.CommitWeight) + commitWeight := input.CommitWeight if req.CommitType.IsTaproot() { commitWeight = input.TaprootCommitWeight } else if req.CommitType.HasAnchors() { - commitWeight = int64(input.AnchorCommitWeight) + commitWeight = input.AnchorCommitWeight } - commitFee := req.CommitFeePerKw.FeeForWeight(commitWeight) + commitFee := req.CommitFeePerKw.FeeForWeight( + lntypes.WeightUnit(commitWeight), + ) localFundingMSat := lnwire.NewMSatFromSatoshis(localFundingAmt) // TODO(halseth): make method take remote funding amount directly diff --git a/lnwallet/rpcwallet/rpcwallet.go b/lnwallet/rpcwallet/rpcwallet.go index 5a16490e5..13f435579 100644 --- a/lnwallet/rpcwallet/rpcwallet.go +++ b/lnwallet/rpcwallet/rpcwallet.go @@ -7,7 +7,7 @@ import ( "crypto/x509" "errors" "fmt" - "io/ioutil" + "os" "time" "github.com/btcsuite/btcd/btcec/v2" @@ -1259,7 +1259,7 @@ func extractSignature(in *psbt.PInput, func connectRPC(hostPort, tlsCertPath, macaroonPath string, timeout time.Duration) (*grpc.ClientConn, error) { - certBytes, err := ioutil.ReadFile(tlsCertPath) + certBytes, err := os.ReadFile(tlsCertPath) if err != nil { return nil, fmt.Errorf("error reading TLS cert file %v: %w", tlsCertPath, err) @@ -1271,7 +1271,7 @@ func connectRPC(hostPort, tlsCertPath, macaroonPath string, "certificate") } - macBytes, err := ioutil.ReadFile(macaroonPath) + macBytes, err := os.ReadFile(macaroonPath) if err != nil { return nil, fmt.Errorf("error reading macaroon file %v: %w", macaroonPath, err) diff --git a/lnwallet/test/test_interface.go b/lnwallet/test/test_interface.go index 0a6f20e5f..a8d1c5212 100644 --- a/lnwallet/test/test_interface.go +++ b/lnwallet/test/test_interface.go @@ -2949,6 +2949,11 @@ func waitForWalletSync(r *rpctest.Harness, w *lnwallet.LightningWallet) error { func testSingleFunderExternalFundingTx(miner *rpctest.Harness, alice, bob *lnwallet.LightningWallet, t *testing.T) { + // Define a filter function without any restrictions. + allowUtxo := func(lnwallet.Utxo) bool { + return true + } + // First, we'll obtain multi-sig keys from both Alice and Bob which // simulates them exchanging keys on a higher level. aliceFundingKey, err := alice.DeriveNextKey(keychain.KeyFamilyMultiSig) @@ -2965,7 +2970,9 @@ func testSingleFunderExternalFundingTx(miner *rpctest.Harness, // we'll create a new chanfunding.Assembler hacked by Alice's wallet. aliceChanFunder := chanfunding.NewWalletAssembler( chanfunding.WalletConfig{ - CoinSource: lnwallet.NewCoinSource(alice), + CoinSource: lnwallet.NewCoinSource( + alice, allowUtxo, + ), CoinSelectLocker: alice, CoinLeaser: alice, Signer: alice.Cfg.Signer, diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index 488064c42..d7ac5df3e 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -17,6 +17,7 @@ import ( "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" @@ -522,9 +523,10 @@ func calcStaticFee(chanType channeldb.ChannelType, numHTLCs int) btcutil.Amount htlcWeight = 172 feePerKw = btcutil.Amount(24/4) * 1000 ) - return feePerKw * - (btcutil.Amount(CommitWeight(chanType) + - htlcWeight*int64(numHTLCs))) / 1000 + htlcsWeight := htlcWeight * int64(numHTLCs) + totalWeight := CommitWeight(chanType) + lntypes.WeightUnit(htlcsWeight) + + return feePerKw * (btcutil.Amount(totalWeight)) / 1000 } // ForceStateTransition executes the necessary interaction between the two diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index fc7ba902e..464597e12 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -8,8 +8,8 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net" + "os" "sort" "strings" "testing" @@ -230,7 +230,7 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) { var testCases []testCase - jsonText, err := ioutil.ReadFile(set.jsonFile) + jsonText, err := os.ReadFile(set.jsonFile) require.NoError(t, err) err = json.Unmarshal(jsonText, &testCases) @@ -915,7 +915,7 @@ func createTestChannelsForVectors(tc *testContext, chanType channeldb.ChannelTyp // Create the initial commitment transactions for the channel. feePerKw := chainfee.SatPerKWeight(feeRate) - commitWeight := int64(input.CommitWeight) + commitWeight := lntypes.WeightUnit(input.CommitWeight) if chanType.HasAnchors() { commitWeight = input.AnchorCommitWeight } diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index afb8b8f03..6a2f443ae 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -213,6 +213,15 @@ type InitFundingReserveMsg struct { // used. ChanFunder chanfunding.Assembler + // AllowUtxoForFunding enables the channel funding workflow to restrict + // the selection of utxos when selecting the inputs for the channel + // opening. This does ONLY apply for the internal wallet backed channel + // opening case. + // + // NOTE: This is very useful when opening channels with unconfirmed + // inputs to make sure stable non-replaceable inputs are used. + AllowUtxoForFunding func(Utxo) bool + // ZeroConf is a boolean that is true if a zero-conf channel was // negotiated. ZeroConf bool @@ -886,7 +895,9 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg // P2WPKH dust limit and to avoid threading through two // different dust limits. cfg := chanfunding.WalletConfig{ - CoinSource: &CoinSource{l}, + CoinSource: NewCoinSource( + l, req.AllowUtxoForFunding, + ), CoinSelectLocker: l, CoinLeaser: l, Signer: l.Cfg.Signer, @@ -2701,12 +2712,16 @@ func (l *LightningWallet) CancelRebroadcast(txid chainhash.Hash) { // CoinSource is a wrapper around the wallet that implements the // chanfunding.CoinSource interface. type CoinSource struct { - wallet *LightningWallet + wallet *LightningWallet + allowUtxo func(Utxo) bool } // NewCoinSource creates a new instance of the CoinSource wrapper struct. -func NewCoinSource(w *LightningWallet) *CoinSource { - return &CoinSource{wallet: w} +func NewCoinSource(w *LightningWallet, allowUtxo func(Utxo) bool) *CoinSource { + return &CoinSource{ + wallet: w, + allowUtxo: allowUtxo, + } } // ListCoins returns all UTXOs from the source that have between @@ -2722,7 +2737,18 @@ func (c *CoinSource) ListCoins(minConfs int32, } var coins []wallet.Coin + for _, utxo := range utxos { + // If there is a filter function supplied all utxos not adhering + // to these conditions will be discared. + if c.allowUtxo != nil && !c.allowUtxo(*utxo) { + walletLog.Infof("Cannot use unconfirmed "+ + "utxo=%v because it is unstable and could be "+ + "replaced", utxo.OutPoint) + + continue + } + coins = append(coins, wallet.Coin{ TxOut: wire.TxOut{ Value: int64(utxo.Value), diff --git a/lnwire/features.go b/lnwire/features.go index ab6facc75..e4dd7f4f8 100644 --- a/lnwire/features.go +++ b/lnwire/features.go @@ -141,6 +141,14 @@ const ( // transactions, which also imply anchor commitments. AnchorsZeroFeeHtlcTxOptional FeatureBit = 23 + // RouteBlindingRequired is a required feature bit that signals that + // the node supports blinded payments. + RouteBlindingRequired FeatureBit = 24 + + // RouteBlindingOptional is an optional feature bit that signals that + // the node supports blinded payments. + RouteBlindingOptional FeatureBit = 25 + // ShutdownAnySegwitRequired is an required feature bit that signals // that the sender is able to properly handle/parse segwit witness // programs up to version 16. This enables utilization of Taproot @@ -315,6 +323,8 @@ var Features = map[FeatureBit]string{ ScidAliasOptional: "scid-alias", ZeroConfRequired: "zero-conf", ZeroConfOptional: "zero-conf", + RouteBlindingRequired: "route-blinding", + RouteBlindingOptional: "route-blinding", ShutdownAnySegwitRequired: "shutdown-any-segwit", ShutdownAnySegwitOptional: "shutdown-any-segwit", SimpleTaprootChannelsRequiredFinal: "simple-taproot-chans", diff --git a/lnwire/onion_error.go b/lnwire/onion_error.go index 8b7fc733b..66db6a9f5 100644 --- a/lnwire/onion_error.go +++ b/lnwire/onion_error.go @@ -7,7 +7,6 @@ import ( "encoding/binary" "fmt" "io" - "io/ioutil" "github.com/davecgh/go-spew/spew" "github.com/go-errors/errors" @@ -1304,7 +1303,7 @@ func DecodeFailure(r io.Reader, pver uint32) (FailureMessage, error) { return nil, fmt.Errorf("unable to read pad len: %w", err) } - if _, err := io.CopyN(ioutil.Discard, r, int64(padLength)); err != nil { + if _, err := io.CopyN(io.Discard, r, int64(padLength)); err != nil { return nil, fmt.Errorf("unable to read padding %w", err) } diff --git a/make/builder.Dockerfile b/make/builder.Dockerfile index efa3d052a..66946e0a4 100644 --- a/make/builder.Dockerfile +++ b/make/builder.Dockerfile @@ -4,7 +4,7 @@ # /dev.Dockerfile # /.github/workflows/main.yml # /.github/workflows/release.yml -FROM golang:1.21.0-bookworm +FROM golang:1.22.3-bookworm MAINTAINER Olaoluwa Osuntokun diff --git a/make/testing_flags.mk b/make/testing_flags.mk index 3fecb073a..b2db6861c 100644 --- a/make/testing_flags.mk +++ b/make/testing_flags.mk @@ -2,7 +2,9 @@ DEV_TAGS = dev RPC_TAGS = autopilotrpc chainrpc invoicesrpc neutrinorpc peersrpc routerrpc signrpc verrpc walletrpc watchtowerrpc wtclientrpc LOG_TAGS = TEST_FLAGS = -ITEST_FLAGS = +ITEST_FLAGS = +ITEST_COVERAGE = +COLLECT_ITEST_COVERAGE = EXEC_SUFFIX = COVER_PKG = $$(go list -deps -tags="$(DEV_TAGS)" ./... | grep '$(PKG)' | grep -v lnrpc) NUM_ITEST_TRANCHES = 4 @@ -77,6 +79,12 @@ ifneq ($(tags),) DEV_TAGS += ${tags} endif +# Enable integration test coverage (requires Go >= 1.20.0). +ifneq ($(cover),) +ITEST_COVERAGE = -cover +COLLECT_ITEST_COVERAGE = go tool covdata textfmt -i=itest/cover -o coverage.txt +endif + # Define the log tags that will be applied only when running unit tests. If none # are provided, we default to "nolog" which will be silent. ifneq ($(log),) diff --git a/peer/brontide.go b/peer/brontide.go index d86f98b34..6ec409685 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -7,6 +7,7 @@ import ( "fmt" "math/rand" "net" + "strings" "sync" "sync/atomic" "time" @@ -77,6 +78,16 @@ const ( // ErrorBufferSize is the number of historic peer errors that we store. ErrorBufferSize = 10 + + // pongSizeCeiling is the upper bound on a uniformly distributed random + // variable that we use for requesting pong responses. We don't use the + // MaxPongBytes (upper bound accepted by the protocol) because it is + // needlessly wasteful of precious Tor bandwidth for little to no gain. + pongSizeCeiling = 4096 + + // torTimeoutMultiplier is the scaling factor we use on network timeouts + // for Tor peers. + torTimeoutMultiplier = 3 ) var ( @@ -403,6 +414,23 @@ type Brontide struct { bytesReceived uint64 bytesSent uint64 + // isTorConnection is a flag that indicates whether or not we believe + // the remote peer is a tor connection. It is not always possible to + // know this with certainty but we have heuristics we use that should + // catch most cases. + // + // NOTE: We judge the tor-ness of a connection by if the remote peer has + // ".onion" in the address OR if it's connected over localhost. + // This will miss cases where our peer is connected to our clearnet + // address over the tor network (via exit nodes). It will also misjudge + // actual localhost connections as tor. We need to include this because + // inbound connections to our tor address will appear to come from the + // local socks5 proxy. This heuristic is only used to expand the timeout + // window for peers so it is OK to misjudge this. If you use this field + // for any other purpose you should seriously consider whether or not + // this heuristic is good enough for your use case. + isTorConnection bool + pingManager *PingManager // lastPingPayload stores an unsafe pointer wrapped as an atomic @@ -553,6 +581,12 @@ func NewBrontide(cfg Config) *Brontide { msgRouter: msgRouter, } + if cfg.Conn != nil && cfg.Conn.RemoteAddr() != nil { + remoteAddr := cfg.Conn.RemoteAddr().String() + p.isTorConnection = strings.Contains(remoteAddr, ".onion") || + strings.Contains(remoteAddr, "127.0.0.1") + } + var ( lastBlockHeader *wire.BlockHeader lastSerializedBlockHeader [wire.MaxBlockHeaderPayload]byte @@ -583,25 +617,24 @@ func NewBrontide(cfg Config) *Brontide { return lastSerializedBlockHeader[:] } - // TODO(roasbeef): make dynamic in order to - // create fake cover traffic - // NOTE(proofofkeags): this was changed to be - // dynamic to allow better pong identification, - // however, more thought is needed to make this - // actually usable as a traffic decoy + // TODO(roasbeef): make dynamic in order to create fake cover traffic. + // + // NOTE(proofofkeags): this was changed to be dynamic to allow better + // pong identification, however, more thought is needed to make this + // actually usable as a traffic decoy. randPongSize := func() uint16 { return uint16( // We don't need cryptographic randomness here. /* #nosec */ - rand.Intn(lnwire.MaxPongBytes + 1), + rand.Intn(pongSizeCeiling) + 1, ) } p.pingManager = NewPingManager(&PingManagerConfig{ NewPingPayload: newPingPayload, NewPongSize: randPongSize, - IntervalDuration: pingInterval, - TimeoutDuration: pingTimeout, + IntervalDuration: p.scaleTimeout(pingInterval), + TimeoutDuration: p.scaleTimeout(pingTimeout), SendPing: func(ping *lnwire.Ping) { p.queueMsg(ping, nil) }, @@ -1186,8 +1219,8 @@ func (p *Brontide) addLink(chanPoint *wire.OutPoint, ), BatchSize: p.cfg.ChannelCommitBatchSize, UnsafeReplay: p.cfg.UnsafeReplay, - MinFeeUpdateTimeout: htlcswitch.DefaultMinLinkFeeUpdateTimeout, - MaxFeeUpdateTimeout: htlcswitch.DefaultMaxLinkFeeUpdateTimeout, + MinUpdateTimeout: htlcswitch.DefaultMinLinkFeeUpdateTimeout, + MaxUpdateTimeout: htlcswitch.DefaultMaxLinkFeeUpdateTimeout, OutgoingCltvRejectDelta: p.cfg.OutgoingCltvRejectDelta, TowerClient: p.cfg.TowerClient, MaxOutgoingCltvExpiry: p.cfg.MaxOutgoingCltvExpiry, @@ -1292,16 +1325,14 @@ func (p *Brontide) Disconnect(reason error) { p.log.Infof(err.Error()) + // Stop PingManager before closing TCP connection. + p.pingManager.Stop() + // Ensure that the TCP connection is properly closed before continuing. p.cfg.Conn.Close() close(p.quit) - if err := p.pingManager.Stop(); err != nil { - p.log.Errorf("couldn't stop pingManager during disconnect: %v", - err) - } - p.msgRouter.WhenSome(func(router protofsm.MsgRouter) { router.Stop() }) @@ -1340,7 +1371,9 @@ func (p *Brontide) readNextMessage() (lnwire.Message, error) { // pool. We do so only after the task has been scheduled to // ensure the deadline doesn't expire while the message is in // the process of being scheduled. - readDeadline := time.Now().Add(readMessageTimeout) + readDeadline := time.Now().Add( + p.scaleTimeout(readMessageTimeout), + ) readErr := noiseConn.SetReadDeadline(readDeadline) if readErr != nil { return readErr @@ -2241,7 +2274,9 @@ func (p *Brontide) writeMessage(msg lnwire.Message) error { flushMsg := func() error { // Ensure the write deadline is set before we attempt to send // the message. - writeDeadline := time.Now().Add(writeMessageTimeout) + writeDeadline := time.Now().Add( + p.scaleTimeout(writeMessageTimeout), + ) err := noiseConn.SetWriteDeadline(writeDeadline) if err != nil { return err @@ -4218,3 +4253,15 @@ func (p *Brontide) sendLinkUpdateMsg(cid lnwire.ChannelID, msg lnwire.Message) { // continue processing message. chanStream.AddMsg(msg) } + +// scaleTimeout multiplies the argument duration by a constant factor depending +// on variious heuristics. Currently this is only used to check whether our peer +// appears to be connected over Tor and relaxes the timout deadline. However, +// this is subject to change and should be treated as opaque. +func (p *Brontide) scaleTimeout(timeout time.Duration) time.Duration { + if p.isTorConnection { + return timeout * time.Duration(torTimeoutMultiplier) + } + + return timeout +} diff --git a/peer/brontide_test.go b/peer/brontide_test.go index 8ea8846b2..d2f3fc7bc 100644 --- a/peer/brontide_test.go +++ b/peer/brontide_test.go @@ -6,22 +6,19 @@ import ( "testing" "time" - "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" - "github.com/lightningnetwork/lnd/channelnotifier" "github.com/lightningnetwork/lnd/contractcourt" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch" - "github.com/lightningnetwork/lnd/lntest/mock" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chancloser" "github.com/lightningnetwork/lnd/lnwire" - "github.com/lightningnetwork/lnd/pool" "github.com/stretchr/testify/require" ) @@ -39,20 +36,14 @@ var ( func TestPeerChannelClosureShutdownResponseLinkRemoved(t *testing.T) { t.Parallel() - notifier := &mock.ChainNotifier{ - SpendChan: make(chan *chainntnfs.SpendDetail), - EpochChan: make(chan *chainntnfs.BlockEpoch), - ConfChan: make(chan *chainntnfs.TxConfirmation), - } - broadcastTxChan := make(chan *wire.MsgTx) - - mockSwitch := &mockMessageSwitch{} - - alicePeer, bobChan, err := createTestPeer( - t, notifier, broadcastTxChan, noUpdate, mockSwitch, - ) + harness, err := createTestPeerWithChannel(t, noUpdate) require.NoError(t, err, "unable to create test channels") + var ( + alicePeer = harness.peer + bobChan = harness.channel + ) + chanPoint := bobChan.ChannelPoint() chanID := lnwire.NewChanIDFromOutPoint(chanPoint) @@ -87,20 +78,17 @@ func TestPeerChannelClosureShutdownResponseLinkRemoved(t *testing.T) { func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) { t.Parallel() - notifier := &mock.ChainNotifier{ - SpendChan: make(chan *chainntnfs.SpendDetail), - EpochChan: make(chan *chainntnfs.BlockEpoch), - ConfChan: make(chan *chainntnfs.TxConfirmation), - } - broadcastTxChan := make(chan *wire.MsgTx) - - mockSwitch := &mockMessageSwitch{} - - alicePeer, bobChan, err := createTestPeer( - t, notifier, broadcastTxChan, noUpdate, mockSwitch, - ) + harness, err := createTestPeerWithChannel(t, noUpdate) require.NoError(t, err, "unable to create test channels") + var ( + alicePeer = harness.peer + bobChan = harness.channel + mockSwitch = harness.mockSwitch + broadcastTxChan = harness.publishTx + notifier = harness.notifier + ) + chanPoint := bobChan.ChannelPoint() chanID := lnwire.NewChanIDFromOutPoint(chanPoint) @@ -192,20 +180,17 @@ func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) { func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) { t.Parallel() - notifier := &mock.ChainNotifier{ - SpendChan: make(chan *chainntnfs.SpendDetail), - EpochChan: make(chan *chainntnfs.BlockEpoch), - ConfChan: make(chan *chainntnfs.TxConfirmation), - } - broadcastTxChan := make(chan *wire.MsgTx) - - mockSwitch := &mockMessageSwitch{} - - alicePeer, bobChan, err := createTestPeer( - t, notifier, broadcastTxChan, noUpdate, mockSwitch, - ) + harness, err := createTestPeerWithChannel(t, noUpdate) require.NoError(t, err, "unable to create test channels") + var ( + bobChan = harness.channel + alicePeer = harness.peer + mockSwitch = harness.mockSwitch + broadcastTxChan = harness.publishTx + notifier = harness.notifier + ) + chanPoint := bobChan.ChannelPoint() chanID := lnwire.NewChanIDFromOutPoint(chanPoint) mockLink := newMockUpdateHandler(chanID) @@ -316,20 +301,17 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) { func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) { t.Parallel() - notifier := &mock.ChainNotifier{ - SpendChan: make(chan *chainntnfs.SpendDetail), - EpochChan: make(chan *chainntnfs.BlockEpoch), - ConfChan: make(chan *chainntnfs.TxConfirmation), - } - broadcastTxChan := make(chan *wire.MsgTx) - - mockSwitch := &mockMessageSwitch{} - - alicePeer, bobChan, err := createTestPeer( - t, notifier, broadcastTxChan, noUpdate, mockSwitch, - ) + harness, err := createTestPeerWithChannel(t, noUpdate) require.NoError(t, err, "unable to create test channels") + var ( + bobChan = harness.channel + alicePeer = harness.peer + mockSwitch = harness.mockSwitch + broadcastTxChan = harness.publishTx + notifier = harness.notifier + ) + chanPoint := bobChan.ChannelPoint() chanID := lnwire.NewChanIDFromOutPoint(chanPoint) @@ -503,20 +485,17 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) { func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) { t.Parallel() - notifier := &mock.ChainNotifier{ - SpendChan: make(chan *chainntnfs.SpendDetail), - EpochChan: make(chan *chainntnfs.BlockEpoch), - ConfChan: make(chan *chainntnfs.TxConfirmation), - } - broadcastTxChan := make(chan *wire.MsgTx) - - mockSwitch := &mockMessageSwitch{} - - alicePeer, bobChan, err := createTestPeer( - t, notifier, broadcastTxChan, noUpdate, mockSwitch, - ) + harness, err := createTestPeerWithChannel(t, noUpdate) require.NoError(t, err, "unable to create test channels") + var ( + alicePeer = harness.peer + bobChan = harness.channel + mockSwitch = harness.mockSwitch + broadcastTxChan = harness.publishTx + notifier = harness.notifier + ) + chanPoint := bobChan.ChannelPoint() chanID := lnwire.NewChanIDFromOutPoint(chanPoint) mockLink := newMockUpdateHandler(chanID) @@ -830,31 +809,27 @@ func TestCustomShutdownScript(t *testing.T) { test := test t.Run(test.name, func(t *testing.T) { - notifier := &mock.ChainNotifier{ - SpendChan: make(chan *chainntnfs.SpendDetail), - EpochChan: make(chan *chainntnfs.BlockEpoch), - ConfChan: make(chan *chainntnfs.TxConfirmation), - } - broadcastTxChan := make(chan *wire.MsgTx) - - mockSwitch := &mockMessageSwitch{} - // Open a channel. - alicePeer, bobChan, err := createTestPeer( - t, notifier, broadcastTxChan, test.update, - mockSwitch, + harness, err := createTestPeerWithChannel( + t, test.update, ) if err != nil { t.Fatalf("unable to create test channels: %v", err) } + var ( + alicePeer = harness.peer + bobChan = harness.channel + mockSwitch = harness.mockSwitch + ) + chanPoint := bobChan.ChannelPoint() chanID := lnwire.NewChanIDFromOutPoint(chanPoint) mockLink := newMockUpdateHandler(chanID) mockSwitch.links = append(mockSwitch.links, mockLink) - // Request initiator to cooperatively close the channel, with - // a specified delivery address. + // Request initiator to cooperatively close the channel, + // with a specified delivery address. updateChan := make(chan interface{}, 1) errChan := make(chan error, 1) closeCommand := htlcswitch.ChanClose{ @@ -1000,35 +975,23 @@ func TestStaticRemoteDowngrade(t *testing.T) { test := test t.Run(test.name, func(t *testing.T) { - writeBufferPool := pool.NewWriteBuffer( - pool.DefaultWriteBufferGCInterval, - pool.DefaultWriteBufferExpiryInterval, + params := createTestPeer(t) + + var ( + p = params.peer + mockConn = params.mockConn + writePool = p.cfg.WritePool ) - - writePool := pool.NewWrite( - writeBufferPool, 1, timeout, - ) - require.NoError(t, writePool.Start()) - - mockConn := newMockConn(t, 1) - - p := Brontide{ - cfg: Config{ - LegacyFeatures: legacy, - Features: test.features, - Conn: mockConn, - WritePool: writePool, - PongBuf: make([]byte, lnwire.MaxPongBytes), - }, - log: peerLog, - } + // Set feature bits. + p.cfg.LegacyFeatures = legacy + p.cfg.Features = test.features var b bytes.Buffer _, err := lnwire.WriteMessage(&b, test.expectedInit, 0) require.NoError(t, err) - // Send our init message, assert that we write our expected message - // and shutdown our write pool. + // Send our init message, assert that we write our + // expected message and shutdown our write pool. require.NoError(t, p.sendInitMsg(test.legacy)) mockConn.assertWrite(b.Bytes()) require.NoError(t, writePool.Stop()) @@ -1056,101 +1019,20 @@ func genScript(t *testing.T, address string) lnwire.DeliveryAddress { func TestPeerCustomMessage(t *testing.T) { t.Parallel() - // Set up node Alice. - dbAlice, err := channeldb.Open(t.TempDir()) + params := createTestPeer(t) + + var ( + mockConn = params.mockConn + alicePeer = params.peer + receivedCustomChan = params.customChan + remoteKey = alicePeer.PubKey() + ) + + // Start peer. + startPeerDone := startPeer(t, mockConn, alicePeer) + _, err := fn.RecvOrTimeout(startPeerDone, 2*timeout) require.NoError(t, err) - aliceKey, err := btcec.NewPrivateKey() - require.NoError(t, err) - - writeBufferPool := pool.NewWriteBuffer( - pool.DefaultWriteBufferGCInterval, - pool.DefaultWriteBufferExpiryInterval, - ) - - writePool := pool.NewWrite( - writeBufferPool, 1, timeout, - ) - require.NoError(t, writePool.Start()) - - readBufferPool := pool.NewReadBuffer( - pool.DefaultReadBufferGCInterval, - pool.DefaultReadBufferExpiryInterval, - ) - - readPool := pool.NewRead( - readBufferPool, 1, timeout, - ) - require.NoError(t, readPool.Start()) - - mockConn := newMockConn(t, 1) - - receivedCustomChan := make(chan *customMsg) - - remoteKey := [33]byte{8} - - notifier := &mock.ChainNotifier{ - SpendChan: make(chan *chainntnfs.SpendDetail), - EpochChan: make(chan *chainntnfs.BlockEpoch), - ConfChan: make(chan *chainntnfs.TxConfirmation), - } - - // TODO(yy): change ChannelNotifier to be an interface. - channelNotifier := channelnotifier.New(dbAlice.ChannelStateDB()) - require.NoError(t, channelNotifier.Start()) - t.Cleanup(func() { - require.NoError(t, channelNotifier.Stop(), - "stop channel notifier failed") - }) - - alicePeer := NewBrontide(Config{ - PubKeyBytes: remoteKey, - ChannelDB: dbAlice.ChannelStateDB(), - Addr: &lnwire.NetAddress{ - IdentityKey: aliceKey.PubKey(), - }, - PrunePersistentPeerConnection: func([33]byte) {}, - Features: lnwire.EmptyFeatureVector(), - LegacyFeatures: lnwire.EmptyFeatureVector(), - WritePool: writePool, - ReadPool: readPool, - Conn: mockConn, - ChainNotifier: notifier, - HandleCustomMessage: func( - peer [33]byte, msg *lnwire.Custom) error { - - receivedCustomChan <- &customMsg{ - peer: peer, - msg: *msg, - } - return nil - }, - PongBuf: make([]byte, lnwire.MaxPongBytes), - ChannelNotifier: channelNotifier, - }) - - // Set up the init sequence. - go func() { - // Read init message. - <-mockConn.writtenMessages - - // Write the init reply message. - initReplyMsg := lnwire.NewInitMessage( - lnwire.NewRawFeatureVector( - lnwire.DataLossProtectRequired, - ), - lnwire.NewRawFeatureVector(), - ) - var b bytes.Buffer - _, err = lnwire.WriteMessage(&b, initReplyMsg, 0) - require.NoError(t, err) - - mockConn.readMessages <- b.Bytes() - }() - - // Start the peer. - require.NoError(t, alicePeer.Start()) - // Send a custom message. customMsg, err := lnwire.NewCustom( lnwire.MessageType(40000), []byte{1, 2, 3}, @@ -1185,21 +1067,12 @@ func TestUpdateNextRevocation(t *testing.T) { require := require.New(t) - // TODO(yy): create interface for lnwallet.LightningChannel so we can - // easily mock it without the following setups. - notifier := &mock.ChainNotifier{ - SpendChan: make(chan *chainntnfs.SpendDetail), - EpochChan: make(chan *chainntnfs.BlockEpoch), - ConfChan: make(chan *chainntnfs.TxConfirmation), - } - broadcastTxChan := make(chan *wire.MsgTx) - mockSwitch := &mockMessageSwitch{} - - alicePeer, bobChan, err := createTestPeer( - t, notifier, broadcastTxChan, noUpdate, mockSwitch, - ) + harness, err := createTestPeerWithChannel(t, noUpdate) require.NoError(err, "unable to create test channels") + bobChan := harness.channel + alicePeer := harness.peer + // testChannel is used to test the updateNextRevocation function. testChannel := bobChan.State() @@ -1412,30 +1285,22 @@ func TestHandleRemovePendingChannel(t *testing.T) { func TestStartupWriteMessageRace(t *testing.T) { t.Parallel() - // Set up parameters for createTestPeer. - notifier := &mock.ChainNotifier{ - SpendChan: make(chan *chainntnfs.SpendDetail), - EpochChan: make(chan *chainntnfs.BlockEpoch), - ConfChan: make(chan *chainntnfs.TxConfirmation), - } - broadcastTxChan := make(chan *wire.MsgTx) - mockSwitch := &mockMessageSwitch{} - - // Use a callback to extract the channel created by createTestPeer, so - // we can mark it borked below. We can't mark it borked within the - // callback, since the channel hasn't been saved to the DB yet when the - // callback executes. + // Use a callback to extract the channel created by + // createTestPeerWithChannel, so we can mark it borked below. + // We can't mark it borked within the callback, since the channel hasn't + // been saved to the DB yet when the callback executes. var channel *channeldb.OpenChannel getChannels := func(a, b *channeldb.OpenChannel) { channel = a } - // createTestPeer creates a peer and a channel with that peer. - peer, _, err := createTestPeer( - t, notifier, broadcastTxChan, getChannels, mockSwitch, - ) + // createTestPeerWithChannel creates a peer and a channel with that + // peer. + harness, err := createTestPeerWithChannel(t, getChannels) require.NoError(t, err, "unable to create test channel") + peer := harness.peer + // Avoid the need to mock the channel graph by marking the channel // borked. Borked channels still get a reestablish message sent on // reconnect, while skipping channel graph checks and link creation. @@ -1445,58 +1310,21 @@ func TestStartupWriteMessageRace(t *testing.T) { mockConn := newMockConn(t, 2) peer.cfg.Conn = mockConn - // Set up other configuration necessary to successfully execute - // peer.Start(). - peer.cfg.LegacyFeatures = lnwire.EmptyFeatureVector() - writeBufferPool := pool.NewWriteBuffer( - pool.DefaultWriteBufferGCInterval, - pool.DefaultWriteBufferExpiryInterval, - ) - writePool := pool.NewWrite( - writeBufferPool, 1, timeout, - ) - require.NoError(t, writePool.Start()) - peer.cfg.WritePool = writePool - readBufferPool := pool.NewReadBuffer( - pool.DefaultReadBufferGCInterval, - pool.DefaultReadBufferExpiryInterval, - ) - readPool := pool.NewRead( - readBufferPool, 1, timeout, - ) - require.NoError(t, readPool.Start()) - peer.cfg.ReadPool = readPool - // Send a message while starting the peer. As the peer starts up, it // should not trigger a data race between the sending of this message // and the sending of the channel reestablish message. - sendPingDone := make(chan struct{}) + var sendPingDone = make(chan struct{}) go func() { require.NoError(t, peer.SendMessage(true, lnwire.NewPing(0))) close(sendPingDone) }() - // Handle init messages. - go func() { - // Read init message. - <-mockConn.writtenMessages - - // Write the init reply message. - initReplyMsg := lnwire.NewInitMessage( - lnwire.NewRawFeatureVector( - lnwire.DataLossProtectRequired, - ), - lnwire.NewRawFeatureVector(), - ) - var b bytes.Buffer - _, err = lnwire.WriteMessage(&b, initReplyMsg, 0) - require.NoError(t, err) - - mockConn.readMessages <- b.Bytes() - }() - // Start the peer. No data race should occur. - require.NoError(t, peer.Start()) + startPeerDone := startPeer(t, mockConn, peer) + + // Ensure startup is complete. + _, err = fn.RecvOrTimeout(startPeerDone, 2*timeout) + require.NoError(t, err) // Ensure messages were sent during startup. <-sendPingDone @@ -1516,21 +1344,12 @@ func TestStartupWriteMessageRace(t *testing.T) { func TestRemovePendingChannel(t *testing.T) { t.Parallel() - // Set up parameters for createTestPeer. - notifier := &mock.ChainNotifier{ - SpendChan: make(chan *chainntnfs.SpendDetail), - EpochChan: make(chan *chainntnfs.BlockEpoch), - ConfChan: make(chan *chainntnfs.TxConfirmation), - } - broadcastTxChan := make(chan *wire.MsgTx) - mockSwitch := &mockMessageSwitch{} - - // createTestPeer creates a peer and a channel with that peer. - peer, _, err := createTestPeer( - t, notifier, broadcastTxChan, noUpdate, mockSwitch, - ) + // createTestPeerWithChannel creates a peer and a channel. + harness, err := createTestPeerWithChannel(t, noUpdate) require.NoError(t, err, "unable to create test channel") + peer := harness.peer + // Add a pending channel to the peer Alice. errChan := make(chan error, 1) pendingChanID := lnwire.ChannelID{1} diff --git a/peer/ping_manager.go b/peer/ping_manager.go index c456a0581..f5c6180be 100644 --- a/peer/ping_manager.go +++ b/peer/ping_manager.go @@ -196,12 +196,10 @@ func (m *PingManager) pingHandler() { } } -// Stop interrupts the goroutines that the PingManager owns. Can only be called -// when the PingManager is running. -func (m *PingManager) Stop() error { +// Stop interrupts the goroutines that the PingManager owns. +func (m *PingManager) Stop() { if m.pingTicker == nil { - return errors.New("PingManager cannot be stopped because it " + - "isn't running") + return } m.stopped.Do(func() { @@ -211,8 +209,6 @@ func (m *PingManager) Stop() error { m.pingTicker.Stop() m.pingTimeout.Stop() }) - - return nil } // setPingState is a private method to keep track of all of the fields we need diff --git a/peer/ping_manager_test.go b/peer/ping_manager_test.go index bdfeeb6af..0f4c3be49 100644 --- a/peer/ping_manager_test.go +++ b/peer/ping_manager_test.go @@ -83,6 +83,6 @@ func TestPingManager(t *testing.T) { require.False(t, test.result) } - require.NoError(t, mgr.Stop(), "Could not stop pingManager") + mgr.Stop() } } diff --git a/peer/test_utils.go b/peer/test_utils.go index 05bfe6ad4..8667b04cd 100644 --- a/peer/test_utils.go +++ b/peer/test_utils.go @@ -18,6 +18,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channelnotifier" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -27,6 +28,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/netann" + "github.com/lightningnetwork/lnd/pool" "github.com/lightningnetwork/lnd/queue" "github.com/lightningnetwork/lnd/shachain" "github.com/stretchr/testify/require" @@ -48,32 +50,52 @@ var ( testKeyLoc = keychain.KeyLocator{Family: keychain.KeyFamilyNodeKey} ) -// noUpdate is a function which can be used as a parameter in createTestPeer to -// call the setup code with no custom values on the channels set up. +// noUpdate is a function which can be used as a parameter in +// createTestPeerWithChannel to call the setup code with no custom values on +// the channels set up. var noUpdate = func(a, b *channeldb.OpenChannel) {} -// createTestPeer creates a channel between two nodes, and returns a peer for -// one of the nodes, together with the channel seen from both nodes. It takes -// an updateChan function which can be used to modify the default values on -// the channel states for each peer. -func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier, - publTx chan *wire.MsgTx, updateChan func(a, b *channeldb.OpenChannel), - mockSwitch *mockMessageSwitch) ( - *Brontide, *lnwallet.LightningChannel, error) { +type peerTestCtx struct { + peer *Brontide + channel *lnwallet.LightningChannel + notifier *mock.ChainNotifier + publishTx <-chan *wire.MsgTx + mockSwitch *mockMessageSwitch + db *channeldb.DB + privKey *btcec.PrivateKey + mockConn *mockMessageConn + customChan chan *customMsg + chanStatusMgr *netann.ChanStatusManager +} - nodeKeyLocator := keychain.KeyLocator{ - Family: keychain.KeyFamilyNodeKey, - } - aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes( - channels.AlicesPrivKey, - ) - aliceKeySigner := keychain.NewPrivKeyMessageSigner( - aliceKeyPriv, nodeKeyLocator, - ) - bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes( - channels.BobsPrivKey, +// createTestPeerWithChannel creates a channel between two nodes, and returns a +// peer for one of the nodes, together with the channel seen from both nodes. +// It takes an updateChan function which can be used to modify the default +// values on the channel states for each peer. +func createTestPeerWithChannel(t *testing.T, updateChan func(a, + b *channeldb.OpenChannel)) (*peerTestCtx, error) { + + params := createTestPeer(t) + + var ( + publishTx = params.publishTx + mockSwitch = params.mockSwitch + alicePeer = params.peer + notifier = params.notifier + aliceKeyPriv = params.privKey + dbAlice = params.db + chanStatusMgr = params.chanStatusMgr ) + err := chanStatusMgr.Start() + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, chanStatusMgr.Stop()) + }) + + aliceKeyPub := alicePeer.IdentityKey() + estimator := alicePeer.cfg.FeeEstimator + channelCapacity := btcutil.Amount(10 * 1e8) channelBal := channelCapacity / 2 aliceDustLimit := btcutil.Amount(200) @@ -88,6 +110,10 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier, } fundingTxIn := wire.NewTxIn(prevOut, nil, nil) + bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes( + channels.BobsPrivKey, + ) + aliceCfg := channeldb.ChannelConfig{ ChannelConstraints: channeldb.ChannelConstraints{ DustLimit: aliceDustLimit, @@ -141,23 +167,23 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier, bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize()) if err != nil { - return nil, nil, err + return nil, err } bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot) bobFirstRevoke, err := bobPreimageProducer.AtIndex(0) if err != nil { - return nil, nil, err + return nil, err } bobCommitPoint := input.ComputeCommitmentPoint(bobFirstRevoke[:]) aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize()) if err != nil { - return nil, nil, err + return nil, err } alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot) aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0) if err != nil { - return nil, nil, err + return nil, err } aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:]) @@ -167,29 +193,20 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier, isAliceInitiator, 0, ) if err != nil { - return nil, nil, err + return nil, err } - dbAlice, err := channeldb.Open(t.TempDir()) - if err != nil { - return nil, nil, err - } - t.Cleanup(func() { - require.NoError(t, dbAlice.Close()) - }) - dbBob, err := channeldb.Open(t.TempDir()) if err != nil { - return nil, nil, err + return nil, err } t.Cleanup(func() { require.NoError(t, dbBob.Close()) }) - estimator := chainfee.NewStaticEstimator(12500, 0) feePerKw, err := estimator.EstimateFeePerKW(1) if err != nil { - return nil, nil, err + return nil, err } // TODO(roasbeef): need to factor in commit fee? @@ -214,7 +231,7 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier, var chanIDBytes [8]byte if _, err := io.ReadFull(crand.Reader, chanIDBytes[:]); err != nil { - return nil, nil, err + return nil, err } shortChanID := lnwire.NewShortChanIDFromInt( @@ -259,13 +276,9 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier, // Set custom values on the channel states. updateChan(aliceChannelState, bobChannelState) - aliceAddr := &net.TCPAddr{ - IP: net.ParseIP("127.0.0.1"), - Port: 18555, - } - + aliceAddr := alicePeer.cfg.Addr.Address if err := aliceChannelState.SyncPending(aliceAddr, 0); err != nil { - return nil, nil, err + return nil, err } bobAddr := &net.TCPAddr{ @@ -274,7 +287,7 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier, } if err := bobChannelState.SyncPending(bobAddr, 0); err != nil { - return nil, nil, err + return nil, err } aliceSigner := input.NewMockSigner( @@ -289,7 +302,7 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier, aliceSigner, aliceChannelState, alicePool, ) if err != nil { - return nil, nil, err + return nil, err } _ = alicePool.Start() t.Cleanup(func() { @@ -301,116 +314,16 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier, bobSigner, bobChannelState, bobPool, ) if err != nil { - return nil, nil, err + return nil, err } _ = bobPool.Start() t.Cleanup(func() { require.NoError(t, bobPool.Stop()) }) - chainIO := &mock.ChainIO{ - BestHeight: broadcastHeight, - } - wallet := &lnwallet.LightningWallet{ - WalletController: &mock.WalletController{ - RootKey: aliceKeyPriv, - PublishedTransactions: publTx, - }, - } - - // If mockSwitch is not set by the caller, set it to the default as the - // caller does not need to control it. - if mockSwitch == nil { - mockSwitch = &mockMessageSwitch{} - } - - nodeSignerAlice := netann.NewNodeSigner(aliceKeySigner) - - const chanActiveTimeout = time.Minute - - chanStatusMgr, err := netann.NewChanStatusManager(&netann.ChanStatusConfig{ - ChanStatusSampleInterval: 30 * time.Second, - ChanEnableTimeout: chanActiveTimeout, - ChanDisableTimeout: 2 * time.Minute, - DB: dbAlice.ChannelStateDB(), - Graph: dbAlice.ChannelGraph(), - MessageSigner: nodeSignerAlice, - OurPubKey: aliceKeyPub, - OurKeyLoc: testKeyLoc, - IsChannelActive: func(lnwire.ChannelID) bool { return true }, - ApplyChannelUpdate: func(*lnwire.ChannelUpdate, - *wire.OutPoint, bool) error { - - return nil - }, - }) - if err != nil { - return nil, nil, err - } - if err = chanStatusMgr.Start(); err != nil { - return nil, nil, err - } - - errBuffer, err := queue.NewCircularBuffer(ErrorBufferSize) - if err != nil { - return nil, nil, err - } - - var pubKey [33]byte - copy(pubKey[:], aliceKeyPub.SerializeCompressed()) - - cfgAddr := &lnwire.NetAddress{ - IdentityKey: aliceKeyPub, - Address: aliceAddr, - ChainNet: wire.SimNet, - } - - interceptableSwitchNotifier := &mock.ChainNotifier{ - EpochChan: make(chan *chainntnfs.BlockEpoch, 1), - } - interceptableSwitchNotifier.EpochChan <- &chainntnfs.BlockEpoch{ - Height: 1, - } - - interceptableSwitch, err := htlcswitch.NewInterceptableSwitch( - &htlcswitch.InterceptableSwitchConfig{ - CltvRejectDelta: testCltvRejectDelta, - CltvInterceptDelta: testCltvRejectDelta + 3, - Notifier: interceptableSwitchNotifier, - }, + alicePeer.remoteFeatures = lnwire.NewFeatureVector( + nil, lnwire.Features, ) - if err != nil { - return nil, nil, err - } - - // TODO(yy): change ChannelNotifier to be an interface. - channelNotifier := channelnotifier.New(dbAlice.ChannelStateDB()) - require.NoError(t, channelNotifier.Start()) - t.Cleanup(func() { - require.NoError(t, channelNotifier.Stop(), - "stop channel notifier failed") - }) - - cfg := &Config{ - Addr: cfgAddr, - PubKeyBytes: pubKey, - ErrorBuffer: errBuffer, - ChainIO: chainIO, - Switch: mockSwitch, - ChanActiveTimeout: chanActiveTimeout, - InterceptSwitch: interceptableSwitch, - ChannelDB: dbAlice.ChannelStateDB(), - FeeEstimator: estimator, - Wallet: wallet, - ChainNotifier: notifier, - ChanStatusMgr: chanStatusMgr, - Features: lnwire.NewFeatureVector(nil, lnwire.Features), - DisconnectPeer: func(b *btcec.PublicKey) error { return nil }, - ChannelNotifier: channelNotifier, - } - - alicePeer := NewBrontide(*cfg) - alicePeer.remoteFeatures = lnwire.NewFeatureVector(nil, lnwire.Features) chanID := lnwire.NewChanIDFromOutPoint(channelAlice.ChannelPoint()) alicePeer.activeChannels.Store(chanID, channelAlice) @@ -418,7 +331,13 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier, alicePeer.wg.Add(1) go alicePeer.channelManager() - return alicePeer, channelBob, nil + return &peerTestCtx{ + peer: alicePeer, + channel: channelBob, + notifier: notifier, + publishTx: publishTx, + mockSwitch: mockSwitch, + }, nil } // mockMessageSwitch is a mock implementation of the messageSwitch interface @@ -618,3 +537,240 @@ func (m *mockMessageConn) LocalAddr() net.Addr { func (m *mockMessageConn) Close() error { return nil } + +// createTestPeer creates a new peer for testing and returns a context struct +// containing necessary handles and mock objects for conducting tests on peer +// functionalities. +func createTestPeer(t *testing.T) *peerTestCtx { + nodeKeyLocator := keychain.KeyLocator{ + Family: keychain.KeyFamilyNodeKey, + } + + aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes( + channels.AlicesPrivKey, + ) + + aliceKeySigner := keychain.NewPrivKeyMessageSigner( + aliceKeyPriv, nodeKeyLocator, + ) + + aliceAddr := &net.TCPAddr{ + IP: net.ParseIP("127.0.0.1"), + Port: 18555, + } + cfgAddr := &lnwire.NetAddress{ + IdentityKey: aliceKeyPub, + Address: aliceAddr, + ChainNet: wire.SimNet, + } + + errBuffer, err := queue.NewCircularBuffer(ErrorBufferSize) + require.NoError(t, err) + + chainIO := &mock.ChainIO{ + BestHeight: broadcastHeight, + } + + publishTx := make(chan *wire.MsgTx) + wallet := &lnwallet.LightningWallet{ + WalletController: &mock.WalletController{ + RootKey: aliceKeyPriv, + PublishedTransactions: publishTx, + }, + } + + const chanActiveTimeout = time.Minute + + dbAlice, err := channeldb.Open(t.TempDir()) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, dbAlice.Close()) + }) + + nodeSignerAlice := netann.NewNodeSigner(aliceKeySigner) + + chanStatusMgr, err := netann.NewChanStatusManager(&netann. + ChanStatusConfig{ + ChanStatusSampleInterval: 30 * time.Second, + ChanEnableTimeout: chanActiveTimeout, + ChanDisableTimeout: 2 * time.Minute, + DB: dbAlice.ChannelStateDB(), + Graph: dbAlice.ChannelGraph(), + MessageSigner: nodeSignerAlice, + OurPubKey: aliceKeyPub, + OurKeyLoc: testKeyLoc, + IsChannelActive: func(lnwire.ChannelID) bool { + return true + }, + ApplyChannelUpdate: func(*lnwire.ChannelUpdate, + *wire.OutPoint, bool) error { + + return nil + }, + }) + require.NoError(t, err) + + interceptableSwitchNotifier := &mock.ChainNotifier{ + EpochChan: make(chan *chainntnfs.BlockEpoch, 1), + } + interceptableSwitchNotifier.EpochChan <- &chainntnfs.BlockEpoch{ + Height: 1, + } + + interceptableSwitch, err := htlcswitch.NewInterceptableSwitch( + &htlcswitch.InterceptableSwitchConfig{ + CltvRejectDelta: testCltvRejectDelta, + CltvInterceptDelta: testCltvRejectDelta + 3, + Notifier: interceptableSwitchNotifier, + }, + ) + require.NoError(t, err) + + // TODO(yy): create interface for lnwallet.LightningChannel so we can + // easily mock it without the following setups. + notifier := &mock.ChainNotifier{ + SpendChan: make(chan *chainntnfs.SpendDetail), + EpochChan: make(chan *chainntnfs.BlockEpoch), + ConfChan: make(chan *chainntnfs.TxConfirmation), + } + + mockSwitch := &mockMessageSwitch{} + + // TODO(yy): change ChannelNotifier to be an interface. + channelNotifier := channelnotifier.New(dbAlice.ChannelStateDB()) + require.NoError(t, channelNotifier.Start()) + t.Cleanup(func() { + require.NoError(t, channelNotifier.Stop(), + "stop channel notifier failed") + }) + + writeBufferPool := pool.NewWriteBuffer( + pool.DefaultWriteBufferGCInterval, + pool.DefaultWriteBufferExpiryInterval, + ) + + writePool := pool.NewWrite( + writeBufferPool, 1, timeout, + ) + require.NoError(t, writePool.Start()) + + readBufferPool := pool.NewReadBuffer( + pool.DefaultReadBufferGCInterval, + pool.DefaultReadBufferExpiryInterval, + ) + + readPool := pool.NewRead( + readBufferPool, 1, timeout, + ) + require.NoError(t, readPool.Start()) + + mockConn := newMockConn(t, 1) + + receivedCustomChan := make(chan *customMsg) + + var pubKey [33]byte + copy(pubKey[:], aliceKeyPub.SerializeCompressed()) + + estimator := chainfee.NewStaticEstimator(12500, 0) + + cfg := &Config{ + Addr: cfgAddr, + PubKeyBytes: pubKey, + ErrorBuffer: errBuffer, + ChainIO: chainIO, + Switch: mockSwitch, + ChanActiveTimeout: chanActiveTimeout, + InterceptSwitch: interceptableSwitch, + ChannelDB: dbAlice.ChannelStateDB(), + FeeEstimator: estimator, + Wallet: wallet, + ChainNotifier: notifier, + ChanStatusMgr: chanStatusMgr, + Features: lnwire.NewFeatureVector( + nil, lnwire.Features, + ), + DisconnectPeer: func(b *btcec.PublicKey) error { + return nil + }, + ChannelNotifier: channelNotifier, + PrunePersistentPeerConnection: func([33]byte) {}, + LegacyFeatures: lnwire.EmptyFeatureVector(), + WritePool: writePool, + ReadPool: readPool, + Conn: mockConn, + HandleCustomMessage: func( + peer [33]byte, msg *lnwire.Custom) error { + + receivedCustomChan <- &customMsg{ + peer: peer, + msg: *msg, + } + + return nil + }, + PongBuf: make([]byte, lnwire.MaxPongBytes), + } + + alicePeer := NewBrontide(*cfg) + + return &peerTestCtx{ + publishTx: publishTx, + mockSwitch: mockSwitch, + peer: alicePeer, + notifier: notifier, + db: dbAlice, + privKey: aliceKeyPriv, + mockConn: mockConn, + customChan: receivedCustomChan, + chanStatusMgr: chanStatusMgr, + } +} + +// startPeer invokes the `Start` method on the specified peer and handles any +// initial startup messages for testing. +func startPeer(t *testing.T, mockConn *mockMessageConn, + peer *Brontide) <-chan struct{} { + + // Start the peer in a goroutine so that we can handle and test for + // startup messages. Successfully sending and receiving init message, + // indicates a successful startup. + done := make(chan struct{}) + go func() { + require.NoError(t, peer.Start()) + close(done) + }() + + // Receive the init message that should be the first message received on + // startup. + rawMsg, err := fn.RecvOrTimeout[[]byte]( + mockConn.writtenMessages, timeout, + ) + require.NoError(t, err) + + msgReader := bytes.NewReader(rawMsg) + nextMsg, err := lnwire.ReadMessage(msgReader, 0) + require.NoError(t, err) + + _, ok := nextMsg.(*lnwire.Init) + require.True(t, ok) + + // Write the reply for the init message to complete the startup. + initReplyMsg := lnwire.NewInitMessage( + lnwire.NewRawFeatureVector( + lnwire.DataLossProtectRequired, + lnwire.GossipQueriesOptional, + ), + lnwire.NewRawFeatureVector(), + ) + + var b bytes.Buffer + _, err = lnwire.WriteMessage(&b, initReplyMsg, 0) + require.NoError(t, err) + + ok = fn.SendOrQuit[[]byte, struct{}]( + mockConn.readMessages, b.Bytes(), make(chan struct{}), + ) + require.True(t, ok) + + return done +} diff --git a/pilot.go b/pilot.go index b0bb6d9f8..380dc5f09 100644 --- a/pilot.go +++ b/pilot.go @@ -17,7 +17,7 @@ import ( "github.com/lightningnetwork/lnd/tor" ) -// validateAtplConfig is a helper method that makes sure the passed +// validateAtplCfg is a helper method that makes sure the passed // configuration is sane. Currently it checks that the heuristic configuration // makes sense. In case the config is valid, it will return a list of // WeightedHeuristics that can be combined for use with the autopilot agent. diff --git a/routing/localchans/manager.go b/routing/localchans/manager.go index 141e4359b..930b4c96b 100644 --- a/routing/localchans/manager.go +++ b/routing/localchans/manager.go @@ -9,6 +9,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/discovery" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwire" @@ -105,6 +106,14 @@ func (r *Manager) UpdatePolicy(newSchema routing.ChannelPolicy, Edge: edge, }) + // Extract inbound fees from the ExtraOpaqueData. + var inboundWireFee lnwire.Fee + _, err = edge.ExtraOpaqueData.ExtractRecords(&inboundWireFee) + if err != nil { + return err + } + inboundFee := models.NewInboundFeeFromWire(inboundWireFee) + // Add updated policy to list of policies to send to switch. policiesToUpdate[info.ChannelPoint] = models.ForwardingPolicy{ BaseFee: edge.FeeBaseMSat, @@ -112,7 +121,7 @@ func (r *Manager) UpdatePolicy(newSchema routing.ChannelPolicy, TimeLockDelta: uint32(edge.TimeLockDelta), MinHTLCOut: edge.MinHTLC, MaxHTLC: edge.MaxHTLC, - InboundFee: newSchema.InboundFee, + InboundFee: inboundFee, } return nil @@ -182,8 +191,15 @@ func (r *Manager) updateEdge(tx kvdb.RTx, chanPoint wire.OutPoint, newSchema.FeeRate, ) - inboundFee := newSchema.InboundFee.ToWire() - if err := edge.ExtraOpaqueData.PackRecords(&inboundFee); err != nil { + // If inbound fees are set, we update the edge with them. + err := fn.MapOptionZ(newSchema.InboundFee, + func(f models.InboundFee) error { + inboundWireFee := f.ToWire() + return edge.ExtraOpaqueData.PackRecords( + &inboundWireFee, + ) + }) + if err != nil { return err } diff --git a/routing/notifications.go b/routing/notifications.go index 9d6c4b51f..3afbb1533 100644 --- a/routing/notifications.go +++ b/routing/notifications.go @@ -301,6 +301,11 @@ type ChannelEdgeUpdate struct { // Disabled, if true, signals that the channel is unavailable to relay // payments. Disabled bool + + // ExtraOpaqueData is the set of data that was appended to this message + // to fill out the full maximum transport message size. These fields can + // be used to specify optional data such as custom TLV fields. + ExtraOpaqueData lnwire.ExtraOpaqueData } // appendTopologyChange appends the passed update message to the passed @@ -379,6 +384,7 @@ func addToTopologyChange(graph *channeldb.ChannelGraph, update *TopologyChange, AdvertisingNode: aNode, ConnectingNode: cNode, Disabled: m.ChannelFlags&lnwire.ChanUpdateDisabled != 0, + ExtraOpaqueData: m.ExtraOpaqueData, } // TODO(roasbeef): add bit to toggle diff --git a/routing/notifications_test.go b/routing/notifications_test.go index 48b0a2766..4e095649b 100644 --- a/routing/notifications_test.go +++ b/routing/notifications_test.go @@ -75,7 +75,18 @@ func createTestNode() (*channeldb.LightningNode, error) { } func randEdgePolicy(chanID *lnwire.ShortChannelID, - node *channeldb.LightningNode) *models.ChannelEdgePolicy { + node *channeldb.LightningNode) (*models.ChannelEdgePolicy, error) { + + InboundFee := models.InboundFee{ + Base: prand.Int31() * -1, + Rate: prand.Int31() * -1, + } + inboundFee := InboundFee.ToWire() + + var extraOpaqueData lnwire.ExtraOpaqueData + if err := extraOpaqueData.PackRecords(&inboundFee); err != nil { + return nil, err + } return &models.ChannelEdgePolicy{ SigBytes: testSig.Serialize(), @@ -87,7 +98,8 @@ func randEdgePolicy(chanID *lnwire.ShortChannelID, FeeBaseMSat: lnwire.MilliSatoshi(prand.Int31()), FeeProportionalMillionths: lnwire.MilliSatoshi(prand.Int31()), ToNode: node.PubKeyBytes, - } + ExtraOpaqueData: extraOpaqueData, + }, nil } func createChannelEdge(ctx *testCtx, bitcoinKey1, bitcoinKey2 []byte, @@ -457,9 +469,12 @@ func TestEdgeUpdateNotification(t *testing.T) { // Create random policy edges that are stemmed to the channel id // created above. - edge1 := randEdgePolicy(chanID, node1) + edge1, err := randEdgePolicy(chanID, node1) + require.NoError(t, err, "unable to create a random chan policy") edge1.ChannelFlags = 0 - edge2 := randEdgePolicy(chanID, node2) + + edge2, err := randEdgePolicy(chanID, node2) + require.NoError(t, err, "unable to create a random chan policy") edge2.ChannelFlags = 1 if err := ctx.router.UpdateEdge(edge1); err != nil { @@ -511,6 +526,9 @@ func TestEdgeUpdateNotification(t *testing.T) { "expected %v, got %v", edgeAnn.TimeLockDelta, edgeUpdate.TimeLockDelta) } + require.Equal( + t, edgeAnn.ExtraOpaqueData, edgeUpdate.ExtraOpaqueData, + ) } // Create lookup map for notifications we are intending to receive. Entries diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index de4935a81..57f723ce6 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -8,9 +8,9 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "math" "net" + "os" "reflect" "strings" "testing" @@ -183,7 +183,7 @@ func makeTestGraph(t *testing.T, useCache bool) (*channeldb.ChannelGraph, func parseTestGraph(t *testing.T, useCache bool, path string) ( *testGraphInstance, error) { - graphJSON, err := ioutil.ReadFile(path) + graphJSON, err := os.ReadFile(path) if err != nil { return nil, err } diff --git a/routing/router.go b/routing/router.go index 4583b9964..c5c635003 100644 --- a/routing/router.go +++ b/routing/router.go @@ -294,7 +294,7 @@ type FeeSchema struct { // InboundFee is the inbound fee schedule that applies to forwards // coming in through a channel to which this FeeSchema pertains. - InboundFee models.InboundFee + InboundFee fn.Option[models.InboundFee] } // ChannelPolicy holds the parameters that determine the policy we enforce diff --git a/routing/unified_edges.go b/routing/unified_edges.go index 04e135a53..8481a2f83 100644 --- a/routing/unified_edges.go +++ b/routing/unified_edges.go @@ -281,14 +281,13 @@ func (u *edgeUnifier) getEdgeLocal(netAmtReceived lnwire.MilliSatoshi, } // We pick the local channel with the highest available - // bandwidth, to maximize the success probability. It - // can be that the channel state changes between - // querying the bandwidth hints and sending out the - // htlc. + // bandwidth, to maximize the success probability. It can be + // that the channel state changes between querying the bandwidth + // hints and sending out the htlc. if bandwidth < maxBandwidth { log.Debugf("Skipped edge %v: not max bandwidth, "+ "bandwidth=%v, maxBandwidth=%v", - bandwidth, maxBandwidth) + edge.policy.ChannelID, bandwidth, maxBandwidth) continue } diff --git a/rpcserver.go b/rpcserver.go index 73e6386f2..664e50270 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -87,6 +87,13 @@ import ( "gopkg.in/macaroon-bakery.v2/bakery" ) +const ( + // defaultNumBlocksEstimate is the number of blocks that we fall back + // to issuing an estimate for if a fee pre fence doesn't specify an + // explicit conf target or fee rate. + defaultNumBlocksEstimate = 6 +) + var ( // readPermissions is a slice of all entities that allow read // permissions for authorization purposes, all lowercase. @@ -1253,15 +1260,46 @@ func (r *rpcServer) EstimateFee(ctx context.Context, return resp, nil } +// maybeUseDefaultConf makes sure that when the user doesn't set either the fee +// rate or conf target, the default conf target is used. +func maybeUseDefaultConf(satPerByte int64, satPerVByte uint64, + targetConf uint32) uint32 { + + // If the fee rate is set, there's no need to use the default conf + // target. In this case, we just return the targetConf from the + // request. + if satPerByte != 0 || satPerVByte != 0 { + return targetConf + } + + // Return the user specified conf target if set. + if targetConf != 0 { + return targetConf + } + + // If the fee rate is not set, yet the conf target is zero, the default + // 6 will be returned. + rpcsLog.Errorf("Expected either 'sat_per_vbyte' or 'conf_target' to " + + "be set, using default conf of 6 instead") + + return defaultNumBlocksEstimate +} + // SendCoins executes a request to send coins to a particular address. Unlike // SendMany, this RPC call only allows creating a single output at a time. func (r *rpcServer) SendCoins(ctx context.Context, in *lnrpc.SendCoinsRequest) (*lnrpc.SendCoinsResponse, error) { + // Keep the old behavior prior to 0.18.0 - when the user doesn't set + // fee rate or conf target, the default conf target of 6 is used. + targetConf := maybeUseDefaultConf( + in.SatPerByte, in.SatPerVbyte, uint32(in.TargetConf), + ) + // Calculate an appropriate fee rate for this transaction. feePerKw, err := lnrpc.CalculateFeeRate( uint64(in.SatPerByte), in.SatPerVbyte, // nolint:staticcheck - uint32(in.TargetConf), r.server.cc.FeeEstimator, + targetConf, r.server.cc.FeeEstimator, ) if err != nil { return nil, err @@ -1479,10 +1517,16 @@ func (r *rpcServer) SendCoins(ctx context.Context, func (r *rpcServer) SendMany(ctx context.Context, in *lnrpc.SendManyRequest) (*lnrpc.SendManyResponse, error) { + // Keep the old behavior prior to 0.18.0 - when the user doesn't set + // fee rate or conf target, the default conf target of 6 is used. + targetConf := maybeUseDefaultConf( + in.SatPerByte, in.SatPerVbyte, uint32(in.TargetConf), + ) + // Calculate an appropriate fee rate for this transaction. feePerKw, err := lnrpc.CalculateFeeRate( uint64(in.SatPerByte), in.SatPerVbyte, // nolint:staticcheck - uint32(in.TargetConf), r.server.cc.FeeEstimator, + targetConf, r.server.cc.FeeEstimator, ) if err != nil { return nil, err @@ -1902,9 +1946,9 @@ func newFundingShimAssembler(chanPointShim *lnrpc.ChanPointShim, initiator bool, ), nil } -// newFundingShimAssembler returns a new fully populated +// newPsbtAssembler returns a new fully populated // chanfunding.PsbtAssembler using a FundingShim obtained from an RPC caller. -func newPsbtAssembler(req *lnrpc.OpenChannelRequest, normalizedMinConfs int32, +func newPsbtAssembler(req *lnrpc.OpenChannelRequest, psbtShim *lnrpc.PsbtShim, netParams *chaincfg.Params) ( chanfunding.Assembler, error) { @@ -1918,11 +1962,6 @@ func newPsbtAssembler(req *lnrpc.OpenChannelRequest, normalizedMinConfs int32, if len(psbtShim.PendingChanId) != 32 { return nil, fmt.Errorf("pending chan ID not set") } - if normalizedMinConfs != 1 { - return nil, fmt.Errorf("setting non-default values for " + - "minimum confirmation is not supported for PSBT " + - "funding") - } if req.SatPerByte != 0 || req.SatPerVbyte != 0 || req.TargetConf != 0 { // nolint:staticcheck return nil, fmt.Errorf("specifying fee estimation parameters " + "is not supported for PSBT funding") @@ -2148,10 +2187,17 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest, // Skip estimating fee rate for PSBT funding. if in.FundingShim == nil || in.FundingShim.GetPsbtShim() == nil { + // Keep the old behavior prior to 0.18.0 - when the user + // doesn't set fee rate or conf target, the default conf target + // of 6 is used. + targetConf := maybeUseDefaultConf( + in.SatPerByte, in.SatPerVbyte, uint32(in.TargetConf), + ) + // Calculate an appropriate fee rate for this transaction. feeRate, err = lnrpc.CalculateFeeRate( uint64(in.SatPerByte), in.SatPerVbyte, - uint32(in.TargetConf), r.server.cc.FeeEstimator, + targetConf, r.server.cc.FeeEstimator, ) if err != nil { return nil, err @@ -2365,8 +2411,12 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest, // chanfunding.PsbtAssembler to construct the funding // transaction. copy(req.PendingChanID[:], psbtShim.PendingChanId) + + // NOTE: For the PSBT case we do also allow unconfirmed + // utxos to fund the psbt transaction because we make + // sure we only use stable utxos. req.ChanFunder, err = newPsbtAssembler( - in, req.MinConfs, psbtShim, + in, psbtShim, &r.server.cc.Wallet.Cfg.NetParams, ) if err != nil { @@ -2695,12 +2745,19 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest, "is offline (try force closing it instead): %v", err) } + // Keep the old behavior prior to 0.18.0 - when the user + // doesn't set fee rate or conf target, the default conf target + // of 6 is used. + targetConf := maybeUseDefaultConf( + in.SatPerByte, in.SatPerVbyte, uint32(in.TargetConf), + ) + // Based on the passed fee related parameters, we'll determine // an appropriate fee rate for the cooperative closure // transaction. feeRate, err := lnrpc.CalculateFeeRate( uint64(in.SatPerByte), in.SatPerVbyte, // nolint:staticcheck - uint32(in.TargetConf), r.server.cc.FeeEstimator, + targetConf, r.server.cc.FeeEstimator, ) if err != nil { return err @@ -6738,6 +6795,14 @@ func marshallTopologyChange(topChange *routing.TopologyChange) *lnrpc.GraphTopol channelUpdates := make([]*lnrpc.ChannelEdgeUpdate, len(topChange.ChannelEdgeUpdates)) for i, channelUpdate := range topChange.ChannelEdgeUpdates { + + customRecords := marshalExtraOpaqueData( + channelUpdate.ExtraOpaqueData, + ) + inboundFee := extractInboundFeeSafe( + channelUpdate.ExtraOpaqueData, + ) + channelUpdates[i] = &lnrpc.ChannelEdgeUpdate{ ChanId: channelUpdate.ChanID, ChanPoint: &lnrpc.ChannelPoint{ @@ -6748,12 +6813,25 @@ func marshallTopologyChange(topChange *routing.TopologyChange) *lnrpc.GraphTopol }, Capacity: int64(channelUpdate.Capacity), RoutingPolicy: &lnrpc.RoutingPolicy{ - TimeLockDelta: uint32(channelUpdate.TimeLockDelta), - MinHtlc: int64(channelUpdate.MinHTLC), - MaxHtlcMsat: uint64(channelUpdate.MaxHTLC), - FeeBaseMsat: int64(channelUpdate.BaseFee), - FeeRateMilliMsat: int64(channelUpdate.FeeRate), - Disabled: channelUpdate.Disabled, + TimeLockDelta: uint32( + channelUpdate.TimeLockDelta, + ), + MinHtlc: int64( + channelUpdate.MinHTLC, + ), + MaxHtlcMsat: uint64( + channelUpdate.MaxHTLC, + ), + FeeBaseMsat: int64( + channelUpdate.BaseFee, + ), + FeeRateMilliMsat: int64( + channelUpdate.FeeRate, + ), + Disabled: channelUpdate.Disabled, + InboundFeeBaseMsat: inboundFee.BaseFee, + InboundFeeRateMilliMsat: inboundFee.FeeRate, + CustomRecords: customRecords, }, AdvertisingNode: encodeKey(channelUpdate.AdvertisingNode), ConnectingNode: encodeKey(channelUpdate.ConnectingNode), @@ -6868,6 +6946,27 @@ func (r *rpcServer) DeleteAllPayments(ctx context.Context, req *lnrpc.DeleteAllPaymentsRequest) ( *lnrpc.DeleteAllPaymentsResponse, error) { + switch { + // Since this is a destructive operation, at least one of the options + // must be set to true. + case !req.AllPayments && !req.FailedPaymentsOnly && + !req.FailedHtlcsOnly: + + return nil, fmt.Errorf("at least one of the options " + + "`all_payments`, `failed_payments_only`, or " + + "`failed_htlcs_only` must be set to true") + + // `all_payments` cannot be true with `failed_payments_only` or + // `failed_htlcs_only`. `all_payments` includes all records, making + // these options contradictory. + case req.AllPayments && + (req.FailedPaymentsOnly || req.FailedHtlcsOnly): + + return nil, fmt.Errorf("`all_payments` cannot be set to true " + + "while either `failed_payments_only` or " + + "`failed_htlcs_only` is also set to true") + } + rpcsLog.Infof("[DeleteAllPayments] failed_payments_only=%v, "+ "failed_htlcs_only=%v", req.FailedPaymentsOnly, req.FailedHtlcsOnly) @@ -7221,27 +7320,35 @@ func (r *rpcServer) UpdateChannelPolicy(ctx context.Context, } // By default, positive inbound fees are rejected. - if !r.cfg.AcceptPositiveInboundFees { - if req.InboundBaseFeeMsat > 0 { + if !r.cfg.AcceptPositiveInboundFees && req.InboundFee != nil { + if req.InboundFee.BaseFeeMsat > 0 { return nil, fmt.Errorf("positive values for inbound "+ "base fee msat are not supported: %v", - req.InboundBaseFeeMsat) + req.InboundFee.BaseFeeMsat) } - if req.InboundFeeRatePpm > 0 { + if req.InboundFee.FeeRatePpm > 0 { return nil, fmt.Errorf("positive values for inbound "+ "fee rate ppm are not supported: %v", - req.InboundFeeRatePpm) + req.InboundFee.FeeRatePpm) } } + // If no inbound fees have been specified, we indicate with an empty + // option that the previous inbound fee should be retained during the + // edge update. + inboundFee := fn.None[models.InboundFee]() + if req.InboundFee != nil { + inboundFee = fn.Some(models.InboundFee{ + Base: req.InboundFee.BaseFeeMsat, + Rate: req.InboundFee.FeeRatePpm, + }) + } + baseFeeMsat := lnwire.MilliSatoshi(req.BaseFeeMsat) feeSchema := routing.FeeSchema{ - BaseFee: baseFeeMsat, - FeeRate: feeRateFixed, - InboundFee: models.InboundFee{ - Base: req.InboundBaseFeeMsat, - Rate: req.InboundFeeRatePpm, - }, + BaseFee: baseFeeMsat, + FeeRate: feeRateFixed, + InboundFee: inboundFee, } maxHtlc := lnwire.MilliSatoshi(req.MaxHtlcMsat) diff --git a/sample-lnd.conf b/sample-lnd.conf index d6bc83d4b..d133adfd3 100644 --- a/sample-lnd.conf +++ b/sample-lnd.conf @@ -305,9 +305,9 @@ ; The default value below is 20 MB (1024 * 1024 * 20) ; blockcachesize=20971520 -; Optional URL for external fee estimation. If no URL is specified, the method -; for fee estimation will depend on the chosen backend and network. Must be set -; for neutrino on mainnet. +; DEPRECATED: Use 'fee.url' option. Optional URL for external fee estimation. +; If no URL is specified, the method for fee estimation will depend on the +; chosen backend and network. Must be set for neutrino on mainnet. ; Default: ; feeurl= ; Example: @@ -530,6 +530,24 @@ ; the headers of an HTTP request. ; http-header-timeout=5s + +[fee] + +; Optional URL for external fee estimation. If no URL is specified, the method +; for fee estimation will depend on the chosen backend and network. Must be set +; for neutrino on mainnet. +; Default: +; fee.url= +; Example: +; fee.url=https://nodes.lightning.computer/fees/v1/btc-fee-estimates.json + +; The minimum interval in which fees will be updated from the specified fee URL. +; fee.min-update-timeout=5m + +; The maximum interval in which fees will be updated from the specified fee URL. +; fee.max-update-timeout=20m + + [prometheus] ; If true, lnd will start the Prometheus exporter. Prometheus flags are @@ -802,10 +820,6 @@ ; NOTE: This value is currently unused. ; neutrino.banthreshold= -; DEPRECATED: Use top level 'feeurl' option. Optional URL for fee estimation. If -; a URL is not specified, static fees will be used for estimation. -; neutrino.feeurl= - ; Optional filter header in height:hash format to assert the state of neutrino's ; filter header chain on startup. If the assertion does not hold, then the ; filter header chain will be re-synced from the genesis block. @@ -1292,6 +1306,39 @@ ; Set to disable blinded route forwarding. ; protocol.no-route-blinding=false +; Set to handle messages of a particular type that falls outside of the +; custom message number range (i.e. 513 is onion messages). Note that you can +; set this option as many times as you want to support more than one custom +; message type. +; Default: +; protocol.custom-message= +; Example: +; protocol.custom-message=513 + +; Specifies feature bits — numbers defined in BOLT 9 — to advertise in the +; node's init message. Note that you can set this option as many times as you +; want to support more than one feature bit. +; Default: +; protocol.custom-init= +; Example: +; protocol.custom-init=39 + +; Specifies custom feature bits — numbers defined in BOLT 9 — to advertise in +; the node's announcement message. Note that you can set this option as many +; times as you want to support more than one feature bit. +; Default: +; protocol.custom-nodeann= +; Example: +; protocol.custom-nodeann=39 + +; Specifies custom feature bits — numbers defined in BOLT 9 — to advertise in +; the node's invoices. Note that you can set this option as many times as you +; want to support more than one feature bit. +; Default: +; protocol.custom-invoice= +; Example: +; protocol.custom-invoice=39 + [db] ; The selected database backend. The current default backend is "bolt". lnd diff --git a/scripts/itest_part.sh b/scripts/itest_part.sh index 312e13a01..bdf4ecdf5 100755 --- a/scripts/itest_part.sh +++ b/scripts/itest_part.sh @@ -16,6 +16,8 @@ shift EXEC="$WORKDIR"/itest.test"$EXEC_SUFFIX" LND_EXEC="$WORKDIR"/lnd-itest"$EXEC_SUFFIX" BTCD_EXEC="$WORKDIR"/btcd-itest"$EXEC_SUFFIX" +export GOCOVERDIR="$WORKDIR/cover" +mkdir -p "$GOCOVERDIR" echo $EXEC -test.v "$@" -logoutput -logdir=.logs-tranche$TRANCHE -lndexec=$LND_EXEC -btcdexec=$BTCD_EXEC -splittranches=$NUM_TRANCHES -runtranche=$TRANCHE # Exit code 255 causes the parallel jobs to abort, so if one part fails the diff --git a/server.go b/server.go index e9ffe60db..c257110ad 100644 --- a/server.go +++ b/server.go @@ -551,8 +551,9 @@ func newServer(cfg *Config, listenAddrs []net.Addr, NoOptionScidAlias: !cfg.ProtocolOptions.ScidAlias(), NoZeroConf: !cfg.ProtocolOptions.ZeroConf(), NoAnySegwit: cfg.ProtocolOptions.NoAnySegwit(), - CustomFeatures: cfg.ProtocolOptions.ExperimentalProtocol.CustomFeatures(), + CustomFeatures: cfg.ProtocolOptions.CustomFeatures(), NoTaprootChans: !cfg.ProtocolOptions.TaprootChans, + NoRouteBlinding: cfg.ProtocolOptions.NoRouteBlinding(), }) if err != nil { return nil, err @@ -1514,6 +1515,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, s.cfg.MaxCommitFeeRateAnchors * 1000).FeePerKWeight(), DeleteAliasEdge: deleteAliasEdge, AliasManager: s.aliasMgr, + IsSweeperOutpoint: s.sweeper.IsSweeperOutpoint, AuxFundingController: implCfg.AuxFundingController, }) if err != nil { @@ -3532,7 +3534,7 @@ func (s *server) nextPeerBackoff(pubStr string, return s.cfg.MinBackoff } -// shouldDropConnection determines if our local connection to a remote peer +// shouldDropLocalConnection determines if our local connection to a remote peer // should be dropped in the case of concurrent connection establishment. In // order to deterministically decide which connection should be dropped, we'll // utilize the ordering of the local and remote public key. If we didn't use diff --git a/sqldb/go.mod b/sqldb/go.mod index 7c9404723..fcf6c3ee9 100644 --- a/sqldb/go.mod +++ b/sqldb/go.mod @@ -11,7 +11,7 @@ require ( github.com/ory/dockertest/v3 v3.10.0 github.com/stretchr/testify v1.9.0 golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 - modernc.org/sqlite v1.29.5 + modernc.org/sqlite v1.29.8 ) require ( @@ -28,7 +28,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.4.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect @@ -38,7 +38,7 @@ require ( github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgproto3/v2 v2.3.3 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/moby/term v0.5.0 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect @@ -55,15 +55,15 @@ require ( go.uber.org/atomic v1.7.0 // indirect golang.org/x/crypto v0.20.0 // indirect golang.org/x/mod v0.16.0 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.19.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect - modernc.org/libc v1.41.0 // indirect + modernc.org/libc v1.49.3 // indirect modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.7.2 // indirect + modernc.org/memory v1.8.0 // indirect modernc.org/strutil v1.2.0 // indirect modernc.org/token v1.1.0 // indirect ) diff --git a/sqldb/go.sum b/sqldb/go.sum index 080895df5..2d92b7667 100644 --- a/sqldb/go.sum +++ b/sqldb/go.sum @@ -51,12 +51,12 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -90,8 +90,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= @@ -183,9 +183,9 @@ golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -213,18 +213,28 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= +modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk= +modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA= +modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= +modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= -modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk= -modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY= +modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg= +modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= -modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= -modernc.org/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE= -modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.29.8 h1:nGKglNx9K5v0As+zF0/Gcl1kMkmaU1XynYyq92PbsC8= +modernc.org/sqlite v1.29.8/go.mod h1:lQPm27iqa4UNZpmr4Aor0MH0HkCLbt1huYDfWylLZFk= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/sweep/README.md b/sweep/README.md index 487ebdcdd..279725474 100644 --- a/sweep/README.md +++ b/sweep/README.md @@ -82,7 +82,8 @@ flowchart LR deadline together. Inputs with the same deadline express the same time sensitivity so it makes sense to sweep them in the same transaction. Once grouped, inputs in each batch are sorted based on their budgets. The only -exception is inputs with `ExclusiveGroup` flag set, which will be swept alone. +exception is inputs with the `ExclusiveGroup` flag set, which will be swept +alone. Once the batching is finished, an `InputSet` is returned, which is an interface used to decide whether a wallet UTXO is needed or not when creating the @@ -91,11 +92,10 @@ the sum of the output values from these inputs against the sum of their budgets - if the total budget cannot be covered, one or more wallet UTXOs are needed. -For instance, when anchor output is swept to perform a CPFP, one or more wallet -UTXOs are likely to be used to meet the specified budget, which is also the -case when sweeping second-level HTLC transactions. However, if the sweeping -transaction also contains other to-be-swept inputs, a wallet UTXO is no longer -needed if their values can cover the total budget. +For instance, commitment and HTLC transactions usually have some proportion of +their outputs timelocked, preventing them from being used to pay fees +immediately. For these transactions, wallet UTXOs are often needed to get them +confirmed in a timely manner. #### `Bumper` @@ -144,7 +144,7 @@ initialized with: - a starting fee rate of 10 sat/vB, which is the result from calling `estimatesmartfee 1000`. - an ending fee rate of 400 sat/vB, which is the result of `200,000/500`. -- a fee rate delta of 390 sat/kvB, which is the result of `(400 - 10) / 500 * +- a fee rate delta of 390 sat/kvB, which is the result of `(400 - 10) / 1000 * 1000`. ## Sweeping Outputs from a Force Close Transaction diff --git a/sweep/aggregator.go b/sweep/aggregator.go index c812cbe2b..6028adca3 100644 --- a/sweep/aggregator.go +++ b/sweep/aggregator.go @@ -5,6 +5,8 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) @@ -201,8 +203,8 @@ func (b *BudgetAggregator) filterInputs(inputs InputsMap) InputsMap { for _, pi := range inputs { op := pi.OutPoint() - // Get the size and skip if there's an error. - size, _, err := pi.WitnessType().SizeUpperBound() + // Get the size of the witness and skip if there's an error. + witnessSize, _, err := pi.WitnessType().SizeUpperBound() if err != nil { log.Warnf("Skipped input=%v: cannot get its size: %v", op, err) @@ -210,12 +212,27 @@ func (b *BudgetAggregator) filterInputs(inputs InputsMap) InputsMap { continue } + //nolint:lll + // Calculate the size if the input is included in the tx. + // + // NOTE: When including this input, we need to account the + // non-witness data which is expressed in vb. + // + // TODO(yy): This is not accurate for tapscript input. We need + // to unify calculations used in the `TxWeightEstimator` inside + // `input/size.go` and `weightEstimator` in + // `weight_estimator.go`. And calculate the expected weights + // similar to BOLT-3: + // https://github.com/lightning/bolts/blob/master/03-transactions.md#appendix-a-expected-weights + wu := lntypes.VByte(input.InputSize).ToWU() + witnessSize + // Skip inputs that has too little budget. - minFee := minFeeRate.FeeForWeight(int64(size)) + minFee := minFeeRate.FeeForWeight(wu) if pi.params.Budget < minFee { log.Warnf("Skipped input=%v: has budget=%v, but the "+ - "min fee requires %v", op, pi.params.Budget, - minFee) + "min fee requires %v (feerate=%v), size=%v", op, + pi.params.Budget, minFee, + minFeeRate.FeePerVByte(), wu.ToVB()) continue } @@ -224,11 +241,12 @@ func (b *BudgetAggregator) filterInputs(inputs InputsMap) InputsMap { startingFeeRate := pi.params.StartingFeeRate.UnwrapOr( chainfee.SatPerKWeight(0), ) - startingFee := startingFeeRate.FeeForWeight(int64(size)) + startingFee := startingFeeRate.FeeForWeight(wu) if pi.params.Budget < startingFee { log.Errorf("Skipped input=%v: has budget=%v, but the "+ - "starting fee requires %v", op, - pi.params.Budget, minFee) + "starting fee requires %v (feerate=%v), "+ + "size=%v", op, pi.params.Budget, startingFee, + startingFeeRate.FeePerVByte(), wu.ToVB()) continue } diff --git a/sweep/aggregator_test.go b/sweep/aggregator_test.go index 289f2780e..a32c849a0 100644 --- a/sweep/aggregator_test.go +++ b/sweep/aggregator_test.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/stretchr/testify/require" ) @@ -34,15 +35,19 @@ func TestBudgetAggregatorFilterInputs(t *testing.T) { // Mock the `SizeUpperBound` method to return an error exactly once. dummyErr := errors.New("dummy error") - wtErr.On("SizeUpperBound").Return(0, false, dummyErr).Once() + wtErr.On("SizeUpperBound").Return( + lntypes.WeightUnit(0), false, dummyErr).Once() // Create a mock WitnessType that gives the size. wt := &input.MockWitnessType{} defer wt.AssertExpectations(t) // Mock the `SizeUpperBound` method to return the size four times. - const wtSize = 100 - wt.On("SizeUpperBound").Return(wtSize, true, nil).Times(4) + const wu lntypes.WeightUnit = 100 + wt.On("SizeUpperBound").Return(wu, true, nil).Times(4) + + // Calculate the input size. + inpSize := lntypes.VByte(input.InputSize).ToWU() + wu // Create a mock input that will be filtered out due to error. inpErr := &input.MockInput{} @@ -62,9 +67,9 @@ func TestBudgetAggregatorFilterInputs(t *testing.T) { var ( // Define three budget values, one below the min fee rate, one // above and one equal to it. - budgetLow = minFeeRate.FeeForWeight(wtSize) - 1 - budgetEqual = minFeeRate.FeeForWeight(wtSize) - budgetHigh = minFeeRate.FeeForWeight(wtSize) + 1 + budgetLow = minFeeRate.FeeForWeight(inpSize) - 1 + budgetEqual = minFeeRate.FeeForWeight(inpSize) + budgetHigh = minFeeRate.FeeForWeight(inpSize) + 1 // Define three outpoints with different budget values. opLow = wire.OutPoint{Hash: chainhash.Hash{2}} @@ -396,8 +401,12 @@ func TestBudgetInputSetClusterInputs(t *testing.T) { // Mock the `SizeUpperBound` method to return the size 10 times since // we are using ten inputs. - const wtSize = 100 - wt.On("SizeUpperBound").Return(wtSize, true, nil).Times(10) + const wu lntypes.WeightUnit = 100 + wt.On("SizeUpperBound").Return(wu, true, nil).Times(10) + + // Calculate the input size. + inpSize := lntypes.VByte(input.InputSize).ToWU() + wu + wt.On("String").Return("mock witness type") // Mock the estimator to return a constant fee rate. @@ -407,8 +416,8 @@ func TestBudgetInputSetClusterInputs(t *testing.T) { var ( // Define two budget values, one below the min fee rate and one // above it. - budgetLow = minFeeRate.FeeForWeight(wtSize) - 1 - budgetHigh = minFeeRate.FeeForWeight(wtSize) + 1 + budgetLow = minFeeRate.FeeForWeight(inpSize) - 1 + budgetHigh = minFeeRate.FeeForWeight(inpSize) + 1 // Create three deadline heights, which means there are three // groups of inputs to be expected. diff --git a/sweep/fee_bumper.go b/sweep/fee_bumper.go index 61e63cc71..b4d429894 100644 --- a/sweep/fee_bumper.go +++ b/sweep/fee_bumper.go @@ -17,6 +17,7 @@ import ( "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/labels" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -155,7 +156,7 @@ func (r *BumpRequest) MaxFeeRateAllowed() (chainfee.SatPerKWeight, error) { // calcSweepTxWeight calculates the weight of the sweep tx. It assumes a // sweeping tx always has a single output(change). func calcSweepTxWeight(inputs []input.Input, - outputPkScript []byte) (uint64, error) { + outputPkScript []byte) (lntypes.WeightUnit, error) { // Use a const fee rate as we only use the weight estimator to // calculate the size. @@ -175,7 +176,7 @@ func calcSweepTxWeight(inputs []input.Input, return 0, err } - return uint64(estimator.weight()), nil + return estimator.weight(), nil } // BumpResult is used by the Bumper to send updates about the tx being @@ -267,7 +268,7 @@ type TxPublisher struct { cfg *TxPublisherConfig // currentHeight is the current block height. - currentHeight int32 + currentHeight atomic.Int32 // records is a map keyed by the requestCounter and the value is the tx // being monitored. @@ -373,7 +374,9 @@ func (t *TxPublisher) initializeFeeFunction( } // Get the initial conf target. - confTarget := calcCurrentConfTarget(t.currentHeight, req.DeadlineHeight) + confTarget := calcCurrentConfTarget( + t.currentHeight.Load(), req.DeadlineHeight, + ) log.Debugf("Initializing fee function with conf target=%v, budget=%v, "+ "maxFeeRateAllowed=%v", confTarget, req.Budget, @@ -542,7 +545,7 @@ func (t *TxPublisher) broadcast(requestID uint64) (*BumpResult, error) { tx := record.tx log.Debugf("Publishing sweep tx %v, num_inputs=%v, height=%v", - txid, len(tx.TxIn), t.currentHeight) + txid, len(tx.TxIn), t.currentHeight.Load()) // Set the event, and change it to TxFailed if the wallet fails to // publish it. @@ -715,7 +718,7 @@ func (t *TxPublisher) monitor(blockEvent *chainntnfs.BlockEpochEvent) { epoch.Height) // Update the best known height for the publisher. - t.currentHeight = epoch.Height + t.currentHeight.Store(epoch.Height) // Check all monitored txns to see if any of them needs // to be bumped. @@ -788,7 +791,7 @@ func (t *TxPublisher) processRecords() { } // Get the current height to be used in the following goroutines. - currentHeight := t.currentHeight + currentHeight := t.currentHeight.Load() // For records that are not confirmed, we perform a fee bump if needed. for requestID, r := range feeBumpRecords { @@ -854,7 +857,7 @@ func (t *TxPublisher) handleFeeBumpTx(requestID uint64, r *monitorRecord, // TODO(yy): send this error back to the sweeper so it can // re-group the inputs? log.Errorf("Failed to increase fee rate for tx %v at "+ - "height=%v: %v", oldTxid, t.currentHeight, err) + "height=%v: %v", oldTxid, t.currentHeight.Load(), err) return } @@ -862,7 +865,7 @@ func (t *TxPublisher) handleFeeBumpTx(requestID uint64, r *monitorRecord, // If the fee rate was not increased, there's no need to bump the fee. if !increased { log.Tracef("Skip bumping tx %v at height=%v", oldTxid, - t.currentHeight) + t.currentHeight.Load()) return } @@ -1122,7 +1125,7 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte, // Validate and calculate the fee and change amount. txFee, changeAmtOpt, locktimeOpt, err := prepareSweepTx( - inputs, changePkScript, feeRate, t.currentHeight, + inputs, changePkScript, feeRate, t.currentHeight.Load(), ) if err != nil { return nil, 0, err @@ -1178,7 +1181,7 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte, // We'll default to using the current block height as locktime, if none // of the inputs commits to a different locktime. - sweepTx.LockTime = uint32(locktimeOpt.UnwrapOr(t.currentHeight)) + sweepTx.LockTime = uint32(locktimeOpt.UnwrapOr(t.currentHeight.Load())) prevInputFetcher, err := input.MultiPrevOutFetcher(inputs) if err != nil { @@ -1214,8 +1217,8 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte, } } - log.Debugf("Created sweep tx %v for %v inputs", sweepTx.TxHash(), - len(inputs)) + log.Debugf("Created sweep tx %v for inputs:\n%v", sweepTx.TxHash(), + inputTypeSummary(inputs)) return sweepTx, txFee, nil } diff --git a/sweep/fee_function.go b/sweep/fee_function.go index 1c783304c..cbf283e37 100644 --- a/sweep/fee_function.go +++ b/sweep/fee_function.go @@ -86,11 +86,14 @@ type LinearFeeFunction struct { currentFeeRate chainfee.SatPerKWeight // width is the number of blocks between the starting block height - // and the deadline block height. + // and the deadline block height minus one. + // + // NOTE: We do minus one from the conf target here because we want to + // max out the budget before the deadline height is reached. width uint32 - // position is the number of blocks between the starting block height - // and the current block height. + // position is the fee function's current position, given a width of w, + // a valid position should lie in range [0, w]. position uint32 // deltaFeeRate is the fee rate (msat/kw) increase per block. @@ -116,10 +119,10 @@ func NewLinearFeeFunction(maxFeeRate chainfee.SatPerKWeight, startingFeeRate fn.Option[chainfee.SatPerKWeight]) ( *LinearFeeFunction, error) { - // If the deadline has already been reached, there's nothing the fee - // function can do. In this case, we'll use the max fee rate - // immediately. - if confTarget == 0 { + // If the deadline is one block away or has already been reached, + // there's nothing the fee function can do. In this case, we'll use the + // max fee rate immediately. + if confTarget <= 1 { return &LinearFeeFunction{ startingFeeRate: maxFeeRate, endingFeeRate: maxFeeRate, @@ -129,7 +132,7 @@ func NewLinearFeeFunction(maxFeeRate chainfee.SatPerKWeight, l := &LinearFeeFunction{ endingFeeRate: maxFeeRate, - width: confTarget, + width: confTarget - 1, estimator: estimator, } @@ -153,18 +156,18 @@ func NewLinearFeeFunction(maxFeeRate chainfee.SatPerKWeight, // The starting and ending fee rates are in sat/kw, so we need to // convert them to msat/kw by multiplying by 1000. - delta := btcutil.Amount(end - start).MulF64(1000 / float64(confTarget)) + delta := btcutil.Amount(end - start).MulF64(1000 / float64(l.width)) l.deltaFeeRate = mSatPerKWeight(delta) // We only allow the delta to be zero if the width is one - when the // delta is zero, it means the starting and ending fee rates are the // same, which means there's nothing to increase, so any width greater // than 1 doesn't provide any utility. This could happen when the - // sweeper is offered to sweep an input that has passed its deadline. + // budget is too small. if l.deltaFeeRate == 0 && l.width != 1 { log.Errorf("Failed to init fee function: startingFeeRate=%v, "+ "endingFeeRate=%v, width=%v, delta=%v", start, end, - confTarget, l.deltaFeeRate) + l.width, l.deltaFeeRate) return nil, fmt.Errorf("fee rate delta is zero") } @@ -175,7 +178,7 @@ func NewLinearFeeFunction(maxFeeRate chainfee.SatPerKWeight, log.Debugf("Linear fee function initialized with startingFeeRate=%v, "+ "endingFeeRate=%v, width=%v, delta=%v", start, end, - confTarget, l.deltaFeeRate) + l.width, l.deltaFeeRate) return l, nil } @@ -211,12 +214,12 @@ func (l *LinearFeeFunction) IncreaseFeeRate(confTarget uint32) (bool, error) { newPosition := uint32(0) // Only calculate the new position when the conf target is less than - // the function's width - the width is the initial conf target, and we - // expect the current conf target to decrease over time. However, we + // the function's width - the width is the initial conf target-1, and + // we expect the current conf target to decrease over time. However, we // still allow the supplied conf target to be greater than the width, // and we won't increase the fee rate in that case. - if confTarget < l.width { - newPosition = l.width - confTarget + if confTarget < l.width+1 { + newPosition = l.width + 1 - confTarget log.Tracef("Increasing position from %v to %v", l.position, newPosition) } @@ -290,7 +293,7 @@ func (l *LinearFeeFunction) estimateFeeRate( // (1008), we will use the min relay fee instead. if confTarget >= chainfee.MaxBlockTarget { minFeeRate := l.estimator.RelayFeePerKW() - log.Debugf("Conf target %v is greater than max block target, "+ + log.Infof("Conf target %v is greater than max block target, "+ "using min relay fee rate %v", confTarget, minFeeRate) return minFeeRate, nil diff --git a/sweep/fee_function_test.go b/sweep/fee_function_test.go index 3b0832946..c278bb7f0 100644 --- a/sweep/fee_function_test.go +++ b/sweep/fee_function_test.go @@ -8,22 +8,20 @@ import ( "github.com/stretchr/testify/require" ) -// TestLinearFeeFunctionNew tests the NewLinearFeeFunction function. -func TestLinearFeeFunctionNew(t *testing.T) { +// TestLinearFeeFunctionNewMaxFeeRateUsed tests when the conf target is <= 1, +// the max fee rate is used. +func TestLinearFeeFunctionNewMaxFeeRateUsed(t *testing.T) { t.Parallel() rt := require.New(t) // Create a mock fee estimator. estimator := &chainfee.MockEstimator{} + defer estimator.AssertExpectations(t) // Create testing params. maxFeeRate := chainfee.SatPerKWeight(10000) - estimatedFeeRate := chainfee.SatPerKWeight(500) - minRelayFeeRate := chainfee.SatPerKWeight(100) - confTarget := uint32(6) noStartFeeRate := fn.None[chainfee.SatPerKWeight]() - startFeeRate := chainfee.SatPerKWeight(1000) // Assert init fee function with zero conf value will end up using the // max fee rate. @@ -36,23 +34,56 @@ func TestLinearFeeFunctionNew(t *testing.T) { rt.Equal(maxFeeRate, f.endingFeeRate) rt.Equal(maxFeeRate, f.currentFeeRate) - // When the fee estimator returns an error, it's returned. - // - // Mock the fee estimator to return an error. - estimator.On("EstimateFeePerKW", confTarget).Return( - chainfee.SatPerKWeight(0), errDummy).Once() + // Assert init fee function with conf of one will end up using the max + // fee rate. + f, err = NewLinearFeeFunction(maxFeeRate, 1, estimator, noStartFeeRate) + rt.NoError(err) + rt.NotNil(f) - f, err = NewLinearFeeFunction( - maxFeeRate, confTarget, estimator, noStartFeeRate, - ) - rt.ErrorIs(err, errDummy) - rt.Nil(f) + // Assert the internal state. + rt.Equal(maxFeeRate, f.startingFeeRate) + rt.Equal(maxFeeRate, f.endingFeeRate) + rt.Equal(maxFeeRate, f.currentFeeRate) +} - // When the starting feerate is greater than the ending feerate, the - // starting feerate is capped. +// TestLinearFeeFunctionNewZeroFeeRateDelta tests when the fee rate delta is +// zero, it will return an error except when the width is one. +func TestLinearFeeFunctionNewZeroFeeRateDelta(t *testing.T) { + t.Parallel() + + rt := require.New(t) + + // Create a mock fee estimator. + estimator := &chainfee.MockEstimator{} + defer estimator.AssertExpectations(t) + + // Create testing params. + maxFeeRate := chainfee.SatPerKWeight(10000) + estimatedFeeRate := chainfee.SatPerKWeight(500) + confTarget := uint32(6) + noStartFeeRate := fn.None[chainfee.SatPerKWeight]() + + // When the calculated fee rate delta is 0, an error should be returned + // when the width is not one. // // Mock the fee estimator to return the fee rate. - smallConf := uint32(1) + estimator.On("EstimateFeePerKW", confTarget).Return( + // The starting fee rate is the max fee rate. + maxFeeRate, nil).Once() + estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once() + + f, err := NewLinearFeeFunction( + maxFeeRate, confTarget, estimator, noStartFeeRate, + ) + rt.ErrorContains(err, "fee rate delta is zero") + rt.Nil(f) + + // When the calculated fee rate delta is 0, an error should NOT be + // returned when the width is one, and the starting feerate is capped + // at the max fee rate. + // + // Mock the fee estimator to return the fee rate. + smallConf := uint32(2) estimator.On("EstimateFeePerKW", smallConf).Return( // The fee rate is greater than the max fee rate. maxFeeRate+1, nil).Once() @@ -64,18 +95,41 @@ func TestLinearFeeFunctionNew(t *testing.T) { rt.NoError(err) rt.NotNil(f) - // When the calculated fee rate delta is 0, an error should be returned. - // - // Mock the fee estimator to return the fee rate. - estimator.On("EstimateFeePerKW", confTarget).Return( - // The starting fee rate is the max fee rate. - maxFeeRate, nil).Once() - estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once() + // Assert the internal state. + rt.Equal(maxFeeRate, f.startingFeeRate) + rt.Equal(maxFeeRate, f.endingFeeRate) + rt.Equal(maxFeeRate, f.currentFeeRate) + rt.Zero(f.deltaFeeRate) + rt.Equal(smallConf-1, f.width) +} - f, err = NewLinearFeeFunction( +// TestLinearFeeFunctionNewEsimator tests the NewLinearFeeFunction function +// properly reacts to the response or error returned from the fee estimator. +func TestLinearFeeFunctionNewEsimator(t *testing.T) { + t.Parallel() + + rt := require.New(t) + + // Create a mock fee estimator. + estimator := &chainfee.MockEstimator{} + defer estimator.AssertExpectations(t) + + // Create testing params. + maxFeeRate := chainfee.SatPerKWeight(10000) + minRelayFeeRate := chainfee.SatPerKWeight(100) + confTarget := uint32(6) + noStartFeeRate := fn.None[chainfee.SatPerKWeight]() + + // When the fee estimator returns an error, it's returned. + // + // Mock the fee estimator to return an error. + estimator.On("EstimateFeePerKW", confTarget).Return( + chainfee.SatPerKWeight(0), errDummy).Once() + + f, err := NewLinearFeeFunction( maxFeeRate, confTarget, estimator, noStartFeeRate, ) - rt.ErrorContains(err, "fee rate delta is zero") + rt.ErrorIs(err, errDummy) rt.Nil(f) // When the conf target is >= 1008, the min relay fee should be used. @@ -95,16 +149,36 @@ func TestLinearFeeFunctionNew(t *testing.T) { rt.Equal(maxFeeRate, f.endingFeeRate) rt.Equal(minRelayFeeRate, f.currentFeeRate) rt.NotZero(f.deltaFeeRate) - rt.Equal(largeConf, f.width) + rt.Equal(largeConf-1, f.width) +} + +// TestLinearFeeFunctionNewSuccess tests we can create the fee function +// successfully. +func TestLinearFeeFunctionNewSuccess(t *testing.T) { + t.Parallel() + + rt := require.New(t) + + // Create a mock fee estimator. + estimator := &chainfee.MockEstimator{} + defer estimator.AssertExpectations(t) + + // Create testing params. + maxFeeRate := chainfee.SatPerKWeight(10000) + estimatedFeeRate := chainfee.SatPerKWeight(500) + minRelayFeeRate := chainfee.SatPerKWeight(100) + confTarget := uint32(6) + noStartFeeRate := fn.None[chainfee.SatPerKWeight]() + startFeeRate := chainfee.SatPerKWeight(1000) // Check a successfully created fee function. // // Mock the fee estimator to return the fee rate. estimator.On("EstimateFeePerKW", confTarget).Return( estimatedFeeRate, nil).Once() - estimator.On("RelayFeePerKW").Return(estimatedFeeRate).Once() + estimator.On("RelayFeePerKW").Return(minRelayFeeRate).Once() - f, err = NewLinearFeeFunction( + f, err := NewLinearFeeFunction( maxFeeRate, confTarget, estimator, noStartFeeRate, ) rt.NoError(err) @@ -115,7 +189,7 @@ func TestLinearFeeFunctionNew(t *testing.T) { rt.Equal(maxFeeRate, f.endingFeeRate) rt.Equal(estimatedFeeRate, f.currentFeeRate) rt.NotZero(f.deltaFeeRate) - rt.Equal(confTarget, f.width) + rt.Equal(confTarget-1, f.width) // Check a successfully created fee function using the specified // starting fee rate. @@ -131,7 +205,10 @@ func TestLinearFeeFunctionNew(t *testing.T) { // Assert the customized starting fee rate is used. rt.Equal(startFeeRate, f.startingFeeRate) + rt.Equal(maxFeeRate, f.endingFeeRate) rt.Equal(startFeeRate, f.currentFeeRate) + rt.NotZero(f.deltaFeeRate) + rt.Equal(confTarget-1, f.width) } // TestLinearFeeFunctionFeeRateAtPosition checks the expected feerate is @@ -201,12 +278,13 @@ func TestLinearFeeFunctionIncrement(t *testing.T) { // Create a mock fee estimator. estimator := &chainfee.MockEstimator{} + defer estimator.AssertExpectations(t) // Create testing params. These params are chosen so the delta value is // 100. - maxFeeRate := chainfee.SatPerKWeight(1000) + maxFeeRate := chainfee.SatPerKWeight(900) estimatedFeeRate := chainfee.SatPerKWeight(100) - confTarget := uint32(9) + confTarget := uint32(9) // This means the width is 8. // Mock the fee estimator to return the fee rate. estimator.On("EstimateFeePerKW", confTarget).Return( @@ -219,8 +297,8 @@ func TestLinearFeeFunctionIncrement(t *testing.T) { ) rt.NoError(err) - // We now increase the position from 1 to 9. - for i := uint32(1); i <= confTarget; i++ { + // We now increase the position from 1 to 8. + for i := uint32(1); i <= confTarget-1; i++ { // Increase the fee rate. increased, err := f.Increment() rt.NoError(err) @@ -236,7 +314,7 @@ func TestLinearFeeFunctionIncrement(t *testing.T) { rt.Equal(estimatedFeeRate+delta, f.FeeRate()) } - // Now the position is at 9th, increase it again should give us an + // Now the position is at 8th, increase it again should give us an // error. increased, err := f.Increment() rt.ErrorIs(err, ErrMaxPosition) @@ -252,12 +330,13 @@ func TestLinearFeeFunctionIncreaseFeeRate(t *testing.T) { // Create a mock fee estimator. estimator := &chainfee.MockEstimator{} + defer estimator.AssertExpectations(t) // Create testing params. These params are chosen so the delta value is // 100. - maxFeeRate := chainfee.SatPerKWeight(1000) + maxFeeRate := chainfee.SatPerKWeight(900) estimatedFeeRate := chainfee.SatPerKWeight(100) - confTarget := uint32(9) + confTarget := uint32(9) // This means the width is 8. // Mock the fee estimator to return the fee rate. estimator.On("EstimateFeePerKW", confTarget).Return( @@ -281,9 +360,9 @@ func TestLinearFeeFunctionIncreaseFeeRate(t *testing.T) { rt.NoError(err) rt.False(increased) - // We now increase the fee rate from conf target 8 to 1 and assert we + // We now increase the fee rate from conf target 8 to 2 and assert we // get no error and true. - for i := uint32(1); i < confTarget; i++ { + for i := uint32(1); i < confTarget-1; i++ { // Increase the fee rate. increased, err := f.IncreaseFeeRate(confTarget - i) rt.NoError(err) @@ -299,11 +378,17 @@ func TestLinearFeeFunctionIncreaseFeeRate(t *testing.T) { rt.Equal(estimatedFeeRate+delta, f.FeeRate()) } - // Test that when we use a conf target of 0, we get the ending fee + // Test that when we use a conf target of 1, we get the ending fee // rate. - increased, err = f.IncreaseFeeRate(0) + increased, err = f.IncreaseFeeRate(1) rt.NoError(err) rt.True(increased) - rt.Equal(confTarget, f.position) + rt.Equal(confTarget-1, f.position) rt.Equal(maxFeeRate, f.currentFeeRate) + + // Test that when we use a conf target of 0, ErrMaxPosition is + // returned. + increased, err = f.IncreaseFeeRate(0) + rt.ErrorIs(err, ErrMaxPosition) + rt.False(increased) } diff --git a/sweep/sweeper.go b/sweep/sweeper.go index 2754485ea..39a03228d 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -767,11 +767,11 @@ func (s *UtxoSweeper) signalResult(pi *SweeperInput, result Result) { listeners := pi.listeners if result.Err == nil { - log.Debugf("Dispatching sweep success for %v to %v listeners", + log.Tracef("Dispatching sweep success for %v to %v listeners", op, len(listeners), ) } else { - log.Debugf("Dispatching sweep error for %v to %v listeners: %v", + log.Tracef("Dispatching sweep error for %v to %v listeners: %v", op, len(listeners), result.Err, ) } @@ -830,6 +830,9 @@ func (s *UtxoSweeper) sweep(set InputSet) error { outpoints[i] = inp.OutPoint() } + log.Errorf("Initial broadcast failed: %v, inputs=\n%v", err, + inputTypeSummary(set.Inputs())) + // TODO(yy): find out which input is causing the failure. s.markInputsPublishFailed(outpoints) @@ -855,7 +858,7 @@ func (s *UtxoSweeper) markInputsPendingPublish(set InputSet) { // It could be that this input is an additional wallet // input that was attached. In that case there also // isn't a pending input to update. - log.Debugf("Skipped marking input as pending "+ + log.Tracef("Skipped marking input as pending "+ "published: %v not found in pending inputs", input.OutPoint()) @@ -904,7 +907,7 @@ func (s *UtxoSweeper) markInputsPublished(tr *TxRecord, // It could be that this input is an additional wallet // input that was attached. In that case there also // isn't a pending input to update. - log.Debugf("Skipped marking input as published: %v "+ + log.Tracef("Skipped marking input as published: %v "+ "not found in pending inputs", input.PreviousOutPoint) @@ -940,7 +943,7 @@ func (s *UtxoSweeper) markInputsPublishFailed(outpoints []wire.OutPoint) { // It could be that this input is an additional wallet // input that was attached. In that case there also // isn't a pending input to update. - log.Debugf("Skipped marking input as publish failed: "+ + log.Tracef("Skipped marking input as publish failed: "+ "%v not found in pending inputs", op) continue @@ -948,7 +951,7 @@ func (s *UtxoSweeper) markInputsPublishFailed(outpoints []wire.OutPoint) { // Valdiate that the input is in an expected state. if pi.state != PendingPublish && pi.state != Published { - log.Errorf("Expect input %v to have %v, instead it "+ + log.Debugf("Expect input %v to have %v, instead it "+ "has %v", op, PendingPublish, pi.state) continue @@ -1397,7 +1400,7 @@ func (s *UtxoSweeper) markInputsSwept(tx *wire.MsgTx, isOurTx bool) { if !ok { // It's very likely that a spending tx contains inputs // that we don't know. - log.Debugf("Skipped marking input as swept: %v not "+ + log.Tracef("Skipped marking input as swept: %v not "+ "found in pending inputs", outpoint) continue @@ -1735,3 +1738,26 @@ func (s *UtxoSweeper) handleBumpEvent(r *BumpResult) error { return nil } + +// IsSweeperOutpoint determines whether the outpoint was created by the sweeper. +// +// NOTE: It is enough to check the txid because the sweeper will create +// outpoints which solely belong to the internal LND wallet. +func (s *UtxoSweeper) IsSweeperOutpoint(op wire.OutPoint) bool { + found, err := s.cfg.Store.IsOurTx(op.Hash) + // In case there is an error fetching the transaction details from the + // sweeper store we assume the outpoint is still used by the sweeper + // (worst case scenario). + // + // TODO(ziggie): Ensure that confirmed outpoints are deleted from the + // bucket. + if err != nil && !errors.Is(err, errNoTxHashesBucket) { + log.Errorf("failed to fetch info for outpoint(%v:%d) "+ + "with: %v, we assume it is still in use by the sweeper", + op.Hash, op.Index, err) + + return true + } + + return found +} diff --git a/sweep/sweeper_test.go b/sweep/sweeper_test.go index e25864bcb..c8d9fc510 100644 --- a/sweep/sweeper_test.go +++ b/sweep/sweeper_test.go @@ -206,7 +206,7 @@ func TestMarkInputsPublishFailed(t *testing.T) { Store: mockStore, }) - // Create three testing inputs. + // Create testing inputs for each state. // // inputNotExist specifies an input that's not found in the sweeper's // `inputs` map. @@ -240,18 +240,52 @@ func TestMarkInputsPublishFailed(t *testing.T) { state: Published, } + // inputPublishFailed specifies an input that's failed to be published. + inputPublishFailed := &wire.TxIn{ + PreviousOutPoint: wire.OutPoint{Index: 5}, + } + s.inputs[inputPublishFailed.PreviousOutPoint] = &SweeperInput{ + state: PublishFailed, + } + + // inputSwept specifies an input that's swept. + inputSwept := &wire.TxIn{ + PreviousOutPoint: wire.OutPoint{Index: 6}, + } + s.inputs[inputSwept.PreviousOutPoint] = &SweeperInput{ + state: Swept, + } + + // inputExcluded specifies an input that's excluded. + inputExcluded := &wire.TxIn{ + PreviousOutPoint: wire.OutPoint{Index: 7}, + } + s.inputs[inputExcluded.PreviousOutPoint] = &SweeperInput{ + state: Excluded, + } + + // inputFailed specifies an input that's failed. + inputFailed := &wire.TxIn{ + PreviousOutPoint: wire.OutPoint{Index: 8}, + } + s.inputs[inputFailed.PreviousOutPoint] = &SweeperInput{ + state: Failed, + } + + // Gather all inputs' outpoints. + pendingOps := make([]wire.OutPoint, 0, len(s.inputs)+1) + for op := range s.inputs { + pendingOps = append(pendingOps, op) + } + pendingOps = append(pendingOps, inputNotExist.PreviousOutPoint) + // Mark the test inputs. We expect the non-exist input and the // inputInit to be skipped, and the final input to be marked as // published. - s.markInputsPublishFailed([]wire.OutPoint{ - inputNotExist.PreviousOutPoint, - inputInit.PreviousOutPoint, - inputPendingPublish.PreviousOutPoint, - inputPublished.PreviousOutPoint, - }) + s.markInputsPublishFailed(pendingOps) // We expect unchanged number of pending inputs. - require.Len(s.inputs, 3) + require.Len(s.inputs, 7) // We expect the init input's state to stay unchanged. require.Equal(Init, @@ -266,6 +300,19 @@ func TestMarkInputsPublishFailed(t *testing.T) { require.Equal(PublishFailed, s.inputs[inputPublished.PreviousOutPoint].state) + // We expect the publish failed input to stay unchanged. + require.Equal(PublishFailed, + s.inputs[inputPublishFailed.PreviousOutPoint].state) + + // We expect the swept input to stay unchanged. + require.Equal(Swept, s.inputs[inputSwept.PreviousOutPoint].state) + + // We expect the excluded input to stay unchanged. + require.Equal(Excluded, s.inputs[inputExcluded.PreviousOutPoint].state) + + // We expect the failed input to stay unchanged. + require.Equal(Failed, s.inputs[inputFailed.PreviousOutPoint].state) + // Assert mocked statements are executed as expected. mockStore.AssertExpectations(t) } @@ -648,12 +695,12 @@ func TestSweepPendingInputs(t *testing.T) { // Mock the methods used in `sweep`. This is not important for this // unit test. - setNeedWallet.On("Inputs").Return(nil).Times(4) + setNeedWallet.On("Inputs").Return(nil).Maybe() setNeedWallet.On("DeadlineHeight").Return(testHeight).Once() setNeedWallet.On("Budget").Return(btcutil.Amount(1)).Once() setNeedWallet.On("StartingFeeRate").Return( fn.None[chainfee.SatPerKWeight]()).Once() - normalSet.On("Inputs").Return(nil).Times(4) + normalSet.On("Inputs").Return(nil).Maybe() normalSet.On("DeadlineHeight").Return(testHeight).Once() normalSet.On("Budget").Return(btcutil.Amount(1)).Once() normalSet.On("StartingFeeRate").Return( diff --git a/sweep/tx_input_set.go b/sweep/tx_input_set.go index cfa4a0ba4..31f20b7db 100644 --- a/sweep/tx_input_set.go +++ b/sweep/tx_input_set.go @@ -241,13 +241,13 @@ func (b *BudgetInputSet) NeedWalletInput() bool { // If the input's budget is not even covered by itself, we need // to borrow outputs from other inputs. if budgetBorrowable < 0 { - log.Debugf("Input %v specified a budget that exceeds "+ + log.Tracef("Input %v specified a budget that exceeds "+ "its output value: %v > %v", inp, budget, output) } } - log.Tracef("NeedWalletInput: budgetNeeded=%v, budgetBorrowable=%v", + log.Debugf("NeedWalletInput: budgetNeeded=%v, budgetBorrowable=%v", budgetNeeded, budgetBorrowable) // If we don't have enough extra budget to borrow, we need wallet @@ -314,6 +314,9 @@ func (b *BudgetInputSet) AddWalletInputs(wallet Wallet) error { } b.addInput(pi) + log.Debugf("Added wallet input to input set: op=%v, amt=%v", + pi.OutPoint(), utxo.Value) + // Return if we've reached the minimum output amount. if !b.NeedWalletInput() { return nil diff --git a/sweep/walletsweep.go b/sweep/walletsweep.go index 5328ae508..8eb3764a1 100644 --- a/sweep/walletsweep.go +++ b/sweep/walletsweep.go @@ -16,13 +16,6 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chanfunding" ) -const ( - // defaultNumBlocksEstimate is the number of blocks that we fall back - // to issuing an estimate for if a fee pre fence doesn't specify an - // explicit conf target or fee rate. - defaultNumBlocksEstimate = 6 -) - var ( // ErrNoFeePreference is returned when we attempt to satisfy a sweep // request from a client whom did not specify a fee preference. diff --git a/sweep/weight_estimator.go b/sweep/weight_estimator.go index f705c826e..429b36ee5 100644 --- a/sweep/weight_estimator.go +++ b/sweep/weight_estimator.go @@ -5,6 +5,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) @@ -15,7 +16,7 @@ type weightEstimator struct { feeRate chainfee.SatPerKWeight parents map[chainhash.Hash]struct{} parentsFee btcutil.Amount - parentsWeight int64 + parentsWeight lntypes.WeightUnit // maxFeeRate is the max allowed fee rate configured by the user. maxFeeRate chainfee.SatPerKWeight @@ -102,7 +103,7 @@ func (w *weightEstimator) addOutput(txOut *wire.TxOut) { } // weight gets the estimated weight of the transaction. -func (w *weightEstimator) weight() int { +func (w *weightEstimator) weight() lntypes.WeightUnit { return w.estimator.Weight() } @@ -111,7 +112,7 @@ func (w *weightEstimator) weight() int { // parent transactions. func (w *weightEstimator) fee() btcutil.Amount { // Calculate the weight of the transaction. - weight := int64(w.estimator.Weight()) + weight := w.estimator.Weight() // Calculate the fee. fee := w.feeRate.FeeForWeight(weight) @@ -123,7 +124,7 @@ func (w *weightEstimator) fee() btcutil.Amount { // outputs, taking into account unconfirmed parent transactions (cpfp). func (w *weightEstimator) feeWithParent() btcutil.Amount { // Calculate fee and weight for just this tx. - childWeight := int64(w.estimator.Weight()) + childWeight := w.estimator.Weight() // Add combined weight of unconfirmed parent txes. totalWeight := childWeight + w.parentsWeight diff --git a/sweep/weight_estimator_test.go b/sweep/weight_estimator_test.go index d5beb5174..513aa01cc 100644 --- a/sweep/weight_estimator_test.go +++ b/sweep/weight_estimator_test.go @@ -8,6 +8,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/stretchr/testify/require" ) @@ -28,7 +29,7 @@ func TestWeightEstimator(t *testing.T) { require.NoError(t, w.add(&input1)) // The expectations is that this input is added. - const expectedWeight1 = 322 + const expectedWeight1 lntypes.WeightUnit = 322 require.Equal(t, expectedWeight1, w.weight()) require.Equal(t, testFeeRate.FeeForWeight(expectedWeight1), w.feeWithParent()) @@ -50,7 +51,7 @@ func TestWeightEstimator(t *testing.T) { // Pay for parent isn't possible because the parent pays a higher fee // rate than the child. We expect no additional fee on the child. - const expectedWeight2 = expectedWeight1 + 280 + const expectedWeight2 lntypes.WeightUnit = expectedWeight1 + 280 require.Equal(t, expectedWeight2, w.weight()) require.Equal(t, testFeeRate.FeeForWeight(expectedWeight2), w.feeWithParent()) @@ -70,7 +71,7 @@ func TestWeightEstimator(t *testing.T) { require.NoError(t, w.add(&input3)) // Expect the weight to increase because of the third input. - const expectedWeight3 = expectedWeight2 + 280 + const expectedWeight3 lntypes.WeightUnit = expectedWeight2 + 280 require.Equal(t, expectedWeight3, w.weight()) // Expect the fee to cover the child and the parent transaction at 20 @@ -107,7 +108,7 @@ func TestWeightEstimatorMaxFee(t *testing.T) { require.NoError(t, w.add(&childInput)) // The child weight should be 322 weight uints. - const childWeight = 322 + const childWeight lntypes.WeightUnit = 322 require.Equal(t, childWeight, w.weight()) // Check the fee is capped at the maximum allowed fee. The diff --git a/tls_manager.go b/tls_manager.go index 7801279fa..076cf44bc 100644 --- a/tls_manager.go +++ b/tls_manager.go @@ -7,7 +7,6 @@ import ( "crypto/x509" "errors" "fmt" - "io/ioutil" "net" "net/http" "os" @@ -279,7 +278,7 @@ func (t *TLSManager) ensureEncryption(keyRing keychain.SecretKeyRing) error { if err != nil { return err } - err = ioutil.WriteFile( + err = os.WriteFile( t.cfg.TLSKeyPath, b.Bytes(), modifyFilePermissions, ) if err != nil { diff --git a/tls_manager_test.go b/tls_manager_test.go index 935834971..42f010411 100644 --- a/tls_manager_test.go +++ b/tls_manager_test.go @@ -9,9 +9,9 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" - "io/ioutil" "math/big" "net" + "os" "testing" "time" @@ -199,7 +199,7 @@ func TestGenerateEphemeralCert(t *testing.T) { keyBytes, err := tlsManager.loadEphemeralCertificate() require.NoError(t, err, "failed to generate new certificate") - certBytes, err := ioutil.ReadFile(tmpCertPath) + certBytes, err := os.ReadFile(tmpCertPath) require.NoError(t, err) tlsr, err := cert.NewTLSReloader(certBytes, keyBytes) @@ -207,15 +207,15 @@ func TestGenerateEphemeralCert(t *testing.T) { tlsManager.tlsReloader = tlsr // Make sure .tmp file is created at the tmp cert path. - _, err = ioutil.ReadFile(tmpCertPath) + _, err = os.ReadFile(tmpCertPath) require.NoError(t, err, "couldn't find temp cert file") // But no key should be stored. - _, err = ioutil.ReadFile(cfg.TLSKeyPath) + _, err = os.ReadFile(cfg.TLSKeyPath) require.Error(t, err, "shouldn't have found file") // And no permanent cert file should be stored. - _, err = ioutil.ReadFile(cfg.TLSCertPath) + _, err = os.ReadFile(cfg.TLSCertPath) require.Error(t, err, "shouldn't have found a permanent cert file") // Now test that when we reload the certificate it generates the new @@ -350,9 +350,9 @@ func writeTestCertFiles(t *testing.T, expiredCert, encryptTLSKey bool, require.NoError(t, err, "failed to encrypt private key") } - err = ioutil.WriteFile(tempDir+"/tls.cert", certBuf.Bytes(), 0644) + err = os.WriteFile(tempDir+"/tls.cert", certBuf.Bytes(), 0644) require.NoError(t, err, "failed to write cert file") - err = ioutil.WriteFile(tempDir+"/tls.key", keyBuf.Bytes(), 0600) + err = os.WriteFile(tempDir+"/tls.key", keyBuf.Bytes(), 0600) require.NoError(t, err, "failed to write key file") return certPath, keyPath, parsedCert diff --git a/tlv/go.mod b/tlv/go.mod index f61284752..bfc3922c6 100644 --- a/tlv/go.mod +++ b/tlv/go.mod @@ -1,23 +1,23 @@ module github.com/lightningnetwork/lnd/tlv require ( - github.com/btcsuite/btcd v0.23.3 - github.com/btcsuite/btcd/btcec/v2 v2.1.3 + github.com/btcsuite/btcd v0.24.1-0.20240301210420-1a2b599bf1af + github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 github.com/lightningnetwork/lnd/fn v1.0.4 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.4 golang.org/x/exp v0.0.0-20231226003508-02704c960a9b ) require ( - github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/lightninglabs/neutrino/cache v1.1.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/sys v0.13.0 // indirect + golang.org/x/crypto v0.16.0 // indirect + golang.org/x/sys v0.15.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/tlv/go.sum b/tlv/go.sum index 25b1c26c2..fb4166322 100644 --- a/tlv/go.sum +++ b/tlv/go.sum @@ -1,11 +1,10 @@ -github.com/btcsuite/btcd v0.23.3 h1:4KH/JKy9WiCd+iUS9Mu0Zp7Dnj17TGdKrg9xc/FGj24= -github.com/btcsuite/btcd v0.23.3/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= -github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE= -github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd v0.24.1-0.20240301210420-1a2b599bf1af h1:F60A3wst4/fy9Yr1Vn8MYmFlfn7DNLxp8o8UTvhqgBE= +github.com/btcsuite/btcd v0.24.1-0.20240301210420-1a2b599bf1af/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= @@ -28,24 +27,18 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tlv/internal/gen/gen_tlv_types.go b/tlv/internal/gen/gen_tlv_types.go index da2d4572e..331d10b64 100644 --- a/tlv/internal/gen/gen_tlv_types.go +++ b/tlv/internal/gen/gen_tlv_types.go @@ -8,8 +8,12 @@ import ( ) const ( - numberOfTypes = 100 - defaultOutputFile = "tlv_types_generated.go" + numberOfTypes uint32 = 100 + + // customTypeStart defines the beginning of the custom TLV type range as + // defined in BOLT-01. + customTypeStart uint32 = 65536 + defaultOutputFile = "tlv_types_generated.go" ) const typeCodeTemplate = `// Code generated by tlv/internal/gen; DO NOT EDIT. @@ -32,9 +36,18 @@ type TlvType{{ $index }} = *tlvType{{ $index }} func main() { // Create a slice of items that the template can range over. - var items []struct{} - for i := uint16(0); i <= numberOfTypes; i++ { - items = append(items, struct{}{}) + // + // We'll generate 100 elements from the lower end of the TLV range + // first. + items := make(map[uint32]struct{}) + for i := uint32(0); i <= numberOfTypes; i++ { + items[i] = struct{}{} + } + + // With the lower end generated, we'll now generate 100 records in the + // upper end of the range. + for i := customTypeStart; i <= customTypeStart+numberOfTypes; i++ { + items[i] = struct{}{} } tpl, err := template.New("tlv").Parse(typeCodeTemplate) diff --git a/tlv/record.go b/tlv/record.go index a872e6004..7813ee771 100644 --- a/tlv/record.go +++ b/tlv/record.go @@ -17,6 +17,10 @@ type Type uint64 // slice containing the encoded data. type TypeMap map[Type][]byte +// Blob is a type alias for a byte slice. It's used to indicate that a slice of +// bytes is actually an encoded TLV stream. +type Blob = []byte + // Encoder is a signature for methods that can encode TLV values. An error // should be returned if the Encoder cannot support the underlying type of val. // The provided scratch buffer must be non-nil. diff --git a/tlv/record_type.go b/tlv/record_type.go index 297617d5a..9bfcd1aaa 100644 --- a/tlv/record_type.go +++ b/tlv/record_type.go @@ -104,15 +104,15 @@ type OptionalRecordT[T TlvType, V any] struct { // TlvType returns the type of the record. This is the value used to identify // this type on the wire. This value is bound to the specified TlvType type // param. -func (t *OptionalRecordT[T, V]) TlvType() Type { +func (o *OptionalRecordT[T, V]) TlvType() Type { zeroRecord := ZeroRecordT[T, V]() return zeroRecord.TlvType() } // WhenSomeV executes the given function if the optional record is present. // This operates on the inner most type, V, which is the value of the record. -func (t *OptionalRecordT[T, V]) WhenSomeV(f func(V)) { - t.Option.WhenSome(func(r RecordT[T, V]) { +func (o *OptionalRecordT[T, V]) WhenSomeV(f func(V)) { + o.Option.WhenSome(func(r RecordT[T, V]) { f(r.Val) }) } @@ -126,7 +126,7 @@ func (o *OptionalRecordT[T, V]) UnwrapOrFailV(t *testing.T) V { return inner.Val } -// UnwrapOrErr is used to extract a value from an option, if the option is +// UnwrapOrErrV is used to extract a value from an option, if the option is // empty, then the specified error is returned directly. This gives the // underlying value of the record, instead of the record itself. func (o *OptionalRecordT[T, V]) UnwrapOrErrV(err error) (V, error) { @@ -141,10 +141,19 @@ func (o *OptionalRecordT[T, V]) UnwrapOrErrV(err error) (V, error) { } // Zero returns a zero value of the record type. -func (t *OptionalRecordT[T, V]) Zero() RecordT[T, V] { +func (o *OptionalRecordT[T, V]) Zero() RecordT[T, V] { return ZeroRecordT[T, V]() } +// ValOpt returns an Option of the underlying value. This can be used to chain +// other option related methods to avoid needing to first go through the outer +// record. +func (o *OptionalRecordT[T, V]) ValOpt() fn.Option[V] { + return fn.MapOption(func(record RecordT[T, V]) V { + return record.Val + })(o.Option) +} + // SomeRecordT creates a new OptionalRecordT type from a given RecordT type. func SomeRecordT[T TlvType, V any](record RecordT[T, V]) OptionalRecordT[T, V] { return OptionalRecordT[T, V]{ @@ -159,3 +168,30 @@ func ZeroRecordT[T TlvType, V any]() RecordT[T, V] { Val: v, } } + +// BigSizeT is a high-order type that represents a TLV record that encodes an +// integer as a BigSize value in the stream. +type BigSizeT[T constraints.Integer] struct { + // We'll store the base value in the struct as a uin64, but then expose + // a public method to cast to the specified type. + v uint64 +} + +// NewBigSizeT creates a new BigSizeT type from a given integer type. +func NewBigSizeT[T constraints.Integer](val T) BigSizeT[T] { + return BigSizeT[T]{ + v: uint64(val), + } +} + +// Int returns the underlying integer value of the BigSize record. +func (b BigSizeT[T]) Int() T { + return T(b.v) +} + +// Record returns the underlying record interface for the record type. +func (b *BigSizeT[T]) Record() Record { + // We use a zero value for the type here as this should be used with + // the higher order RecordT type. + return MakeBigSizeRecord(0, &b.v) +} diff --git a/tlv/stream.go b/tlv/stream.go index 3d353ad22..70fade22d 100644 --- a/tlv/stream.go +++ b/tlv/stream.go @@ -4,7 +4,6 @@ import ( "bytes" "errors" "io" - "io/ioutil" "math" ) @@ -262,7 +261,7 @@ func (s *Stream) decode(r io.Reader, parsedTypes TypeMap, p2p bool) (TypeMap, // If the caller provided an initialized TypeMap, record // the encoded bytes. var b *bytes.Buffer - writer := ioutil.Discard + writer := io.Discard if parsedTypes != nil { b = bytes.NewBuffer(make([]byte, 0, length)) writer = b diff --git a/tlv/tlv_types_generated.go b/tlv/tlv_types_generated.go index f2e0420fd..c75a86232 100644 --- a/tlv/tlv_types_generated.go +++ b/tlv/tlv_types_generated.go @@ -1011,3 +1011,1013 @@ func (t *tlvType100) TypeVal() Type { func (t *tlvType100) tlv() {} type TlvType100 = *tlvType100 + +type tlvType65536 struct{} + +func (t *tlvType65536) TypeVal() Type { + return 65536 +} + +func (t *tlvType65536) tlv() {} + +type TlvType65536 = *tlvType65536 + +type tlvType65537 struct{} + +func (t *tlvType65537) TypeVal() Type { + return 65537 +} + +func (t *tlvType65537) tlv() {} + +type TlvType65537 = *tlvType65537 + +type tlvType65538 struct{} + +func (t *tlvType65538) TypeVal() Type { + return 65538 +} + +func (t *tlvType65538) tlv() {} + +type TlvType65538 = *tlvType65538 + +type tlvType65539 struct{} + +func (t *tlvType65539) TypeVal() Type { + return 65539 +} + +func (t *tlvType65539) tlv() {} + +type TlvType65539 = *tlvType65539 + +type tlvType65540 struct{} + +func (t *tlvType65540) TypeVal() Type { + return 65540 +} + +func (t *tlvType65540) tlv() {} + +type TlvType65540 = *tlvType65540 + +type tlvType65541 struct{} + +func (t *tlvType65541) TypeVal() Type { + return 65541 +} + +func (t *tlvType65541) tlv() {} + +type TlvType65541 = *tlvType65541 + +type tlvType65542 struct{} + +func (t *tlvType65542) TypeVal() Type { + return 65542 +} + +func (t *tlvType65542) tlv() {} + +type TlvType65542 = *tlvType65542 + +type tlvType65543 struct{} + +func (t *tlvType65543) TypeVal() Type { + return 65543 +} + +func (t *tlvType65543) tlv() {} + +type TlvType65543 = *tlvType65543 + +type tlvType65544 struct{} + +func (t *tlvType65544) TypeVal() Type { + return 65544 +} + +func (t *tlvType65544) tlv() {} + +type TlvType65544 = *tlvType65544 + +type tlvType65545 struct{} + +func (t *tlvType65545) TypeVal() Type { + return 65545 +} + +func (t *tlvType65545) tlv() {} + +type TlvType65545 = *tlvType65545 + +type tlvType65546 struct{} + +func (t *tlvType65546) TypeVal() Type { + return 65546 +} + +func (t *tlvType65546) tlv() {} + +type TlvType65546 = *tlvType65546 + +type tlvType65547 struct{} + +func (t *tlvType65547) TypeVal() Type { + return 65547 +} + +func (t *tlvType65547) tlv() {} + +type TlvType65547 = *tlvType65547 + +type tlvType65548 struct{} + +func (t *tlvType65548) TypeVal() Type { + return 65548 +} + +func (t *tlvType65548) tlv() {} + +type TlvType65548 = *tlvType65548 + +type tlvType65549 struct{} + +func (t *tlvType65549) TypeVal() Type { + return 65549 +} + +func (t *tlvType65549) tlv() {} + +type TlvType65549 = *tlvType65549 + +type tlvType65550 struct{} + +func (t *tlvType65550) TypeVal() Type { + return 65550 +} + +func (t *tlvType65550) tlv() {} + +type TlvType65550 = *tlvType65550 + +type tlvType65551 struct{} + +func (t *tlvType65551) TypeVal() Type { + return 65551 +} + +func (t *tlvType65551) tlv() {} + +type TlvType65551 = *tlvType65551 + +type tlvType65552 struct{} + +func (t *tlvType65552) TypeVal() Type { + return 65552 +} + +func (t *tlvType65552) tlv() {} + +type TlvType65552 = *tlvType65552 + +type tlvType65553 struct{} + +func (t *tlvType65553) TypeVal() Type { + return 65553 +} + +func (t *tlvType65553) tlv() {} + +type TlvType65553 = *tlvType65553 + +type tlvType65554 struct{} + +func (t *tlvType65554) TypeVal() Type { + return 65554 +} + +func (t *tlvType65554) tlv() {} + +type TlvType65554 = *tlvType65554 + +type tlvType65555 struct{} + +func (t *tlvType65555) TypeVal() Type { + return 65555 +} + +func (t *tlvType65555) tlv() {} + +type TlvType65555 = *tlvType65555 + +type tlvType65556 struct{} + +func (t *tlvType65556) TypeVal() Type { + return 65556 +} + +func (t *tlvType65556) tlv() {} + +type TlvType65556 = *tlvType65556 + +type tlvType65557 struct{} + +func (t *tlvType65557) TypeVal() Type { + return 65557 +} + +func (t *tlvType65557) tlv() {} + +type TlvType65557 = *tlvType65557 + +type tlvType65558 struct{} + +func (t *tlvType65558) TypeVal() Type { + return 65558 +} + +func (t *tlvType65558) tlv() {} + +type TlvType65558 = *tlvType65558 + +type tlvType65559 struct{} + +func (t *tlvType65559) TypeVal() Type { + return 65559 +} + +func (t *tlvType65559) tlv() {} + +type TlvType65559 = *tlvType65559 + +type tlvType65560 struct{} + +func (t *tlvType65560) TypeVal() Type { + return 65560 +} + +func (t *tlvType65560) tlv() {} + +type TlvType65560 = *tlvType65560 + +type tlvType65561 struct{} + +func (t *tlvType65561) TypeVal() Type { + return 65561 +} + +func (t *tlvType65561) tlv() {} + +type TlvType65561 = *tlvType65561 + +type tlvType65562 struct{} + +func (t *tlvType65562) TypeVal() Type { + return 65562 +} + +func (t *tlvType65562) tlv() {} + +type TlvType65562 = *tlvType65562 + +type tlvType65563 struct{} + +func (t *tlvType65563) TypeVal() Type { + return 65563 +} + +func (t *tlvType65563) tlv() {} + +type TlvType65563 = *tlvType65563 + +type tlvType65564 struct{} + +func (t *tlvType65564) TypeVal() Type { + return 65564 +} + +func (t *tlvType65564) tlv() {} + +type TlvType65564 = *tlvType65564 + +type tlvType65565 struct{} + +func (t *tlvType65565) TypeVal() Type { + return 65565 +} + +func (t *tlvType65565) tlv() {} + +type TlvType65565 = *tlvType65565 + +type tlvType65566 struct{} + +func (t *tlvType65566) TypeVal() Type { + return 65566 +} + +func (t *tlvType65566) tlv() {} + +type TlvType65566 = *tlvType65566 + +type tlvType65567 struct{} + +func (t *tlvType65567) TypeVal() Type { + return 65567 +} + +func (t *tlvType65567) tlv() {} + +type TlvType65567 = *tlvType65567 + +type tlvType65568 struct{} + +func (t *tlvType65568) TypeVal() Type { + return 65568 +} + +func (t *tlvType65568) tlv() {} + +type TlvType65568 = *tlvType65568 + +type tlvType65569 struct{} + +func (t *tlvType65569) TypeVal() Type { + return 65569 +} + +func (t *tlvType65569) tlv() {} + +type TlvType65569 = *tlvType65569 + +type tlvType65570 struct{} + +func (t *tlvType65570) TypeVal() Type { + return 65570 +} + +func (t *tlvType65570) tlv() {} + +type TlvType65570 = *tlvType65570 + +type tlvType65571 struct{} + +func (t *tlvType65571) TypeVal() Type { + return 65571 +} + +func (t *tlvType65571) tlv() {} + +type TlvType65571 = *tlvType65571 + +type tlvType65572 struct{} + +func (t *tlvType65572) TypeVal() Type { + return 65572 +} + +func (t *tlvType65572) tlv() {} + +type TlvType65572 = *tlvType65572 + +type tlvType65573 struct{} + +func (t *tlvType65573) TypeVal() Type { + return 65573 +} + +func (t *tlvType65573) tlv() {} + +type TlvType65573 = *tlvType65573 + +type tlvType65574 struct{} + +func (t *tlvType65574) TypeVal() Type { + return 65574 +} + +func (t *tlvType65574) tlv() {} + +type TlvType65574 = *tlvType65574 + +type tlvType65575 struct{} + +func (t *tlvType65575) TypeVal() Type { + return 65575 +} + +func (t *tlvType65575) tlv() {} + +type TlvType65575 = *tlvType65575 + +type tlvType65576 struct{} + +func (t *tlvType65576) TypeVal() Type { + return 65576 +} + +func (t *tlvType65576) tlv() {} + +type TlvType65576 = *tlvType65576 + +type tlvType65577 struct{} + +func (t *tlvType65577) TypeVal() Type { + return 65577 +} + +func (t *tlvType65577) tlv() {} + +type TlvType65577 = *tlvType65577 + +type tlvType65578 struct{} + +func (t *tlvType65578) TypeVal() Type { + return 65578 +} + +func (t *tlvType65578) tlv() {} + +type TlvType65578 = *tlvType65578 + +type tlvType65579 struct{} + +func (t *tlvType65579) TypeVal() Type { + return 65579 +} + +func (t *tlvType65579) tlv() {} + +type TlvType65579 = *tlvType65579 + +type tlvType65580 struct{} + +func (t *tlvType65580) TypeVal() Type { + return 65580 +} + +func (t *tlvType65580) tlv() {} + +type TlvType65580 = *tlvType65580 + +type tlvType65581 struct{} + +func (t *tlvType65581) TypeVal() Type { + return 65581 +} + +func (t *tlvType65581) tlv() {} + +type TlvType65581 = *tlvType65581 + +type tlvType65582 struct{} + +func (t *tlvType65582) TypeVal() Type { + return 65582 +} + +func (t *tlvType65582) tlv() {} + +type TlvType65582 = *tlvType65582 + +type tlvType65583 struct{} + +func (t *tlvType65583) TypeVal() Type { + return 65583 +} + +func (t *tlvType65583) tlv() {} + +type TlvType65583 = *tlvType65583 + +type tlvType65584 struct{} + +func (t *tlvType65584) TypeVal() Type { + return 65584 +} + +func (t *tlvType65584) tlv() {} + +type TlvType65584 = *tlvType65584 + +type tlvType65585 struct{} + +func (t *tlvType65585) TypeVal() Type { + return 65585 +} + +func (t *tlvType65585) tlv() {} + +type TlvType65585 = *tlvType65585 + +type tlvType65586 struct{} + +func (t *tlvType65586) TypeVal() Type { + return 65586 +} + +func (t *tlvType65586) tlv() {} + +type TlvType65586 = *tlvType65586 + +type tlvType65587 struct{} + +func (t *tlvType65587) TypeVal() Type { + return 65587 +} + +func (t *tlvType65587) tlv() {} + +type TlvType65587 = *tlvType65587 + +type tlvType65588 struct{} + +func (t *tlvType65588) TypeVal() Type { + return 65588 +} + +func (t *tlvType65588) tlv() {} + +type TlvType65588 = *tlvType65588 + +type tlvType65589 struct{} + +func (t *tlvType65589) TypeVal() Type { + return 65589 +} + +func (t *tlvType65589) tlv() {} + +type TlvType65589 = *tlvType65589 + +type tlvType65590 struct{} + +func (t *tlvType65590) TypeVal() Type { + return 65590 +} + +func (t *tlvType65590) tlv() {} + +type TlvType65590 = *tlvType65590 + +type tlvType65591 struct{} + +func (t *tlvType65591) TypeVal() Type { + return 65591 +} + +func (t *tlvType65591) tlv() {} + +type TlvType65591 = *tlvType65591 + +type tlvType65592 struct{} + +func (t *tlvType65592) TypeVal() Type { + return 65592 +} + +func (t *tlvType65592) tlv() {} + +type TlvType65592 = *tlvType65592 + +type tlvType65593 struct{} + +func (t *tlvType65593) TypeVal() Type { + return 65593 +} + +func (t *tlvType65593) tlv() {} + +type TlvType65593 = *tlvType65593 + +type tlvType65594 struct{} + +func (t *tlvType65594) TypeVal() Type { + return 65594 +} + +func (t *tlvType65594) tlv() {} + +type TlvType65594 = *tlvType65594 + +type tlvType65595 struct{} + +func (t *tlvType65595) TypeVal() Type { + return 65595 +} + +func (t *tlvType65595) tlv() {} + +type TlvType65595 = *tlvType65595 + +type tlvType65596 struct{} + +func (t *tlvType65596) TypeVal() Type { + return 65596 +} + +func (t *tlvType65596) tlv() {} + +type TlvType65596 = *tlvType65596 + +type tlvType65597 struct{} + +func (t *tlvType65597) TypeVal() Type { + return 65597 +} + +func (t *tlvType65597) tlv() {} + +type TlvType65597 = *tlvType65597 + +type tlvType65598 struct{} + +func (t *tlvType65598) TypeVal() Type { + return 65598 +} + +func (t *tlvType65598) tlv() {} + +type TlvType65598 = *tlvType65598 + +type tlvType65599 struct{} + +func (t *tlvType65599) TypeVal() Type { + return 65599 +} + +func (t *tlvType65599) tlv() {} + +type TlvType65599 = *tlvType65599 + +type tlvType65600 struct{} + +func (t *tlvType65600) TypeVal() Type { + return 65600 +} + +func (t *tlvType65600) tlv() {} + +type TlvType65600 = *tlvType65600 + +type tlvType65601 struct{} + +func (t *tlvType65601) TypeVal() Type { + return 65601 +} + +func (t *tlvType65601) tlv() {} + +type TlvType65601 = *tlvType65601 + +type tlvType65602 struct{} + +func (t *tlvType65602) TypeVal() Type { + return 65602 +} + +func (t *tlvType65602) tlv() {} + +type TlvType65602 = *tlvType65602 + +type tlvType65603 struct{} + +func (t *tlvType65603) TypeVal() Type { + return 65603 +} + +func (t *tlvType65603) tlv() {} + +type TlvType65603 = *tlvType65603 + +type tlvType65604 struct{} + +func (t *tlvType65604) TypeVal() Type { + return 65604 +} + +func (t *tlvType65604) tlv() {} + +type TlvType65604 = *tlvType65604 + +type tlvType65605 struct{} + +func (t *tlvType65605) TypeVal() Type { + return 65605 +} + +func (t *tlvType65605) tlv() {} + +type TlvType65605 = *tlvType65605 + +type tlvType65606 struct{} + +func (t *tlvType65606) TypeVal() Type { + return 65606 +} + +func (t *tlvType65606) tlv() {} + +type TlvType65606 = *tlvType65606 + +type tlvType65607 struct{} + +func (t *tlvType65607) TypeVal() Type { + return 65607 +} + +func (t *tlvType65607) tlv() {} + +type TlvType65607 = *tlvType65607 + +type tlvType65608 struct{} + +func (t *tlvType65608) TypeVal() Type { + return 65608 +} + +func (t *tlvType65608) tlv() {} + +type TlvType65608 = *tlvType65608 + +type tlvType65609 struct{} + +func (t *tlvType65609) TypeVal() Type { + return 65609 +} + +func (t *tlvType65609) tlv() {} + +type TlvType65609 = *tlvType65609 + +type tlvType65610 struct{} + +func (t *tlvType65610) TypeVal() Type { + return 65610 +} + +func (t *tlvType65610) tlv() {} + +type TlvType65610 = *tlvType65610 + +type tlvType65611 struct{} + +func (t *tlvType65611) TypeVal() Type { + return 65611 +} + +func (t *tlvType65611) tlv() {} + +type TlvType65611 = *tlvType65611 + +type tlvType65612 struct{} + +func (t *tlvType65612) TypeVal() Type { + return 65612 +} + +func (t *tlvType65612) tlv() {} + +type TlvType65612 = *tlvType65612 + +type tlvType65613 struct{} + +func (t *tlvType65613) TypeVal() Type { + return 65613 +} + +func (t *tlvType65613) tlv() {} + +type TlvType65613 = *tlvType65613 + +type tlvType65614 struct{} + +func (t *tlvType65614) TypeVal() Type { + return 65614 +} + +func (t *tlvType65614) tlv() {} + +type TlvType65614 = *tlvType65614 + +type tlvType65615 struct{} + +func (t *tlvType65615) TypeVal() Type { + return 65615 +} + +func (t *tlvType65615) tlv() {} + +type TlvType65615 = *tlvType65615 + +type tlvType65616 struct{} + +func (t *tlvType65616) TypeVal() Type { + return 65616 +} + +func (t *tlvType65616) tlv() {} + +type TlvType65616 = *tlvType65616 + +type tlvType65617 struct{} + +func (t *tlvType65617) TypeVal() Type { + return 65617 +} + +func (t *tlvType65617) tlv() {} + +type TlvType65617 = *tlvType65617 + +type tlvType65618 struct{} + +func (t *tlvType65618) TypeVal() Type { + return 65618 +} + +func (t *tlvType65618) tlv() {} + +type TlvType65618 = *tlvType65618 + +type tlvType65619 struct{} + +func (t *tlvType65619) TypeVal() Type { + return 65619 +} + +func (t *tlvType65619) tlv() {} + +type TlvType65619 = *tlvType65619 + +type tlvType65620 struct{} + +func (t *tlvType65620) TypeVal() Type { + return 65620 +} + +func (t *tlvType65620) tlv() {} + +type TlvType65620 = *tlvType65620 + +type tlvType65621 struct{} + +func (t *tlvType65621) TypeVal() Type { + return 65621 +} + +func (t *tlvType65621) tlv() {} + +type TlvType65621 = *tlvType65621 + +type tlvType65622 struct{} + +func (t *tlvType65622) TypeVal() Type { + return 65622 +} + +func (t *tlvType65622) tlv() {} + +type TlvType65622 = *tlvType65622 + +type tlvType65623 struct{} + +func (t *tlvType65623) TypeVal() Type { + return 65623 +} + +func (t *tlvType65623) tlv() {} + +type TlvType65623 = *tlvType65623 + +type tlvType65624 struct{} + +func (t *tlvType65624) TypeVal() Type { + return 65624 +} + +func (t *tlvType65624) tlv() {} + +type TlvType65624 = *tlvType65624 + +type tlvType65625 struct{} + +func (t *tlvType65625) TypeVal() Type { + return 65625 +} + +func (t *tlvType65625) tlv() {} + +type TlvType65625 = *tlvType65625 + +type tlvType65626 struct{} + +func (t *tlvType65626) TypeVal() Type { + return 65626 +} + +func (t *tlvType65626) tlv() {} + +type TlvType65626 = *tlvType65626 + +type tlvType65627 struct{} + +func (t *tlvType65627) TypeVal() Type { + return 65627 +} + +func (t *tlvType65627) tlv() {} + +type TlvType65627 = *tlvType65627 + +type tlvType65628 struct{} + +func (t *tlvType65628) TypeVal() Type { + return 65628 +} + +func (t *tlvType65628) tlv() {} + +type TlvType65628 = *tlvType65628 + +type tlvType65629 struct{} + +func (t *tlvType65629) TypeVal() Type { + return 65629 +} + +func (t *tlvType65629) tlv() {} + +type TlvType65629 = *tlvType65629 + +type tlvType65630 struct{} + +func (t *tlvType65630) TypeVal() Type { + return 65630 +} + +func (t *tlvType65630) tlv() {} + +type TlvType65630 = *tlvType65630 + +type tlvType65631 struct{} + +func (t *tlvType65631) TypeVal() Type { + return 65631 +} + +func (t *tlvType65631) tlv() {} + +type TlvType65631 = *tlvType65631 + +type tlvType65632 struct{} + +func (t *tlvType65632) TypeVal() Type { + return 65632 +} + +func (t *tlvType65632) tlv() {} + +type TlvType65632 = *tlvType65632 + +type tlvType65633 struct{} + +func (t *tlvType65633) TypeVal() Type { + return 65633 +} + +func (t *tlvType65633) tlv() {} + +type TlvType65633 = *tlvType65633 + +type tlvType65634 struct{} + +func (t *tlvType65634) TypeVal() Type { + return 65634 +} + +func (t *tlvType65634) tlv() {} + +type TlvType65634 = *tlvType65634 + +type tlvType65635 struct{} + +func (t *tlvType65635) TypeVal() Type { + return 65635 +} + +func (t *tlvType65635) tlv() {} + +type TlvType65635 = *tlvType65635 + +type tlvType65636 struct{} + +func (t *tlvType65636) TypeVal() Type { + return 65636 +} + +func (t *tlvType65636) tlv() {} + +type TlvType65636 = *tlvType65636 diff --git a/tor/cmd_onion.go b/tor/cmd_onion.go index 72f508fc0..6e60504a9 100644 --- a/tor/cmd_onion.go +++ b/tor/cmd_onion.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" ) @@ -105,7 +104,7 @@ func (f *OnionFile) StorePrivateKey(privateKey []byte) error { privateKeyContent = b.Bytes() } - err := ioutil.WriteFile( + err := os.WriteFile( f.privateKeyPath, privateKeyContent, f.privateKeyPerm, ) if err != nil { @@ -124,7 +123,7 @@ func (f *OnionFile) PrivateKey() ([]byte, error) { } // Try to read the Tor private key to pass into the AddOnion call. - privateKeyContent, err := ioutil.ReadFile(f.privateKeyPath) + privateKeyContent, err := os.ReadFile(f.privateKeyPath) if err != nil { return nil, err } diff --git a/tor/controller.go b/tor/controller.go index 241b3ddbe..47ea6e129 100644 --- a/tor/controller.go +++ b/tor/controller.go @@ -8,8 +8,8 @@ import ( "encoding/hex" "errors" "fmt" - "io/ioutil" "net/textproto" + "os" "regexp" "strconv" "strings" @@ -597,7 +597,7 @@ func (c *Controller) getAuthCookie(info protocolInfo) ([]byte, error) { cookieFilePath = strings.Trim(cookieFilePath, "\"") // Read the cookie from the file and ensure it has the correct length. - cookie, err := ioutil.ReadFile(cookieFilePath) + cookie, err := os.ReadFile(cookieFilePath) if err != nil { return nil, err } diff --git a/watchtower/blob/commitments.go b/watchtower/blob/commitments.go index f86240447..994c55caf 100644 --- a/watchtower/blob/commitments.go +++ b/watchtower/blob/commitments.go @@ -5,6 +5,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" ) @@ -115,7 +116,7 @@ func (c CommitmentType) ToRemoteWitnessType() (input.WitnessType, error) { // ToRemoteWitnessSize is the size of the witness that will be required to spend // the to_remote output. -func (c CommitmentType) ToRemoteWitnessSize() (int, error) { +func (c CommitmentType) ToRemoteWitnessSize() (lntypes.WeightUnit, error) { switch c { // Legacy channels (both tweaked and non-tweaked) spend from P2WKH // output. @@ -137,7 +138,7 @@ func (c CommitmentType) ToRemoteWitnessSize() (int, error) { // ToLocalWitnessSize is the size of the witness that will be required to spend // the to_local output. -func (c CommitmentType) ToLocalWitnessSize() (int, error) { +func (c CommitmentType) ToLocalWitnessSize() (lntypes.WeightUnit, error) { switch c { // An older ToLocalPenaltyWitnessSize constant used to underestimate the // size by one byte. The difference in weight can cause different output diff --git a/watchtower/lookout/justice_descriptor.go b/watchtower/lookout/justice_descriptor.go index 2c0623e65..96b414470 100644 --- a/watchtower/lookout/justice_descriptor.go +++ b/watchtower/lookout/justice_descriptor.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/watchtower/blob" "github.com/lightningnetwork/lnd/watchtower/wtdb" ) @@ -123,7 +124,7 @@ func (p *JusticeDescriptor) commitToRemoteInput() (*breachedInput, error) { // assembleJusticeTxn accepts the breached inputs recovered from state update // and attempts to construct the justice transaction that sweeps the victims // funds to their wallet and claims the watchtower's reward. -func (p *JusticeDescriptor) assembleJusticeTxn(txWeight int64, +func (p *JusticeDescriptor) assembleJusticeTxn(txWeight lntypes.WeightUnit, inputs ...*breachedInput) (*wire.MsgTx, error) { justiceTxn := wire.NewMsgTx(2) @@ -287,7 +288,7 @@ func (p *JusticeDescriptor) CreateJusticeTxn() (*wire.MsgTx, error) { // TODO(conner): sweep htlc outputs - txWeight := int64(weightEstimate.Weight()) + txWeight := weightEstimate.Weight() return p.assembleJusticeTxn(txWeight, sweepInputs...) } diff --git a/watchtower/lookout/justice_descriptor_test.go b/watchtower/lookout/justice_descriptor_test.go index 29c7f9a5a..5045b4a0f 100644 --- a/watchtower/lookout/justice_descriptor_test.go +++ b/watchtower/lookout/justice_descriptor_test.go @@ -318,7 +318,7 @@ func testJusticeDescriptor(t *testing.T, blobType blob.Type) { } outputs, err := policy.ComputeJusticeTxOuts( - totalAmount, int64(txWeight), justiceKit.SweepAddress(), + totalAmount, txWeight, justiceKit.SweepAddress(), sessionInfo.RewardAddress, ) require.NoError(t, err) diff --git a/watchtower/tlv_bench_test.go b/watchtower/tlv_bench_test.go index 5ffabde08..e6d8decd7 100644 --- a/watchtower/tlv_bench_test.go +++ b/watchtower/tlv_bench_test.go @@ -3,7 +3,6 @@ package watchtower_test import ( "bytes" "io" - "io/ioutil" "testing" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -104,7 +103,7 @@ func BenchmarkEncodeCreateSession(t *testing.B) { var err error for i := 0; i < t.N; i++ { - err = m.Encode(ioutil.Discard, 0) + err = m.Encode(io.Discard, 0) } require.NoError(t, err) } @@ -118,7 +117,7 @@ func BenchmarkEncodeCreateSessionTLV(t *testing.B) { var err error for i := 0; i < t.N; i++ { - err = m.Encode(ioutil.Discard) + err = m.Encode(io.Discard) } require.NoError(t, err) } diff --git a/watchtower/wtclient/backup_task.go b/watchtower/wtclient/backup_task.go index 49f917514..1e90d7b37 100644 --- a/watchtower/wtclient/backup_task.go +++ b/watchtower/wtclient/backup_task.go @@ -216,7 +216,7 @@ func (t *backupTask) bindSession(session *wtdb.ClientSessionBody, // Now, compute the output values depending on whether FlagReward is set // in the current session's policy. outputs, err := session.Policy.ComputeJusticeTxOuts( - t.totalAmt, int64(weightEstimate.Weight()), + t.totalAmt, weightEstimate.Weight(), t.sweepPkScript, session.RewardPkScript, ) if err != nil { diff --git a/watchtower/wtpolicy/policy.go b/watchtower/wtpolicy/policy.go index 9d5763fb2..89804685b 100644 --- a/watchtower/wtpolicy/policy.go +++ b/watchtower/wtpolicy/policy.go @@ -6,6 +6,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" @@ -180,8 +181,9 @@ func (p *Policy) Validate() error { // that pays no reward to the tower. The value is computed using the weight of // of the justice transaction and subtracting an amount that satisfies the // policy's fee rate. -func (p *Policy) ComputeAltruistOutput(totalAmt btcutil.Amount, - txWeight int64, sweepScript []byte) (btcutil.Amount, error) { +func (p *Policy) ComputeAltruistOutput( + totalAmt btcutil.Amount, txWeight lntypes.WeightUnit, + sweepScript []byte) (btcutil.Amount, error) { txFee := p.SweepFeeRate.FeeForWeight(txWeight) if txFee > totalAmt { @@ -204,7 +206,7 @@ func (p *Policy) ComputeAltruistOutput(totalAmt btcutil.Amount, // and reward rate. The reward to he tower is subtracted first, before // splitting the remaining balance amongst the victim and fees. func (p *Policy) ComputeRewardOutputs(totalAmt btcutil.Amount, - txWeight int64, + txWeight lntypes.WeightUnit, rewardScript []byte) (btcutil.Amount, btcutil.Amount, error) { txFee := p.SweepFeeRate.FeeForWeight(txWeight) @@ -266,7 +268,8 @@ func ComputeRewardAmount(total btcutil.Amount, base, rate uint32) btcutil.Amount // rewardPkScript is the pkScript of the tower where its reward will be // deposited, and will be // ignored if the blob type does not specify a reward. -func (p *Policy) ComputeJusticeTxOuts(totalAmt btcutil.Amount, txWeight int64, +func (p *Policy) ComputeJusticeTxOuts( + totalAmt btcutil.Amount, txWeight lntypes.WeightUnit, sweepPkScript, rewardPkScript []byte) ([]*wire.TxOut, error) { var outputs []*wire.TxOut