Merge pull request #8960 from lightningnetwork/0-19-staging-rebased

[custom channels 5/5]: merge custom channel staging branch into master
This commit is contained in:
Olaoluwa Osuntokun authored and GitHub committed 2024-10-03 10:07:16 -07:00
commit 2d333178c3
56 files changed
+2283 -901

No files matched your search

+9
View File
@@ -50,6 +50,7 @@ import (
"github.com/lightningnetwork/lnd/rpcperms" "github.com/lightningnetwork/lnd/rpcperms"
"github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/signal"
"github.com/lightningnetwork/lnd/sqldb" "github.com/lightningnetwork/lnd/sqldb"
"github.com/lightningnetwork/lnd/sweep"
"github.com/lightningnetwork/lnd/walletunlocker" "github.com/lightningnetwork/lnd/walletunlocker"
"github.com/lightningnetwork/lnd/watchtower" "github.com/lightningnetwork/lnd/watchtower"
"github.com/lightningnetwork/lnd/watchtower/wtclient" "github.com/lightningnetwork/lnd/watchtower/wtclient"
@@ -187,6 +188,14 @@ type AuxComponents struct {
// AuxChanCloser is an optional channel closer that can be used to // AuxChanCloser is an optional channel closer that can be used to
// modify the way a coop-close transaction is constructed. // modify the way a coop-close transaction is constructed.
AuxChanCloser fn.Option[chancloser.AuxChanCloser] AuxChanCloser fn.Option[chancloser.AuxChanCloser]
// AuxSweeper is an optional interface that can be used to modify the
// way sweep transaction are generated.
AuxSweeper fn.Option[sweep.AuxSweeper]
// AuxContractResolver is an optional interface that can be used to
// modify the way contracts are resolved.
AuxContractResolver fn.Option[lnwallet.AuxContractResolver]
} }
// DefaultWalletImpl is the default implementation of our normal, btcwallet // DefaultWalletImpl is the default implementation of our normal, btcwallet
+160 -29
View File
@@ -15,6 +15,7 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/labels" "github.com/lightningnetwork/lnd/labels"
@@ -22,6 +23,8 @@ import (
"github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/sweep"
"github.com/lightningnetwork/lnd/tlv"
) )
const ( const (
@@ -147,7 +150,7 @@ type BreachConfig struct {
Estimator chainfee.Estimator Estimator chainfee.Estimator
// GenSweepScript generates the receiving scripts for swept outputs. // GenSweepScript generates the receiving scripts for swept outputs.
GenSweepScript func() ([]byte, error) GenSweepScript func() fn.Result[lnwallet.AddrWithKey]
// Notifier provides a publish/subscribe interface for event driven // Notifier provides a publish/subscribe interface for event driven
// notifications regarding the confirmation of txids. // notifications regarding the confirmation of txids.
@@ -172,6 +175,10 @@ type BreachConfig struct {
// breached channels. This is used in conjunction with DB to recover // breached channels. This is used in conjunction with DB to recover
// from crashes, restarts, or other failures. // from crashes, restarts, or other failures.
Store RetributionStorer Store RetributionStorer
// AuxSweeper is an optional interface that can be used to modify the
// way sweep transaction are generated.
AuxSweeper fn.Option[sweep.AuxSweeper]
} }
// BreachArbitrator is a special subsystem which is responsible for watching and // BreachArbitrator is a special subsystem which is responsible for watching and
@@ -735,10 +742,28 @@ justiceTxBroadcast:
brarLog.Debugf("Broadcasting justice tx: %v", lnutils.SpewLogClosure( brarLog.Debugf("Broadcasting justice tx: %v", lnutils.SpewLogClosure(
finalTx)) finalTx))
// As we're about to broadcast our breach transaction, we'll notify the
// aux sweeper of our broadcast attempt first.
err = fn.MapOptionZ(b.cfg.AuxSweeper, func(aux sweep.AuxSweeper) error {
bumpReq := sweep.BumpRequest{
Inputs: finalTx.inputs,
DeliveryAddress: finalTx.sweepAddr,
ExtraTxOut: finalTx.extraTxOut,
}
return aux.NotifyBroadcast(
&bumpReq, finalTx.justiceTx, finalTx.fee,
)
})
if err != nil {
brarLog.Errorf("unable to notify broadcast: %w", err)
return
}
// We'll now attempt to broadcast the transaction which finalized the // We'll now attempt to broadcast the transaction which finalized the
// channel's retribution against the cheating counter party. // channel's retribution against the cheating counter party.
label := labels.MakeLabel(labels.LabelTypeJusticeTransaction, nil) label := labels.MakeLabel(labels.LabelTypeJusticeTransaction, nil)
err = b.cfg.PublishTransaction(finalTx, label) err = b.cfg.PublishTransaction(finalTx.justiceTx, label)
if err != nil { if err != nil {
brarLog.Errorf("Unable to broadcast justice tx: %v", err) brarLog.Errorf("Unable to broadcast justice tx: %v", err)
} }
@@ -858,7 +883,9 @@ Loop:
"spending commitment outs: %v", "spending commitment outs: %v",
lnutils.SpewLogClosure(tx)) lnutils.SpewLogClosure(tx))
err = b.cfg.PublishTransaction(tx, label) err = b.cfg.PublishTransaction(
tx.justiceTx, label,
)
if err != nil { if err != nil {
brarLog.Warnf("Unable to broadcast "+ brarLog.Warnf("Unable to broadcast "+
"commit out spending justice "+ "commit out spending justice "+
@@ -873,7 +900,9 @@ Loop:
"spending HTLC outs: %v", "spending HTLC outs: %v",
lnutils.SpewLogClosure(tx)) lnutils.SpewLogClosure(tx))
err = b.cfg.PublishTransaction(tx, label) err = b.cfg.PublishTransaction(
tx.justiceTx, label,
)
if err != nil { if err != nil {
brarLog.Warnf("Unable to broadcast "+ brarLog.Warnf("Unable to broadcast "+
"HTLC out spending justice "+ "HTLC out spending justice "+
@@ -888,7 +917,9 @@ Loop:
"spending second-level HTLC output: %v", "spending second-level HTLC output: %v",
lnutils.SpewLogClosure(tx)) lnutils.SpewLogClosure(tx))
err = b.cfg.PublishTransaction(tx, label) err = b.cfg.PublishTransaction(
tx.justiceTx, label,
)
if err != nil { if err != nil {
brarLog.Warnf("Unable to broadcast "+ brarLog.Warnf("Unable to broadcast "+
"second-level HTLC out "+ "second-level HTLC out "+
@@ -1067,15 +1098,18 @@ type breachedOutput struct {
secondLevelTapTweak [32]byte secondLevelTapTweak [32]byte
witnessFunc input.WitnessGenerator witnessFunc input.WitnessGenerator
resolutionBlob fn.Option[tlv.Blob]
// TODO(roasbeef): function opt and hook into brar
} }
// makeBreachedOutput assembles a new breachedOutput that can be used by the // makeBreachedOutput assembles a new breachedOutput that can be used by the
// breach arbiter to construct a justice or sweep transaction. // breach arbiter to construct a justice or sweep transaction.
func makeBreachedOutput(outpoint *wire.OutPoint, func makeBreachedOutput(outpoint *wire.OutPoint,
witnessType input.StandardWitnessType, witnessType input.StandardWitnessType, secondLevelScript []byte,
secondLevelScript []byte, signDescriptor *input.SignDescriptor, confHeight uint32,
signDescriptor *input.SignDescriptor, resolutionBlob fn.Option[tlv.Blob]) breachedOutput {
confHeight uint32) breachedOutput {
amount := signDescriptor.Output.Value amount := signDescriptor.Output.Value
@@ -1086,6 +1120,7 @@ func makeBreachedOutput(outpoint *wire.OutPoint,
witnessType: witnessType, witnessType: witnessType,
signDesc: *signDescriptor, signDesc: *signDescriptor,
confHeight: confHeight, confHeight: confHeight,
resolutionBlob: resolutionBlob,
} }
} }
@@ -1174,6 +1209,12 @@ func (bo *breachedOutput) UnconfParent() *input.TxInfo {
return nil return nil
} }
// ResolutionBlob returns a special opaque blob to be used to sweep/resolve this
// input.
func (bo *breachedOutput) ResolutionBlob() fn.Option[tlv.Blob] {
return bo.resolutionBlob
}
// Add compile-time constraint ensuring breachedOutput implements the Input // Add compile-time constraint ensuring breachedOutput implements the Input
// interface. // interface.
var _ input.Input = (*breachedOutput)(nil) var _ input.Input = (*breachedOutput)(nil)
@@ -1258,6 +1299,7 @@ func newRetributionInfo(chanPoint *wire.OutPoint,
nil, nil,
breachInfo.LocalOutputSignDesc, breachInfo.LocalOutputSignDesc,
breachInfo.BreachHeight, breachInfo.BreachHeight,
breachInfo.LocalResolutionBlob,
) )
breachedOutputs = append(breachedOutputs, localOutput) breachedOutputs = append(breachedOutputs, localOutput)
@@ -1284,6 +1326,7 @@ func newRetributionInfo(chanPoint *wire.OutPoint,
nil, nil,
breachInfo.RemoteOutputSignDesc, breachInfo.RemoteOutputSignDesc,
breachInfo.BreachHeight, breachInfo.BreachHeight,
breachInfo.RemoteResolutionBlob,
) )
breachedOutputs = append(breachedOutputs, remoteOutput) breachedOutputs = append(breachedOutputs, remoteOutput)
@@ -1318,6 +1361,7 @@ func newRetributionInfo(chanPoint *wire.OutPoint,
breachInfo.HtlcRetributions[i].SecondLevelWitnessScript, breachInfo.HtlcRetributions[i].SecondLevelWitnessScript,
&breachInfo.HtlcRetributions[i].SignDesc, &breachInfo.HtlcRetributions[i].SignDesc,
breachInfo.BreachHeight, breachInfo.BreachHeight,
breachInfo.HtlcRetributions[i].ResolutionBlob,
) )
// For taproot outputs, we also need to hold onto the second // For taproot outputs, we also need to hold onto the second
@@ -1357,10 +1401,10 @@ func newRetributionInfo(chanPoint *wire.OutPoint,
// spend the to_local output and commitment level HTLC outputs separately, // spend the to_local output and commitment level HTLC outputs separately,
// before the CSV locks expire. // before the CSV locks expire.
type justiceTxVariants struct { type justiceTxVariants struct {
spendAll *wire.MsgTx spendAll *justiceTxCtx
spendCommitOuts *wire.MsgTx spendCommitOuts *justiceTxCtx
spendHTLCs *wire.MsgTx spendHTLCs *justiceTxCtx
spendSecondLevelHTLCs []*wire.MsgTx spendSecondLevelHTLCs []*justiceTxCtx
} }
// createJusticeTx creates transactions which exacts "justice" by sweeping ALL // createJusticeTx creates transactions which exacts "justice" by sweeping ALL
@@ -1424,7 +1468,9 @@ func (b *BreachArbitrator) createJusticeTx(
err) err)
} }
secondLevelSweeps := make([]*wire.MsgTx, 0, len(secondLevelInputs)) // TODO(roasbeef): only register one of them?
secondLevelSweeps := make([]*justiceTxCtx, 0, len(secondLevelInputs))
for _, input := range secondLevelInputs { for _, input := range secondLevelInputs {
sweepTx, err := b.createSweepTx(input) sweepTx, err := b.createSweepTx(input)
if err != nil { if err != nil {
@@ -1441,9 +1487,23 @@ func (b *BreachArbitrator) createJusticeTx(
return txs, nil return txs, nil
} }
// justiceTxCtx contains the justice transaction along with other related meta
// data.
type justiceTxCtx struct {
justiceTx *wire.MsgTx
sweepAddr lnwallet.AddrWithKey
extraTxOut fn.Option[sweep.SweepOutput]
fee btcutil.Amount
inputs []input.Input
}
// createSweepTx creates a tx that sweeps the passed inputs back to our wallet. // createSweepTx creates a tx that sweeps the passed inputs back to our wallet.
func (b *BreachArbitrator) createSweepTx(inputs ...input.Input) (*wire.MsgTx, func (b *BreachArbitrator) createSweepTx(
error) { inputs ...input.Input) (*justiceTxCtx, error) {
if len(inputs) == 0 { if len(inputs) == 0 {
return nil, nil return nil, nil
@@ -1466,6 +1526,18 @@ func (b *BreachArbitrator) createSweepTx(inputs ...input.Input) (*wire.MsgTx,
// nLockTime, and output are already included in the TxWeightEstimator. // nLockTime, and output are already included in the TxWeightEstimator.
weightEstimate.AddP2TROutput() weightEstimate.AddP2TROutput()
// If any of our inputs has a resolution blob, then we'll add another
// P2TR _output_, since we'll want to separate the custom channel
// outputs from the regular, BTC only outputs. So we only need one such
// output, which'll carry the custom channel "valuables" from both the
// breached commitment and HTLC outputs.
hasBlobs := fn.Any(func(i input.Input) bool {
return i.ResolutionBlob().IsSome()
}, inputs)
if hasBlobs {
weightEstimate.AddP2TROutput()
}
// Next, we iterate over the breached outputs contained in the // Next, we iterate over the breached outputs contained in the
// retribution info. For each, we switch over the witness type such // retribution info. For each, we switch over the witness type such
// that we contribute the appropriate weight for each input and // that we contribute the appropriate weight for each input and
@@ -1499,13 +1571,13 @@ func (b *BreachArbitrator) createSweepTx(inputs ...input.Input) (*wire.MsgTx,
// sweepSpendableOutputsTxn creates a signed transaction from a sequence of // sweepSpendableOutputsTxn creates a signed transaction from a sequence of
// spendable outputs by sweeping the funds into a single p2wkh output. // spendable outputs by sweeping the funds into a single p2wkh output.
func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit, func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit,
inputs ...input.Input) (*wire.MsgTx, error) { inputs ...input.Input) (*justiceTxCtx, error) {
// First, we obtain a new public key script from the wallet which we'll // First, we obtain a new public key script from the wallet which we'll
// sweep the funds to. // sweep the funds to.
// TODO(roasbeef): possibly create many outputs to minimize change in // TODO(roasbeef): possibly create many outputs to minimize change in
// the future? // the future?
pkScript, err := b.cfg.GenSweepScript() pkScript, err := b.cfg.GenSweepScript().Unpack()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1524,6 +1596,18 @@ func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit,
} }
txFee := feePerKw.FeeForWeight(txWeight) txFee := feePerKw.FeeForWeight(txWeight)
// At this point, we'll check to see if we have any extra outputs to
// add from the aux sweeper.
extraChangeOut := fn.MapOptionZ(
b.cfg.AuxSweeper,
func(aux sweep.AuxSweeper) fn.Result[sweep.SweepOutput] {
return aux.DeriveSweepAddr(inputs, pkScript)
},
)
if err := extraChangeOut.Err(); err != nil {
return nil, err
}
// TODO(roasbeef): already start to siphon their funds into fees // TODO(roasbeef): already start to siphon their funds into fees
sweepAmt := int64(totalAmt - txFee) sweepAmt := int64(totalAmt - txFee)
@@ -1531,12 +1615,24 @@ func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit,
// information gathered above and the provided retribution information. // information gathered above and the provided retribution information.
txn := wire.NewMsgTx(2) txn := wire.NewMsgTx(2)
// We begin by adding the output to which our funds will be deposited. // First, we'll add the extra sweep output if it exists, subtracting the
// amount from the sweep amt.
if b.cfg.AuxSweeper.IsSome() {
extraChangeOut.WhenResult(func(o sweep.SweepOutput) {
sweepAmt -= o.Value
txn.AddTxOut(&o.TxOut)
})
}
// Next, we'll add the output to which our funds will be deposited.
txn.AddTxOut(&wire.TxOut{ txn.AddTxOut(&wire.TxOut{
PkScript: pkScript, PkScript: pkScript.DeliveryAddress,
Value: sweepAmt, Value: sweepAmt,
}) })
// TODO(roasbeef): add other output change modify sweep amt
// Next, we add all of the spendable outputs as inputs to the // Next, we add all of the spendable outputs as inputs to the
// transaction. // transaction.
for _, inp := range inputs { for _, inp := range inputs {
@@ -1592,7 +1688,13 @@ func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit,
} }
} }
return txn, nil return &justiceTxCtx{
justiceTx: txn,
sweepAddr: pkScript,
extraTxOut: extraChangeOut.Option(),
fee: txFee,
inputs: inputs,
}, nil
} }
// RetributionStore handles persistence of retribution states to disk and is // RetributionStore handles persistence of retribution states to disk and is
@@ -1622,13 +1724,29 @@ func taprootBriefcaseFromRetInfo(retInfo *retributionInfo) *taprootBriefcase {
// commitment, we'll need to stash the control block. // commitment, we'll need to stash the control block.
case input.TaprootRemoteCommitSpend: case input.TaprootRemoteCommitSpend:
//nolint:lll //nolint:lll
tapCase.CtrlBlocks.CommitSweepCtrlBlock = bo.signDesc.ControlBlock tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock = bo.signDesc.ControlBlock
bo.resolutionBlob.WhenSome(func(blob tlv.Blob) {
tapCase.SettledCommitBlob = tlv.SomeRecordT(
tlv.NewPrimitiveRecord[tlv.TlvType2](
blob,
),
)
})
// To spend the revoked output again, we'll store the same // To spend the revoked output again, we'll store the same
// control block value as above, but in a different place. // control block value as above, but in a different place.
case input.TaprootCommitmentRevoke: case input.TaprootCommitmentRevoke:
//nolint:lll //nolint:lll
tapCase.CtrlBlocks.RevokeSweepCtrlBlock = bo.signDesc.ControlBlock tapCase.CtrlBlocks.Val.RevokeSweepCtrlBlock = bo.signDesc.ControlBlock
bo.resolutionBlob.WhenSome(func(blob tlv.Blob) {
tapCase.BreachedCommitBlob = tlv.SomeRecordT(
tlv.NewPrimitiveRecord[tlv.TlvType3](
blob,
),
)
})
// For spending the HTLC outputs, we'll store the first and // For spending the HTLC outputs, we'll store the first and
// second level tweak values. // second level tweak values.
@@ -1642,10 +1760,10 @@ func taprootBriefcaseFromRetInfo(retInfo *retributionInfo) *taprootBriefcase {
secondLevelTweak := bo.secondLevelTapTweak secondLevelTweak := bo.secondLevelTapTweak
//nolint:lll //nolint:lll
tapCase.TapTweaks.BreachedHtlcTweaks[resID] = firstLevelTweak tapCase.TapTweaks.Val.BreachedHtlcTweaks[resID] = firstLevelTweak
//nolint:lll //nolint:lll
tapCase.TapTweaks.BreachedSecondLevelHltcTweaks[resID] = secondLevelTweak tapCase.TapTweaks.Val.BreachedSecondLevelHltcTweaks[resID] = secondLevelTweak
} }
} }
@@ -1665,13 +1783,25 @@ func applyTaprootRetInfo(tapCase *taprootBriefcase,
// commitment, we'll apply the control block. // commitment, we'll apply the control block.
case input.TaprootRemoteCommitSpend: case input.TaprootRemoteCommitSpend:
//nolint:lll //nolint:lll
bo.signDesc.ControlBlock = tapCase.CtrlBlocks.CommitSweepCtrlBlock bo.signDesc.ControlBlock = tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock
tapCase.SettledCommitBlob.WhenSomeV(
func(blob tlv.Blob) {
bo.resolutionBlob = fn.Some(blob)
},
)
// To spend the revoked output again, we'll apply the same // To spend the revoked output again, we'll apply the same
// control block value as above, but to a different place. // control block value as above, but to a different place.
case input.TaprootCommitmentRevoke: case input.TaprootCommitmentRevoke:
//nolint:lll //nolint:lll
bo.signDesc.ControlBlock = tapCase.CtrlBlocks.RevokeSweepCtrlBlock bo.signDesc.ControlBlock = tapCase.CtrlBlocks.Val.RevokeSweepCtrlBlock
tapCase.BreachedCommitBlob.WhenSomeV(
func(blob tlv.Blob) {
bo.resolutionBlob = fn.Some(blob)
},
)
// For spending the HTLC outputs, we'll apply the first and // For spending the HTLC outputs, we'll apply the first and
// second level tweak values. // second level tweak values.
@@ -1680,7 +1810,8 @@ func applyTaprootRetInfo(tapCase *taprootBriefcase,
case input.TaprootHtlcOfferedRevoke: case input.TaprootHtlcOfferedRevoke:
resID := newResolverID(bo.OutPoint()) resID := newResolverID(bo.OutPoint())
tap1, ok := tapCase.TapTweaks.BreachedHtlcTweaks[resID] //nolint:lll
tap1, ok := tapCase.TapTweaks.Val.BreachedHtlcTweaks[resID]
if !ok { if !ok {
return fmt.Errorf("unable to find taproot "+ return fmt.Errorf("unable to find taproot "+
"tweak for: %v", bo.OutPoint()) "tweak for: %v", bo.OutPoint())
@@ -1688,7 +1819,7 @@ func applyTaprootRetInfo(tapCase *taprootBriefcase,
bo.signDesc.TapTweak = tap1[:] bo.signDesc.TapTweak = tap1[:]
//nolint:lll //nolint:lll
tap2, ok := tapCase.TapTweaks.BreachedSecondLevelHltcTweaks[resID] tap2, ok := tapCase.TapTweaks.Val.BreachedSecondLevelHltcTweaks[resID]
if !ok { if !ok {
return fmt.Errorf("unable to find taproot "+ return fmt.Errorf("unable to find taproot "+
"tweak for: %v", bo.OutPoint()) "tweak for: %v", bo.OutPoint())
+25 -12
View File
@@ -1199,6 +1199,8 @@ func TestBreachCreateJusticeTx(t *testing.T) {
input.HtlcSecondLevelRevoke, input.HtlcSecondLevelRevoke,
} }
rBlob := fn.Some([]byte{0x01})
breachedOutputs := make([]breachedOutput, len(outputTypes)) breachedOutputs := make([]breachedOutput, len(outputTypes))
for i, wt := range outputTypes { for i, wt := range outputTypes {
// Create a fake breached output for each type, ensuring they // Create a fake breached output for each type, ensuring they
@@ -1217,6 +1219,7 @@ func TestBreachCreateJusticeTx(t *testing.T) {
nil, nil,
signDesc, signDesc,
1, 1,
rBlob,
) )
} }
@@ -1227,16 +1230,16 @@ func TestBreachCreateJusticeTx(t *testing.T) {
// The spendAll tx should be spending all the outputs. This is the // The spendAll tx should be spending all the outputs. This is the
// "regular" justice transaction type. // "regular" justice transaction type.
require.Len(t, justiceTxs.spendAll.TxIn, len(breachedOutputs)) require.Len(t, justiceTxs.spendAll.justiceTx.TxIn, len(breachedOutputs))
// The spendCommitOuts tx should be spending the 4 types of commit outs // The spendCommitOuts tx should be spending the 4 types of commit outs
// (note that in practice there will be at most two commit outputs per // (note that in practice there will be at most two commit outputs per
// commit, but we test all 4 types here). // commit, but we test all 4 types here).
require.Len(t, justiceTxs.spendCommitOuts.TxIn, 4) require.Len(t, justiceTxs.spendCommitOuts.justiceTx.TxIn, 4)
// Check that the spendHTLCs tx is spending the two revoked commitment // Check that the spendHTLCs tx is spending the two revoked commitment
// level HTLC output types. // level HTLC output types.
require.Len(t, justiceTxs.spendHTLCs.TxIn, 2) require.Len(t, justiceTxs.spendHTLCs.justiceTx.TxIn, 2)
// Finally, check that the spendSecondLevelHTLCs txs are spending the // Finally, check that the spendSecondLevelHTLCs txs are spending the
// second level type. // second level type.
@@ -1592,6 +1595,9 @@ func testBreachSpends(t *testing.T, test breachTest) {
retribution, err := lnwallet.NewBreachRetribution( retribution, err := lnwallet.NewBreachRetribution(
alice.State(), height, 1, forceCloseTx, alice.State(), height, 1, forceCloseTx,
fn.Some[lnwallet.AuxLeafStore](&lnwallet.MockAuxLeafStore{}), fn.Some[lnwallet.AuxLeafStore](&lnwallet.MockAuxLeafStore{}),
fn.Some[lnwallet.AuxContractResolver](
&lnwallet.MockAuxContractResolver{},
),
) )
require.NoError(t, err, "unable to create breach retribution") require.NoError(t, err, "unable to create breach retribution")
@@ -1802,6 +1808,9 @@ func TestBreachDelayedJusticeConfirmation(t *testing.T) {
retribution, err := lnwallet.NewBreachRetribution( retribution, err := lnwallet.NewBreachRetribution(
alice.State(), height, uint32(blockHeight), forceCloseTx, alice.State(), height, uint32(blockHeight), forceCloseTx,
fn.Some[lnwallet.AuxLeafStore](&lnwallet.MockAuxLeafStore{}), fn.Some[lnwallet.AuxLeafStore](&lnwallet.MockAuxLeafStore{}),
fn.Some[lnwallet.AuxContractResolver](
&lnwallet.MockAuxContractResolver{},
),
) )
require.NoError(t, err, "unable to create breach retribution") require.NoError(t, err, "unable to create breach retribution")
@@ -2129,15 +2138,19 @@ func createTestArbiter(t *testing.T, contractBreaches chan *ContractBreachEvent,
// Assemble our test arbiter. // Assemble our test arbiter.
notifier := mock.MakeMockSpendNotifier() notifier := mock.MakeMockSpendNotifier()
ba := NewBreachArbitrator(&BreachConfig{ ba := NewBreachArbitrator(&BreachConfig{
CloseLink: func(_ *wire.OutPoint, _ ChannelCloseType) {}, CloseLink: func(_ *wire.OutPoint, _ ChannelCloseType) {},
DB: db.ChannelStateDB(), DB: db.ChannelStateDB(),
Estimator: chainfee.NewStaticEstimator(12500, 0), Estimator: chainfee.NewStaticEstimator(12500, 0),
GenSweepScript: func() ([]byte, error) { return nil, nil }, GenSweepScript: func() fn.Result[lnwallet.AddrWithKey] {
ContractBreaches: contractBreaches, return fn.Ok(lnwallet.AddrWithKey{})
Signer: signer, },
Notifier: notifier, ContractBreaches: contractBreaches,
PublishTransaction: func(_ *wire.MsgTx, _ string) error { return nil }, Signer: signer,
Store: store, Notifier: notifier,
PublishTransaction: func(_ *wire.MsgTx, _ string) error {
return nil
},
Store: store,
}) })
if err := ba.Start(); err != nil { if err := ba.Start(); err != nil {
+28 -16
View File
@@ -10,9 +10,11 @@ import (
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/tlv"
) )
// ContractResolutions is a wrapper struct around the two forms of resolutions // ContractResolutions is a wrapper struct around the two forms of resolutions
@@ -1553,7 +1555,13 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
commitResolution := c.CommitResolution commitResolution := c.CommitResolution
commitSignDesc := commitResolution.SelfOutputSignDesc commitSignDesc := commitResolution.SelfOutputSignDesc
//nolint:lll //nolint:lll
tapCase.CtrlBlocks.CommitSweepCtrlBlock = commitSignDesc.ControlBlock tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock = commitSignDesc.ControlBlock
c.CommitResolution.ResolutionBlob.WhenSome(func(b []byte) {
tapCase.SettledCommitBlob = tlv.SomeRecordT(
tlv.NewPrimitiveRecord[tlv.TlvType2](b),
)
})
} }
for _, htlc := range c.HtlcResolutions.IncomingHTLCs { for _, htlc := range c.HtlcResolutions.IncomingHTLCs {
@@ -1571,7 +1579,7 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
htlc.SignedSuccessTx.TxIn[0].PreviousOutPoint, htlc.SignedSuccessTx.TxIn[0].PreviousOutPoint,
) )
//nolint:lll //nolint:lll
tapCase.CtrlBlocks.SecondLevelCtrlBlocks[resID] = ctrlBlock tapCase.CtrlBlocks.Val.SecondLevelCtrlBlocks[resID] = ctrlBlock
// For HTLCs we need to go to the second level for, we // For HTLCs we need to go to the second level for, we
// also need to store the control block needed to // also need to store the control block needed to
@@ -1580,12 +1588,12 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
//nolint:lll //nolint:lll
bridgeCtrlBlock := htlc.SignDetails.SignDesc.ControlBlock bridgeCtrlBlock := htlc.SignDetails.SignDesc.ControlBlock
//nolint:lll //nolint:lll
tapCase.CtrlBlocks.IncomingHtlcCtrlBlocks[resID] = bridgeCtrlBlock tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = bridgeCtrlBlock
} }
} else { } else {
resID := newResolverID(htlc.ClaimOutpoint) resID := newResolverID(htlc.ClaimOutpoint)
//nolint:lll //nolint:lll
tapCase.CtrlBlocks.IncomingHtlcCtrlBlocks[resID] = ctrlBlock tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = ctrlBlock
} }
} }
for _, htlc := range c.HtlcResolutions.OutgoingHTLCs { for _, htlc := range c.HtlcResolutions.OutgoingHTLCs {
@@ -1603,7 +1611,7 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
htlc.SignedTimeoutTx.TxIn[0].PreviousOutPoint, htlc.SignedTimeoutTx.TxIn[0].PreviousOutPoint,
) )
//nolint:lll //nolint:lll
tapCase.CtrlBlocks.SecondLevelCtrlBlocks[resID] = ctrlBlock tapCase.CtrlBlocks.Val.SecondLevelCtrlBlocks[resID] = ctrlBlock
// For HTLCs we need to go to the second level for, we // For HTLCs we need to go to the second level for, we
// also need to store the control block needed to // also need to store the control block needed to
@@ -1614,18 +1622,18 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error {
//nolint:lll //nolint:lll
bridgeCtrlBlock := htlc.SignDetails.SignDesc.ControlBlock bridgeCtrlBlock := htlc.SignDetails.SignDesc.ControlBlock
//nolint:lll //nolint:lll
tapCase.CtrlBlocks.OutgoingHtlcCtrlBlocks[resID] = bridgeCtrlBlock tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = bridgeCtrlBlock
} }
} else { } else {
resID := newResolverID(htlc.ClaimOutpoint) resID := newResolverID(htlc.ClaimOutpoint)
//nolint:lll //nolint:lll
tapCase.CtrlBlocks.OutgoingHtlcCtrlBlocks[resID] = ctrlBlock tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = ctrlBlock
} }
} }
if c.AnchorResolution != nil { if c.AnchorResolution != nil {
anchorSignDesc := c.AnchorResolution.AnchorSignDescriptor anchorSignDesc := c.AnchorResolution.AnchorSignDescriptor
tapCase.TapTweaks.AnchorTweak = anchorSignDesc.TapTweak tapCase.TapTweaks.Val.AnchorTweak = anchorSignDesc.TapTweak
} }
return tapCase.Encode(w) return tapCase.Encode(w)
@@ -1639,7 +1647,11 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error {
if c.CommitResolution != nil { if c.CommitResolution != nil {
c.CommitResolution.SelfOutputSignDesc.ControlBlock = c.CommitResolution.SelfOutputSignDesc.ControlBlock =
tapCase.CtrlBlocks.CommitSweepCtrlBlock tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock
tapCase.SettledCommitBlob.WhenSomeV(func(b []byte) {
c.CommitResolution.ResolutionBlob = fn.Some(b)
})
} }
for i := range c.HtlcResolutions.IncomingHTLCs { for i := range c.HtlcResolutions.IncomingHTLCs {
@@ -1652,19 +1664,19 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error {
) )
//nolint:lll //nolint:lll
ctrlBlock := tapCase.CtrlBlocks.SecondLevelCtrlBlocks[resID] ctrlBlock := tapCase.CtrlBlocks.Val.SecondLevelCtrlBlocks[resID]
htlc.SweepSignDesc.ControlBlock = ctrlBlock htlc.SweepSignDesc.ControlBlock = ctrlBlock
//nolint:lll //nolint:lll
if htlc.SignDetails != nil { if htlc.SignDetails != nil {
bridgeCtrlBlock := tapCase.CtrlBlocks.IncomingHtlcCtrlBlocks[resID] bridgeCtrlBlock := tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID]
htlc.SignDetails.SignDesc.ControlBlock = bridgeCtrlBlock htlc.SignDetails.SignDesc.ControlBlock = bridgeCtrlBlock
} }
} else { } else {
resID = newResolverID(htlc.ClaimOutpoint) resID = newResolverID(htlc.ClaimOutpoint)
//nolint:lll //nolint:lll
ctrlBlock := tapCase.CtrlBlocks.IncomingHtlcCtrlBlocks[resID] ctrlBlock := tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID]
htlc.SweepSignDesc.ControlBlock = ctrlBlock htlc.SweepSignDesc.ControlBlock = ctrlBlock
} }
@@ -1680,19 +1692,19 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error {
) )
//nolint:lll //nolint:lll
ctrlBlock := tapCase.CtrlBlocks.SecondLevelCtrlBlocks[resID] ctrlBlock := tapCase.CtrlBlocks.Val.SecondLevelCtrlBlocks[resID]
htlc.SweepSignDesc.ControlBlock = ctrlBlock htlc.SweepSignDesc.ControlBlock = ctrlBlock
//nolint:lll //nolint:lll
if htlc.SignDetails != nil { if htlc.SignDetails != nil {
bridgeCtrlBlock := tapCase.CtrlBlocks.OutgoingHtlcCtrlBlocks[resID] bridgeCtrlBlock := tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID]
htlc.SignDetails.SignDesc.ControlBlock = bridgeCtrlBlock htlc.SignDetails.SignDesc.ControlBlock = bridgeCtrlBlock
} }
} else { } else {
resID = newResolverID(htlc.ClaimOutpoint) resID = newResolverID(htlc.ClaimOutpoint)
//nolint:lll //nolint:lll
ctrlBlock := tapCase.CtrlBlocks.OutgoingHtlcCtrlBlocks[resID] ctrlBlock := tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID]
htlc.SweepSignDesc.ControlBlock = ctrlBlock htlc.SweepSignDesc.ControlBlock = ctrlBlock
} }
@@ -1701,7 +1713,7 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error {
if c.AnchorResolution != nil { if c.AnchorResolution != nil {
c.AnchorResolution.AnchorSignDescriptor.TapTweak = c.AnchorResolution.AnchorSignDescriptor.TapTweak =
tapCase.TapTweaks.AnchorTweak tapCase.TapTweaks.Val.AnchorTweak
} }
return nil return nil
+14
View File
@@ -225,6 +225,10 @@ type ChainArbitratorConfig struct {
// AuxSigner is an optional signer that can be used to sign auxiliary // AuxSigner is an optional signer that can be used to sign auxiliary
// leaves for certain custom channel types. // leaves for certain custom channel types.
AuxSigner fn.Option[lnwallet.AuxSigner] AuxSigner fn.Option[lnwallet.AuxSigner]
// AuxResolver is an optional interface that can be used to modify the
// way contracts are resolved.
AuxResolver fn.Option[lnwallet.AuxContractResolver]
} }
// ChainArbitrator is a sub-system that oversees the on-chain resolution of all // ChainArbitrator is a sub-system that oversees the on-chain resolution of all
@@ -314,6 +318,9 @@ func (a *arbChannel) NewAnchorResolutions() (*lnwallet.AnchorResolutions,
a.c.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { a.c.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) {
chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s))
}) })
a.c.cfg.AuxResolver.WhenSome(func(s lnwallet.AuxContractResolver) {
chanOpts = append(chanOpts, lnwallet.WithAuxResolver(s))
})
chanMachine, err := lnwallet.NewLightningChannel( chanMachine, err := lnwallet.NewLightningChannel(
a.c.cfg.Signer, channel, nil, chanOpts..., a.c.cfg.Signer, channel, nil, chanOpts...,
@@ -367,6 +374,9 @@ func (a *arbChannel) ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error)
a.c.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { a.c.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) {
chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s))
}) })
a.c.cfg.AuxResolver.WhenSome(func(s lnwallet.AuxContractResolver) {
chanOpts = append(chanOpts, lnwallet.WithAuxResolver(s))
})
// Finally, we'll force close the channel completing // Finally, we'll force close the channel completing
// the force close workflow. // the force close workflow.
@@ -581,6 +591,8 @@ func (c *ChainArbitrator) Start() error {
isOurAddr: c.cfg.IsOurAddress, isOurAddr: c.cfg.IsOurAddress,
contractBreach: breachClosure, contractBreach: breachClosure,
extractStateNumHint: lnwallet.GetStateNumHint, extractStateNumHint: lnwallet.GetStateNumHint,
auxLeafStore: c.cfg.AuxLeafStore,
auxResolver: c.cfg.AuxResolver,
}, },
) )
if err != nil { if err != nil {
@@ -1210,6 +1222,8 @@ func (c *ChainArbitrator) WatchNewChannel(newChan *channeldb.OpenChannel) error
) )
}, },
extractStateNumHint: lnwallet.GetStateNumHint, extractStateNumHint: lnwallet.GetStateNumHint,
auxLeafStore: c.cfg.AuxLeafStore,
auxResolver: c.cfg.AuxResolver,
}, },
) )
if err != nil { if err != nil {
+7 -4
View File
@@ -196,6 +196,9 @@ type chainWatcherConfig struct {
// auxLeafStore can be used to fetch information for custom channels. // auxLeafStore can be used to fetch information for custom channels.
auxLeafStore fn.Option[lnwallet.AuxLeafStore] auxLeafStore fn.Option[lnwallet.AuxLeafStore]
// auxResolver is used to supplement contract resolution.
auxResolver fn.Option[lnwallet.AuxContractResolver]
} }
// chainWatcher is a system that's assigned to every active channel. The duty // chainWatcher is a system that's assigned to every active channel. The duty
@@ -896,7 +899,7 @@ func (c *chainWatcher) handlePossibleBreach(commitSpend *chainntnfs.SpendDetail,
spendHeight := uint32(commitSpend.SpendingHeight) spendHeight := uint32(commitSpend.SpendingHeight)
retribution, err := lnwallet.NewBreachRetribution( retribution, err := lnwallet.NewBreachRetribution(
c.cfg.chanState, broadcastStateNum, spendHeight, c.cfg.chanState, broadcastStateNum, spendHeight,
commitSpend.SpendingTx, c.cfg.auxLeafStore, commitSpend.SpendingTx, c.cfg.auxLeafStore, c.cfg.auxResolver,
) )
switch { switch {
@@ -1147,7 +1150,7 @@ func (c *chainWatcher) dispatchLocalForceClose(
forceClose, err := lnwallet.NewLocalForceCloseSummary( forceClose, err := lnwallet.NewLocalForceCloseSummary(
c.cfg.chanState, c.cfg.signer, commitSpend.SpendingTx, stateNum, c.cfg.chanState, c.cfg.signer, commitSpend.SpendingTx, stateNum,
c.cfg.auxLeafStore, c.cfg.auxLeafStore, c.cfg.auxResolver,
) )
if err != nil { if err != nil {
return err return err
@@ -1239,8 +1242,8 @@ func (c *chainWatcher) dispatchRemoteForceClose(
// materials required to let each subscriber sweep the funds in the // materials required to let each subscriber sweep the funds in the
// channel on-chain. // channel on-chain.
uniClose, err := lnwallet.NewUnilateralCloseSummary( uniClose, err := lnwallet.NewUnilateralCloseSummary(
c.cfg.chanState, c.cfg.signer, commitSpend, c.cfg.chanState, c.cfg.signer, commitSpend, remoteCommit,
remoteCommit, commitPoint, c.cfg.auxLeafStore, commitPoint, c.cfg.auxLeafStore, c.cfg.auxResolver,
) )
if err != nil { if err != nil {
return err return err
+6
View File
@@ -345,12 +345,18 @@ func (c *commitSweepResolver) Resolve(_ bool) (ContractResolver, error) {
&c.commitResolution.SelfOutputSignDesc, &c.commitResolution.SelfOutputSignDesc,
c.broadcastHeight, c.commitResolution.MaturityDelay, c.broadcastHeight, c.commitResolution.MaturityDelay,
c.leaseExpiry, c.leaseExpiry,
input.WithResolutionBlob(
c.commitResolution.ResolutionBlob,
),
) )
} else { } else {
inp = input.NewCsvInput( inp = input.NewCsvInput(
&c.commitResolution.SelfOutPoint, witnessType, &c.commitResolution.SelfOutPoint, witnessType,
&c.commitResolution.SelfOutputSignDesc, &c.commitResolution.SelfOutputSignDesc,
c.broadcastHeight, c.commitResolution.MaturityDelay, c.broadcastHeight, c.commitResolution.MaturityDelay,
input.WithResolutionBlob(
c.commitResolution.ResolutionBlob,
),
) )
} }
@@ -99,6 +99,17 @@ func (h *htlcIncomingContestResolver) Resolve(
return nil, nil return nil, nil
} }
// If the HTLC has custom records, then for now we'll pause resolution.
//
// TODO(roasbeef): Implement resolving HTLCs with custom records
// (follow-up PR).
if len(h.htlc.CustomRecords) != 0 {
select { //nolint:gosimple
case <-h.quit:
return nil, errResolverShuttingDown
}
}
// First try to parse the payload. If that fails, we can stop resolution // First try to parse the payload. If that fails, we can stop resolution
// now. // now.
payload, nextHopOnionBlob, err := h.decodePayload() payload, nextHopOnionBlob, err := h.decodePayload()
@@ -58,6 +58,17 @@ func (h *htlcOutgoingContestResolver) Resolve(
return nil, nil return nil, nil
} }
// If the HTLC has custom records, then for now we'll pause resolution.
//
// TODO(roasbeef): Implement resolving HTLCs with custom records
// (follow-up PR).
if len(h.htlc.CustomRecords) != 0 {
select { //nolint:gosimple
case <-h.quit:
return nil, errResolverShuttingDown
}
}
// Otherwise, we'll watch for two external signals to decide if we'll // Otherwise, we'll watch for two external signals to decide if we'll
// morph into another resolver, or fully resolve the contract. // morph into another resolver, or fully resolve the contract.
// //
+11
View File
@@ -123,6 +123,17 @@ func (h *htlcSuccessResolver) Resolve(
return nil, nil return nil, nil
} }
// If the HTLC has custom records, then for now we'll pause resolution.
//
// TODO(roasbeef): Implement resolving HTLCs with custom records
// (follow-up PR).
if len(h.htlc.CustomRecords) != 0 {
select { //nolint:gosimple
case <-h.quit:
return nil, errResolverShuttingDown
}
}
// If we don't have a success transaction, then this means that this is // If we don't have a success transaction, then this means that this is
// an output on the remote party's commitment transaction. // an output on the remote party's commitment transaction.
if h.htlcResolution.SignedSuccessTx == nil { if h.htlcResolution.SignedSuccessTx == nil {
+11
View File
@@ -426,6 +426,17 @@ func (h *htlcTimeoutResolver) Resolve(
return nil, nil return nil, nil
} }
// If the HTLC has custom records, then for now we'll pause resolution.
//
// TODO(roasbeef): Implement resolving HTLCs with custom records
// (follow-up PR).
if len(h.htlc.CustomRecords) != 0 {
select { //nolint:gosimple
case <-h.quit:
return nil, errResolverShuttingDown
}
}
// Start by spending the HTLC output, either by broadcasting the // Start by spending the HTLC output, either by broadcasting the
// second-level timeout transaction, or directly if this is the remote // second-level timeout transaction, or directly if this is the remote
// commitment. // commitment.
+96 -69
View File
@@ -8,9 +8,6 @@ import (
) )
const ( const (
taprootCtrlBlockType tlv.Type = 0
taprootTapTweakType tlv.Type = 1
commitCtrlBlockType tlv.Type = 0 commitCtrlBlockType tlv.Type = 0
revokeCtrlBlockType tlv.Type = 1 revokeCtrlBlockType tlv.Type = 1
outgoingHtlcCtrlBlockType tlv.Type = 2 outgoingHtlcCtrlBlockType tlv.Type = 2
@@ -26,36 +23,62 @@ const (
// information we need to sweep taproot outputs. // information we need to sweep taproot outputs.
type taprootBriefcase struct { type taprootBriefcase struct {
// CtrlBlock is the set of control block for the taproot outputs. // CtrlBlock is the set of control block for the taproot outputs.
CtrlBlocks *ctrlBlocks CtrlBlocks tlv.RecordT[tlv.TlvType0, ctrlBlocks]
// TapTweaks is the set of taproot tweaks for the taproot outputs that // TapTweaks is the set of taproot tweaks for the taproot outputs that
// are to be spent via a keyspend path. This includes anchors, and any // are to be spent via a keyspend path. This includes anchors, and any
// revocation paths. // revocation paths.
TapTweaks *tapTweaks TapTweaks tlv.RecordT[tlv.TlvType1, tapTweaks]
// SettledCommitBlob is an optional record that contains an opaque blob
// that may be used to properly sweep commitment outputs on a force
// close transaction.
SettledCommitBlob tlv.OptionalRecordT[tlv.TlvType2, tlv.Blob]
// BreachCommitBlob is an optional record that contains an opaque blob
// used to sweep a remote party's breached output.
BreachedCommitBlob tlv.OptionalRecordT[tlv.TlvType3, tlv.Blob]
// TODO(roasbeef): htlc blobs
} }
// TODO(roasbeef): morph into new tlv record
// newTaprootBriefcase returns a new instance of the taproot specific briefcase // newTaprootBriefcase returns a new instance of the taproot specific briefcase
// variant. // variant.
func newTaprootBriefcase() *taprootBriefcase { func newTaprootBriefcase() *taprootBriefcase {
return &taprootBriefcase{ return &taprootBriefcase{
CtrlBlocks: newCtrlBlocks(), CtrlBlocks: tlv.NewRecordT[tlv.TlvType0](newCtrlBlocks()),
TapTweaks: newTapTweaks(), TapTweaks: tlv.NewRecordT[tlv.TlvType1](newTapTweaks()),
} }
} }
// EncodeRecords returns a slice of TLV records that should be encoded. // EncodeRecords returns a slice of TLV records that should be encoded.
func (t *taprootBriefcase) EncodeRecords() []tlv.Record { func (t *taprootBriefcase) EncodeRecords() []tlv.Record {
return []tlv.Record{ records := []tlv.Record{
newCtrlBlocksRecord(&t.CtrlBlocks), t.CtrlBlocks.Record(),
newTapTweaksRecord(&t.TapTweaks), t.TapTweaks.Record(),
} }
t.SettledCommitBlob.WhenSome(
func(r tlv.RecordT[tlv.TlvType2, tlv.Blob]) {
records = append(records, r.Record())
},
)
t.BreachedCommitBlob.WhenSome(
func(r tlv.RecordT[tlv.TlvType3, tlv.Blob]) {
records = append(records, r.Record())
},
)
return records
} }
// DecodeRecords returns a slice of TLV records that should be decoded. // DecodeRecords returns a slice of TLV records that should be decoded.
func (t *taprootBriefcase) DecodeRecords() []tlv.Record { func (t *taprootBriefcase) DecodeRecords() []tlv.Record {
return []tlv.Record{ return []tlv.Record{
newCtrlBlocksRecord(&t.CtrlBlocks), t.CtrlBlocks.Record(),
newTapTweaksRecord(&t.TapTweaks), t.TapTweaks.Record(),
} }
} }
@@ -71,12 +94,31 @@ func (t *taprootBriefcase) Encode(w io.Writer) error {
// Decode decodes the given reader into the target struct. // Decode decodes the given reader into the target struct.
func (t *taprootBriefcase) Decode(r io.Reader) error { func (t *taprootBriefcase) Decode(r io.Reader) error {
stream, err := tlv.NewStream(t.DecodeRecords()...) settledCommitBlob := t.SettledCommitBlob.Zero()
breachedCommitBlob := t.BreachedCommitBlob.Zero()
records := append(
t.DecodeRecords(),
settledCommitBlob.Record(),
breachedCommitBlob.Record(),
)
stream, err := tlv.NewStream(records...)
if err != nil { if err != nil {
return err return err
} }
return stream.Decode(r) typeMap, err := stream.DecodeWithParsedTypes(r)
if err != nil {
return err
}
if val, ok := typeMap[t.SettledCommitBlob.TlvType()]; ok && val == nil {
t.SettledCommitBlob = tlv.SomeRecordT(settledCommitBlob)
}
if v, ok := typeMap[t.BreachedCommitBlob.TlvType()]; ok && v == nil {
t.BreachedCommitBlob = tlv.SomeRecordT(breachedCommitBlob)
}
return nil
} }
// resolverCtrlBlocks is a map of resolver IDs to their corresponding control // resolverCtrlBlocks is a map of resolver IDs to their corresponding control
@@ -216,8 +258,8 @@ type ctrlBlocks struct {
} }
// newCtrlBlocks returns a new instance of the ctrlBlocks struct. // newCtrlBlocks returns a new instance of the ctrlBlocks struct.
func newCtrlBlocks() *ctrlBlocks { func newCtrlBlocks() ctrlBlocks {
return &ctrlBlocks{ return ctrlBlocks{
OutgoingHtlcCtrlBlocks: newResolverCtrlBlocks(), OutgoingHtlcCtrlBlocks: newResolverCtrlBlocks(),
IncomingHtlcCtrlBlocks: newResolverCtrlBlocks(), IncomingHtlcCtrlBlocks: newResolverCtrlBlocks(),
SecondLevelCtrlBlocks: newResolverCtrlBlocks(), SecondLevelCtrlBlocks: newResolverCtrlBlocks(),
@@ -260,7 +302,7 @@ func varBytesDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error {
// ctrlBlockEncoder is a custom TLV encoder for the ctrlBlocks struct. // ctrlBlockEncoder is a custom TLV encoder for the ctrlBlocks struct.
func ctrlBlockEncoder(w io.Writer, val any, _ *[8]byte) error { func ctrlBlockEncoder(w io.Writer, val any, _ *[8]byte) error {
if t, ok := val.(**ctrlBlocks); ok { if t, ok := val.(*ctrlBlocks); ok {
return (*t).Encode(w) return (*t).Encode(w)
} }
@@ -269,7 +311,7 @@ func ctrlBlockEncoder(w io.Writer, val any, _ *[8]byte) error {
// ctrlBlockDecoder is a custom TLV decoder for the ctrlBlocks struct. // ctrlBlockDecoder is a custom TLV decoder for the ctrlBlocks struct.
func ctrlBlockDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error { func ctrlBlockDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error {
if typ, ok := val.(**ctrlBlocks); ok { if typ, ok := val.(*ctrlBlocks); ok {
ctrlReader := io.LimitReader(r, int64(l)) ctrlReader := io.LimitReader(r, int64(l))
var ctrlBlocks ctrlBlocks var ctrlBlocks ctrlBlocks
@@ -278,7 +320,7 @@ func ctrlBlockDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error {
return err return err
} }
*typ = &ctrlBlocks *typ = ctrlBlocks
return nil return nil
} }
@@ -286,28 +328,6 @@ func ctrlBlockDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error {
return tlv.NewTypeForDecodingErr(val, "ctrlBlocks", l, l) return tlv.NewTypeForDecodingErr(val, "ctrlBlocks", l, l)
} }
// newCtrlBlocksRecord returns a new TLV record that can be used to
// encode/decode the set of cotrol blocks for the taproot outputs for a
// channel.
func newCtrlBlocksRecord(blks **ctrlBlocks) tlv.Record {
recordSize := func() uint64 {
var (
b bytes.Buffer
buf [8]byte
)
if err := ctrlBlockEncoder(&b, blks, &buf); err != nil {
panic(err)
}
return uint64(len(b.Bytes()))
}
return tlv.MakeDynamicRecord(
taprootCtrlBlockType, blks, recordSize, ctrlBlockEncoder,
ctrlBlockDecoder,
)
}
// EncodeRecords returns the set of TLV records that encode the control block // EncodeRecords returns the set of TLV records that encode the control block
// for the commitment transaction. // for the commitment transaction.
func (c *ctrlBlocks) EncodeRecords() []tlv.Record { func (c *ctrlBlocks) EncodeRecords() []tlv.Record {
@@ -382,7 +402,21 @@ func (c *ctrlBlocks) DecodeRecords() []tlv.Record {
// Record returns a TLV record that can be used to encode/decode the control // Record returns a TLV record that can be used to encode/decode the control
// blocks. type from a given TLV stream. // blocks. type from a given TLV stream.
func (c *ctrlBlocks) Record() tlv.Record { func (c *ctrlBlocks) Record() tlv.Record {
return tlv.MakePrimitiveRecord(commitCtrlBlockType, c) recordSize := func() uint64 {
var (
b bytes.Buffer
buf [8]byte
)
if err := ctrlBlockEncoder(&b, c, &buf); err != nil {
panic(err)
}
return uint64(len(b.Bytes()))
}
return tlv.MakeDynamicRecord(
0, c, recordSize, ctrlBlockEncoder, ctrlBlockDecoder,
)
} }
// Encode encodes the set of control blocks. // Encode encodes the set of control blocks.
@@ -530,8 +564,8 @@ type tapTweaks struct {
} }
// newTapTweaks returns a new tapTweaks struct. // newTapTweaks returns a new tapTweaks struct.
func newTapTweaks() *tapTweaks { func newTapTweaks() tapTweaks {
return &tapTweaks{ return tapTweaks{
BreachedHtlcTweaks: make(htlcTapTweaks), BreachedHtlcTweaks: make(htlcTapTweaks),
BreachedSecondLevelHltcTweaks: make(htlcTapTweaks), BreachedSecondLevelHltcTweaks: make(htlcTapTweaks),
} }
@@ -539,7 +573,7 @@ func newTapTweaks() *tapTweaks {
// tapTweaksEncoder is a custom TLV encoder for the tapTweaks struct. // tapTweaksEncoder is a custom TLV encoder for the tapTweaks struct.
func tapTweaksEncoder(w io.Writer, val any, _ *[8]byte) error { func tapTweaksEncoder(w io.Writer, val any, _ *[8]byte) error {
if t, ok := val.(**tapTweaks); ok { if t, ok := val.(*tapTweaks); ok {
return (*t).Encode(w) return (*t).Encode(w)
} }
@@ -548,7 +582,7 @@ func tapTweaksEncoder(w io.Writer, val any, _ *[8]byte) error {
// tapTweaksDecoder is a custom TLV decoder for the tapTweaks struct. // tapTweaksDecoder is a custom TLV decoder for the tapTweaks struct.
func tapTweaksDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error { func tapTweaksDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error {
if typ, ok := val.(**tapTweaks); ok { if typ, ok := val.(*tapTweaks); ok {
tweakReader := io.LimitReader(r, int64(l)) tweakReader := io.LimitReader(r, int64(l))
var tapTweaks tapTweaks var tapTweaks tapTweaks
@@ -557,7 +591,7 @@ func tapTweaksDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error {
return err return err
} }
*typ = &tapTweaks *typ = tapTweaks
return nil return nil
} }
@@ -565,27 +599,6 @@ func tapTweaksDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error {
return tlv.NewTypeForDecodingErr(val, "tapTweaks", l, l) return tlv.NewTypeForDecodingErr(val, "tapTweaks", l, l)
} }
// newTapTweaksRecord returns a new TLV record that can be used to
// encode/decode the tap tweak structs.
func newTapTweaksRecord(tweaks **tapTweaks) tlv.Record {
recordSize := func() uint64 {
var (
b bytes.Buffer
buf [8]byte
)
if err := tapTweaksEncoder(&b, tweaks, &buf); err != nil {
panic(err)
}
return uint64(len(b.Bytes()))
}
return tlv.MakeDynamicRecord(
taprootTapTweakType, tweaks, recordSize, tapTweaksEncoder,
tapTweaksDecoder,
)
}
// EncodeRecords returns the set of TLV records that encode the tweaks. // EncodeRecords returns the set of TLV records that encode the tweaks.
func (t *tapTweaks) EncodeRecords() []tlv.Record { func (t *tapTweaks) EncodeRecords() []tlv.Record {
var records []tlv.Record var records []tlv.Record
@@ -637,7 +650,21 @@ func (t *tapTweaks) DecodeRecords() []tlv.Record {
// Record returns a TLV record that can be used to encode/decode the tap // Record returns a TLV record that can be used to encode/decode the tap
// tweaks. // tweaks.
func (t *tapTweaks) Record() tlv.Record { func (t *tapTweaks) Record() tlv.Record {
return tlv.MakePrimitiveRecord(taprootTapTweakType, t) recordSize := func() uint64 {
var (
b bytes.Buffer
buf [8]byte
)
if err := tapTweaksEncoder(&b, t, &buf); err != nil {
panic(err)
}
return uint64(len(b.Bytes()))
}
return tlv.MakeDynamicRecord(
0, t, recordSize, tapTweaksEncoder, tapTweaksDecoder,
)
} }
// Encode encodes the set of tap tweaks. // Encode encodes the set of tap tweaks.
+15 -4
View File
@@ -5,6 +5,7 @@ import (
"math/rand" "math/rand"
"testing" "testing"
"github.com/lightningnetwork/lnd/tlv"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -69,19 +70,29 @@ func TestTaprootBriefcase(t *testing.T) {
_, err = rand.Read(anchorTweak[:]) _, err = rand.Read(anchorTweak[:])
require.NoError(t, err) require.NoError(t, err)
var commitBlob [100]byte
_, err = rand.Read(commitBlob[:])
require.NoError(t, err)
testCase := &taprootBriefcase{ testCase := &taprootBriefcase{
CtrlBlocks: &ctrlBlocks{ CtrlBlocks: tlv.NewRecordT[tlv.TlvType0](ctrlBlocks{
CommitSweepCtrlBlock: sweepCtrlBlock[:], CommitSweepCtrlBlock: sweepCtrlBlock[:],
RevokeSweepCtrlBlock: revokeCtrlBlock[:], RevokeSweepCtrlBlock: revokeCtrlBlock[:],
OutgoingHtlcCtrlBlocks: randResolverCtrlBlocks(t), OutgoingHtlcCtrlBlocks: randResolverCtrlBlocks(t),
IncomingHtlcCtrlBlocks: randResolverCtrlBlocks(t), IncomingHtlcCtrlBlocks: randResolverCtrlBlocks(t),
SecondLevelCtrlBlocks: randResolverCtrlBlocks(t), SecondLevelCtrlBlocks: randResolverCtrlBlocks(t),
}, }),
TapTweaks: &tapTweaks{ TapTweaks: tlv.NewRecordT[tlv.TlvType1](tapTweaks{
AnchorTweak: anchorTweak[:], AnchorTweak: anchorTweak[:],
BreachedHtlcTweaks: randHtlcTweaks(t), BreachedHtlcTweaks: randHtlcTweaks(t),
BreachedSecondLevelHltcTweaks: randHtlcTweaks(t), BreachedSecondLevelHltcTweaks: randHtlcTweaks(t),
}, }),
SettledCommitBlob: tlv.SomeRecordT(
tlv.NewPrimitiveRecord[tlv.TlvType2](commitBlob[:]),
),
BreachedCommitBlob: tlv.SomeRecordT(
tlv.NewPrimitiveRecord[tlv.TlvType3](commitBlob[:]),
),
} }
var b bytes.Buffer var b bytes.Buffer
+2
View File
@@ -21,6 +21,7 @@ import (
"github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/sweep"
"github.com/lightningnetwork/lnd/tlv"
) )
// SUMMARY OF OUTPUT STATES // SUMMARY OF OUTPUT STATES
@@ -1423,6 +1424,7 @@ func makeKidOutput(outpoint, originChanPoint *wire.OutPoint,
return kidOutput{ return kidOutput{
breachedOutput: makeBreachedOutput( breachedOutput: makeBreachedOutput(
outpoint, witnessType, nil, signDescriptor, heightHint, outpoint, witnessType, nil, signDescriptor, heightHint,
fn.None[tlv.Blob](),
), ),
isHtlc: isHtlc, isHtlc: isHtlc,
originChanPoint: *originChanPoint, originChanPoint: *originChanPoint,
@@ -0,0 +1,95 @@
# Release Notes
- [Bug Fixes](#bug-fixes)
- [New Features](#new-features)
- [Functional Enhancements](#functional-enhancements)
- [RPC Additions](#rpc-additions)
- [lncli Additions](#lncli-additions)
- [Improvements](#improvements)
- [Functional Updates](#functional-updates)
- [RPC Updates](#rpc-updates)
- [lncli Updates](#lncli-updates)
- [Breaking Changes](#breaking-changes)
- [Performance Improvements](#performance-improvements)
- [Technical and Architectural Updates](#technical-and-architectural-updates)
- [BOLT Spec Updates](#bolt-spec-updates)
- [Testing](#testing)
- [Database](#database)
- [Code Health](#code-health)
- [Tooling and Documentation](#tooling-and-documentation)
# Bug Fixes
# New Features
The main channel state machine and database now allow for processing and storing
custom Taproot script leaves, [allowing the implementation of custom channel
types](https://github.com/lightningnetwork/lnd/pull/8960).
## Functional Enhancements
* A new `protocol.simple-taproot-overlay-chans` configuration item/CLI flag was
added [to turn on custom channel
functionality](https://github.com/lightningnetwork/lnd/pull/8960).
## RPC Additions
* Some new experimental [RPCs for managing SCID
aliases](https://github.com/lightningnetwork/lnd/pull/8960) were added under
the `routerrpc` package. These methods allow manually adding and deleting SCID
aliases locally to your node.
> NOTE: these new RPC methods are marked as experimental
(`XAddLocalChanAliases` & `XDeleteLocalChanAliases`) and upon calling
them the aliases will not be communicated with the channel peer.
* The responses for the `ListChannels`, `PendingChannels` and `ChannelBalance`
RPCs now include [a new `custom_channel_data` field that is only set for
custom channels](https://github.com/lightningnetwork/lnd/pull/8960).
* The `routerrpc.SendPaymentV2` RPC has a new field [`first_hop_custom_records`
that allows the user to send custom p2p wire message TLV types to the first
hop of a payment](https://github.com/lightningnetwork/lnd/pull/8960).
That new field is also exposed in the `routerrpc.HtlcInterceptor`, so it can
be read and interpreted by external software.
* The `routerrpc.HtlcInterceptor` now [allows some values of the HTLC to be
modified before they're validated by the state
machine](https://github.com/lightningnetwork/lnd/pull/8960). The fields that
can be modified are `outgoing_amount_msat` (if transported overlaid value of
HTLC doesn't match the actual BTC amount being transferred) and
`outgoing_htlc_wire_custom_records` (allow adding custom TLV values to the
p2p wire message of the forwarded HTLC).
* A new [`invoicesrpc.HtlcModifier` RPC now allows incoming HTLCs that attempt
to satisfy an invoice to be modified before they're
validated](https://github.com/lightningnetwork/lnd/pull/8960). This allows
custom channels to determine what the actual (overlaid) value of an HTLC is,
even if that value doesn't match the actual BTC amount being transferred by
the HTLC.
## lncli Additions
# Improvements
## Functional Updates
## RPC Updates
## lncli Updates
## Code Health
## Breaking Changes
## Performance Improvements
# Technical and Architectural Updates
## BOLT Spec Updates
## Testing
## Database
## Code Health
## Tooling and Documentation
# Contributors (Alphabetical Order)
* ffranr
* George Tsagkarelis
* Olaoluwa Osuntokun
* Oliver Gugger
+4
View File
@@ -92,4 +92,8 @@ var defaultSetDesc = setDesc{
SetInit: {}, // I SetInit: {}, // I
SetNodeAnn: {}, // N SetNodeAnn: {}, // N
}, },
lnwire.SimpleTaprootOverlayChansOptional: {
SetInit: {}, // I
SetNodeAnn: {}, // N
},
} }
+5
View File
@@ -79,6 +79,11 @@ var deps = depDesc{
lnwire.AnchorsZeroFeeHtlcTxOptional: {}, lnwire.AnchorsZeroFeeHtlcTxOptional: {},
lnwire.ExplicitChannelTypeOptional: {}, lnwire.ExplicitChannelTypeOptional: {},
}, },
lnwire.SimpleTaprootOverlayChansOptional: {
lnwire.SimpleTaprootChannelsOptionalStaging: {},
lnwire.TLVOnionPayloadOptional: {},
lnwire.ScidAliasOptional: {},
},
lnwire.RouteBlindingOptional: { lnwire.RouteBlindingOptional: {
lnwire.TLVOnionPayloadOptional: {}, lnwire.TLVOnionPayloadOptional: {},
}, },
+7
View File
@@ -63,6 +63,9 @@ type Config struct {
// NoRouteBlinding unsets route blinding feature bits. // NoRouteBlinding unsets route blinding feature bits.
NoRouteBlinding bool NoRouteBlinding bool
// NoTaprootOverlay unsets the taproot overlay channel feature bits.
NoTaprootOverlay bool
// CustomFeatures is a set of custom features to advertise in each // CustomFeatures is a set of custom features to advertise in each
// set. // set.
CustomFeatures map[Set][]lnwire.FeatureBit CustomFeatures map[Set][]lnwire.FeatureBit
@@ -192,6 +195,10 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
raw.Unset(lnwire.Bolt11BlindedPathsOptional) raw.Unset(lnwire.Bolt11BlindedPathsOptional)
raw.Unset(lnwire.Bolt11BlindedPathsRequired) raw.Unset(lnwire.Bolt11BlindedPathsRequired)
} }
if cfg.NoTaprootOverlay {
raw.Unset(lnwire.SimpleTaprootOverlayChansOptional)
raw.Unset(lnwire.SimpleTaprootOverlayChansRequired)
}
for _, custom := range cfg.CustomFeatures[set] { for _, custom := range cfg.CustomFeatures[set] {
if custom > set.Maximum() { if custom > set.Maximum() {
return nil, fmt.Errorf("feature bit: %v "+ return nil, fmt.Errorf("feature bit: %v "+
+68
View File
@@ -307,6 +307,74 @@ func explicitNegotiateCommitmentType(channelType lnwire.ChannelType, local,
return lnwallet.CommitmentTypeSimpleTaproot, nil return lnwallet.CommitmentTypeSimpleTaproot, nil
// Simple taproot channels overlay only.
case channelFeatures.OnlyContains(
lnwire.SimpleTaprootOverlayChansRequired,
):
if !hasFeatures(
local, remote,
lnwire.SimpleTaprootOverlayChansOptional,
) {
return 0, errUnsupportedChannelType
}
return lnwallet.CommitmentTypeSimpleTaprootOverlay, nil
// Simple taproot overlay channels with scid only.
case channelFeatures.OnlyContains(
lnwire.SimpleTaprootOverlayChansRequired,
lnwire.ScidAliasRequired,
):
if !hasFeatures(
local, remote,
lnwire.SimpleTaprootOverlayChansOptional,
lnwire.ScidAliasOptional,
) {
return 0, errUnsupportedChannelType
}
return lnwallet.CommitmentTypeSimpleTaprootOverlay, nil
// Simple taproot overlay channels with zero conf only.
case channelFeatures.OnlyContains(
lnwire.SimpleTaprootOverlayChansRequired,
lnwire.ZeroConfRequired,
):
if !hasFeatures(
local, remote,
lnwire.SimpleTaprootOverlayChansOptional,
lnwire.ZeroConfOptional,
) {
return 0, errUnsupportedChannelType
}
return lnwallet.CommitmentTypeSimpleTaprootOverlay, nil
// Simple taproot overlay channels with scid and zero conf.
case channelFeatures.OnlyContains(
lnwire.SimpleTaprootOverlayChansRequired,
lnwire.ZeroConfRequired,
lnwire.ScidAliasRequired,
):
if !hasFeatures(
local, remote,
lnwire.SimpleTaprootOverlayChansOptional,
lnwire.ZeroConfOptional,
lnwire.ScidAliasOptional,
) {
return 0, errUnsupportedChannelType
}
return lnwallet.CommitmentTypeSimpleTaprootOverlay, nil
// No features, use legacy commitment type. // No features, use legacy commitment type.
case channelFeatures.IsEmpty(): case channelFeatures.IsEmpty():
return lnwallet.CommitmentTypeLegacy, nil return lnwallet.CommitmentTypeLegacy, nil
+7
View File
@@ -558,6 +558,10 @@ type Config struct {
// AuxSigner is an optional signer that can be used to sign auxiliary // AuxSigner is an optional signer that can be used to sign auxiliary
// leaves for certain custom channel types. // leaves for certain custom channel types.
AuxSigner fn.Option[lnwallet.AuxSigner] AuxSigner fn.Option[lnwallet.AuxSigner]
// AuxResolver is an optional interface that can be used to modify the
// way contracts are resolved.
AuxResolver fn.Option[lnwallet.AuxContractResolver]
} }
// Manager acts as an orchestrator/bridge between the wallet's // Manager acts as an orchestrator/bridge between the wallet's
@@ -1090,6 +1094,9 @@ func (f *Manager) advanceFundingState(channel *channeldb.OpenChannel,
f.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { f.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) {
chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s))
}) })
f.cfg.AuxResolver.WhenSome(func(s lnwallet.AuxContractResolver) {
chanOpts = append(chanOpts, lnwallet.WithAuxResolver(s))
})
// We create the state-machine object which wraps the database state. // We create the state-machine object which wraps the database state.
lnChannel, err := lnwallet.NewLightningChannel( lnChannel, err := lnwallet.NewLightningChannel(
+3 -2
View File
@@ -4653,8 +4653,8 @@ func testZeroConf(t *testing.T, chanType *lnwire.ChannelType) {
// opening behavior with a specified fundmax flag. To give a hypothetical // opening behavior with a specified fundmax flag. To give a hypothetical
// example, if ANCHOR types had been introduced after the fundmax flag had been // example, if ANCHOR types had been introduced after the fundmax flag had been
// activated, the developer would have had to code for the anchor reserve in the // activated, the developer would have had to code for the anchor reserve in the
// funding manager in the context of public and private channels. Otherwise // funding manager in the context of public and private channels. Otherwise,
// inconsistent bahvior would have resulted when specifying fundmax for // inconsistent behavior would have resulted when specifying fundmax for
// different types of channel openings. // different types of channel openings.
// To ensure consistency this test compares a map of locally defined channel // To ensure consistency this test compares a map of locally defined channel
// commitment types to the list of channel types that are defined in the proto // commitment types to the list of channel types that are defined in the proto
@@ -4670,6 +4670,7 @@ func TestCommitmentTypeFundmaxSanityCheck(t *testing.T) {
"ANCHORS": 3, "ANCHORS": 3,
"SCRIPT_ENFORCED_LEASE": 4, "SCRIPT_ENFORCED_LEASE": 4,
"SIMPLE_TAPROOT": 5, "SIMPLE_TAPROOT": 5,
"SIMPLE_TAPROOT_OVERLAY": 6,
} }
for commitmentType := range lnrpc.CommitmentType_value { for commitmentType := range lnrpc.CommitmentType_value {
+125 -86
View File
@@ -6,7 +6,9 @@ import (
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/tlv"
) )
// EmptyOutPoint is a zeroed outpoint. // EmptyOutPoint is a zeroed outpoint.
@@ -63,6 +65,10 @@ type Input interface {
// UnconfParent returns information about a possibly unconfirmed parent // UnconfParent returns information about a possibly unconfirmed parent
// tx. // tx.
UnconfParent() *TxInfo UnconfParent() *TxInfo
// ResolutionBlob returns a special opaque blob to be used to
// sweep/resolve this input.
ResolutionBlob() fn.Option[tlv.Blob]
} }
// TxInfo describes properties of a parent tx that are relevant for CPFP. // TxInfo describes properties of a parent tx that are relevant for CPFP.
@@ -106,6 +112,10 @@ type inputKit struct {
// unconfParent contains information about a potential unconfirmed // unconfParent contains information about a potential unconfirmed
// parent transaction. // parent transaction.
unconfParent *TxInfo unconfParent *TxInfo
// resolutionBlob is an optional blob that can be used to resolve an
// input.
resolutionBlob fn.Option[tlv.Blob]
} }
// OutPoint returns the breached output's identifier that is to be included as // OutPoint returns the breached output's identifier that is to be included as
@@ -156,6 +166,36 @@ func (i *inputKit) UnconfParent() *TxInfo {
return i.unconfParent return i.unconfParent
} }
// ResolutionBlob returns a special opaque blob to be used to sweep/resolve
// this input.
func (i *inputKit) ResolutionBlob() fn.Option[tlv.Blob] {
return i.resolutionBlob
}
// inputOpts contains options for constructing a new input.
type inputOpts struct {
// resolutionBlob is an optional blob that can be used to resolve an
// input.
resolutionBlob fn.Option[tlv.Blob]
}
// defaultInputOpts returns a new inputOpts with default values.
func defaultInputOpts() *inputOpts {
return &inputOpts{}
}
// InputOpt is a functional option that can be used to modify the default input
// options.
type InputOpt func(*inputOpts) //nolint:revive
// WithResolutionBlob is an option that can be used to set a resolution blob on
// for an input.
func WithResolutionBlob(b fn.Option[tlv.Blob]) InputOpt {
return func(o *inputOpts) {
o.resolutionBlob = b
}
}
// BaseInput contains all the information needed to sweep a basic // BaseInput contains all the information needed to sweep a basic
// output (CSV/CLTV/no time lock). // output (CSV/CLTV/no time lock).
type BaseInput struct { type BaseInput struct {
@@ -166,15 +206,21 @@ type BaseInput struct {
// sweep transaction. // sweep transaction.
func MakeBaseInput(outpoint *wire.OutPoint, witnessType WitnessType, func MakeBaseInput(outpoint *wire.OutPoint, witnessType WitnessType,
signDescriptor *SignDescriptor, heightHint uint32, signDescriptor *SignDescriptor, heightHint uint32,
unconfParent *TxInfo) BaseInput { unconfParent *TxInfo, opts ...InputOpt) BaseInput {
opt := defaultInputOpts()
for _, optF := range opts {
optF(opt)
}
return BaseInput{ return BaseInput{
inputKit{ inputKit{
outpoint: *outpoint, outpoint: *outpoint,
witnessType: witnessType, witnessType: witnessType,
signDesc: *signDescriptor, signDesc: *signDescriptor,
heightHint: heightHint, heightHint: heightHint,
unconfParent: unconfParent, unconfParent: unconfParent,
resolutionBlob: opt.resolutionBlob,
}, },
} }
} }
@@ -182,10 +228,11 @@ func MakeBaseInput(outpoint *wire.OutPoint, witnessType WitnessType,
// NewBaseInput allocates and assembles a new *BaseInput that can be used to // NewBaseInput allocates and assembles a new *BaseInput that can be used to
// construct a sweep transaction. // construct a sweep transaction.
func NewBaseInput(outpoint *wire.OutPoint, witnessType WitnessType, func NewBaseInput(outpoint *wire.OutPoint, witnessType WitnessType,
signDescriptor *SignDescriptor, heightHint uint32) *BaseInput { signDescriptor *SignDescriptor, heightHint uint32,
opts ...InputOpt) *BaseInput {
input := MakeBaseInput( input := MakeBaseInput(
outpoint, witnessType, signDescriptor, heightHint, nil, outpoint, witnessType, signDescriptor, heightHint, nil, opts...,
) )
return &input return &input
@@ -195,36 +242,31 @@ func NewBaseInput(outpoint *wire.OutPoint, witnessType WitnessType,
// construct a sweep transaction. // construct a sweep transaction.
func NewCsvInput(outpoint *wire.OutPoint, witnessType WitnessType, func NewCsvInput(outpoint *wire.OutPoint, witnessType WitnessType,
signDescriptor *SignDescriptor, heightHint uint32, signDescriptor *SignDescriptor, heightHint uint32,
blockToMaturity uint32) *BaseInput { blockToMaturity uint32, opts ...InputOpt) *BaseInput {
return &BaseInput{ input := MakeBaseInput(
inputKit{ outpoint, witnessType, signDescriptor, heightHint, nil, opts...,
outpoint: *outpoint, )
witnessType: witnessType,
signDesc: *signDescriptor, input.blockToMaturity = blockToMaturity
heightHint: heightHint,
blockToMaturity: blockToMaturity, return &input
},
}
} }
// NewCsvInputWithCltv assembles a new csv and cltv locked input that can be // NewCsvInputWithCltv assembles a new csv and cltv locked input that can be
// used to construct a sweep transaction. // used to construct a sweep transaction.
func NewCsvInputWithCltv(outpoint *wire.OutPoint, witnessType WitnessType, func NewCsvInputWithCltv(outpoint *wire.OutPoint, witnessType WitnessType,
signDescriptor *SignDescriptor, heightHint uint32, signDescriptor *SignDescriptor, heightHint uint32,
csvDelay uint32, cltvExpiry uint32) *BaseInput { csvDelay uint32, cltvExpiry uint32, opts ...InputOpt) *BaseInput {
return &BaseInput{ input := MakeBaseInput(
inputKit{ outpoint, witnessType, signDescriptor, heightHint, nil, opts...,
outpoint: *outpoint, )
witnessType: witnessType,
signDesc: *signDescriptor, input.blockToMaturity = csvDelay
heightHint: heightHint, input.cltvExpiry = cltvExpiry
blockToMaturity: csvDelay,
cltvExpiry: cltvExpiry, return &input
unconfParent: nil,
},
}
} }
// CraftInputScript returns a valid set of input scripts allowing this output // CraftInputScript returns a valid set of input scripts allowing this output
@@ -256,16 +298,16 @@ type HtlcSucceedInput struct {
// construct a sweep transaction. // construct a sweep transaction.
func MakeHtlcSucceedInput(outpoint *wire.OutPoint, func MakeHtlcSucceedInput(outpoint *wire.OutPoint,
signDescriptor *SignDescriptor, preimage []byte, heightHint, signDescriptor *SignDescriptor, preimage []byte, heightHint,
blocksToMaturity uint32) HtlcSucceedInput { blocksToMaturity uint32, opts ...InputOpt) HtlcSucceedInput {
input := MakeBaseInput(
outpoint, HtlcAcceptedRemoteSuccess, signDescriptor,
heightHint, nil, opts...,
)
input.blockToMaturity = blocksToMaturity
return HtlcSucceedInput{ return HtlcSucceedInput{
inputKit: inputKit{ inputKit: input.inputKit,
outpoint: *outpoint,
witnessType: HtlcAcceptedRemoteSuccess,
signDesc: *signDescriptor,
heightHint: heightHint,
blockToMaturity: blocksToMaturity,
},
preimage: preimage, preimage: preimage,
} }
} }
@@ -274,16 +316,17 @@ func MakeHtlcSucceedInput(outpoint *wire.OutPoint,
// to spend an HTLC output for a taproot channel on the remote party's // to spend an HTLC output for a taproot channel on the remote party's
// commitment transaction. // commitment transaction.
func MakeTaprootHtlcSucceedInput(op *wire.OutPoint, signDesc *SignDescriptor, func MakeTaprootHtlcSucceedInput(op *wire.OutPoint, signDesc *SignDescriptor,
preimage []byte, heightHint, blocksToMaturity uint32) HtlcSucceedInput { preimage []byte, heightHint, blocksToMaturity uint32,
opts ...InputOpt) HtlcSucceedInput {
input := MakeBaseInput(
op, TaprootHtlcAcceptedRemoteSuccess, signDesc,
heightHint, nil, opts...,
)
input.blockToMaturity = blocksToMaturity
return HtlcSucceedInput{ return HtlcSucceedInput{
inputKit: inputKit{ inputKit: input.inputKit,
outpoint: *op,
witnessType: TaprootHtlcAcceptedRemoteSuccess,
signDesc: *signDesc,
heightHint: heightHint,
blockToMaturity: blocksToMaturity,
},
preimage: preimage, preimage: preimage,
} }
} }
@@ -388,7 +431,8 @@ func (i *HtlcSecondLevelAnchorInput) CraftInputScript(signer Signer,
// to spend the HTLC output on our commit using the second level timeout // to spend the HTLC output on our commit using the second level timeout
// transaction. // transaction.
func MakeHtlcSecondLevelTimeoutAnchorInput(signedTx *wire.MsgTx, func MakeHtlcSecondLevelTimeoutAnchorInput(signedTx *wire.MsgTx,
signDetails *SignDetails, heightHint uint32) HtlcSecondLevelAnchorInput { signDetails *SignDetails, heightHint uint32,
opts ...InputOpt) HtlcSecondLevelAnchorInput {
// Spend an HTLC output on our local commitment tx using the // Spend an HTLC output on our local commitment tx using the
// 2nd timeout transaction. // 2nd timeout transaction.
@@ -408,16 +452,15 @@ func MakeHtlcSecondLevelTimeoutAnchorInput(signedTx *wire.MsgTx,
) )
} }
return HtlcSecondLevelAnchorInput{ input := MakeBaseInput(
inputKit: inputKit{ &signedTx.TxIn[0].PreviousOutPoint,
outpoint: signedTx.TxIn[0].PreviousOutPoint, HtlcOfferedTimeoutSecondLevelInputConfirmed,
witnessType: HtlcOfferedTimeoutSecondLevelInputConfirmed, &signDetails.SignDesc, heightHint, nil, opts...,
signDesc: signDetails.SignDesc, )
heightHint: heightHint, input.blockToMaturity = 1
// CSV delay is always 1 for these inputs. return HtlcSecondLevelAnchorInput{
blockToMaturity: 1, inputKit: input.inputKit,
},
SignedTx: signedTx, SignedTx: signedTx,
createWitness: createWitness, createWitness: createWitness,
} }
@@ -429,7 +472,7 @@ func MakeHtlcSecondLevelTimeoutAnchorInput(signedTx *wire.MsgTx,
// sweep the second level HTLC aggregated with other transactions. // sweep the second level HTLC aggregated with other transactions.
func MakeHtlcSecondLevelTimeoutTaprootInput(signedTx *wire.MsgTx, func MakeHtlcSecondLevelTimeoutTaprootInput(signedTx *wire.MsgTx,
signDetails *SignDetails, signDetails *SignDetails,
heightHint uint32) HtlcSecondLevelAnchorInput { heightHint uint32, opts ...InputOpt) HtlcSecondLevelAnchorInput {
createWitness := func(signer Signer, txn *wire.MsgTx, createWitness := func(signer Signer, txn *wire.MsgTx,
hashCache *txscript.TxSigHashes, hashCache *txscript.TxSigHashes,
@@ -453,16 +496,15 @@ func MakeHtlcSecondLevelTimeoutTaprootInput(signedTx *wire.MsgTx,
) )
} }
return HtlcSecondLevelAnchorInput{ input := MakeBaseInput(
inputKit: inputKit{ &signedTx.TxIn[0].PreviousOutPoint,
outpoint: signedTx.TxIn[0].PreviousOutPoint, TaprootHtlcLocalOfferedTimeout,
witnessType: TaprootHtlcLocalOfferedTimeout, &signDetails.SignDesc, heightHint, nil, opts...,
signDesc: signDetails.SignDesc, )
heightHint: heightHint, input.blockToMaturity = 1
// CSV delay is always 1 for these inputs. return HtlcSecondLevelAnchorInput{
blockToMaturity: 1, inputKit: input.inputKit,
},
SignedTx: signedTx, SignedTx: signedTx,
createWitness: createWitness, createWitness: createWitness,
} }
@@ -473,7 +515,7 @@ func MakeHtlcSecondLevelTimeoutTaprootInput(signedTx *wire.MsgTx,
// transaction. // transaction.
func MakeHtlcSecondLevelSuccessAnchorInput(signedTx *wire.MsgTx, func MakeHtlcSecondLevelSuccessAnchorInput(signedTx *wire.MsgTx,
signDetails *SignDetails, preimage lntypes.Preimage, signDetails *SignDetails, preimage lntypes.Preimage,
heightHint uint32) HtlcSecondLevelAnchorInput { heightHint uint32, opts ...InputOpt) HtlcSecondLevelAnchorInput {
// Spend an HTLC output on our local commitment tx using the 2nd // Spend an HTLC output on our local commitment tx using the 2nd
// success transaction. // success transaction.
@@ -492,18 +534,16 @@ func MakeHtlcSecondLevelSuccessAnchorInput(signedTx *wire.MsgTx,
preimage[:], signer, &desc, txn, preimage[:], signer, &desc, txn,
) )
} }
input := MakeBaseInput(
&signedTx.TxIn[0].PreviousOutPoint,
HtlcAcceptedSuccessSecondLevelInputConfirmed,
&signDetails.SignDesc, heightHint, nil, opts...,
)
input.blockToMaturity = 1
return HtlcSecondLevelAnchorInput{ return HtlcSecondLevelAnchorInput{
inputKit: inputKit{
outpoint: signedTx.TxIn[0].PreviousOutPoint,
witnessType: HtlcAcceptedSuccessSecondLevelInputConfirmed,
signDesc: signDetails.SignDesc,
heightHint: heightHint,
// CSV delay is always 1 for these inputs.
blockToMaturity: 1,
},
SignedTx: signedTx, SignedTx: signedTx,
inputKit: input.inputKit,
createWitness: createWitness, createWitness: createWitness,
} }
} }
@@ -513,7 +553,7 @@ func MakeHtlcSecondLevelSuccessAnchorInput(signedTx *wire.MsgTx,
// commitment transaction. // commitment transaction.
func MakeHtlcSecondLevelSuccessTaprootInput(signedTx *wire.MsgTx, func MakeHtlcSecondLevelSuccessTaprootInput(signedTx *wire.MsgTx,
signDetails *SignDetails, preimage lntypes.Preimage, signDetails *SignDetails, preimage lntypes.Preimage,
heightHint uint32) HtlcSecondLevelAnchorInput { heightHint uint32, opts ...InputOpt) HtlcSecondLevelAnchorInput {
createWitness := func(signer Signer, txn *wire.MsgTx, createWitness := func(signer Signer, txn *wire.MsgTx,
hashCache *txscript.TxSigHashes, hashCache *txscript.TxSigHashes,
@@ -537,16 +577,15 @@ func MakeHtlcSecondLevelSuccessTaprootInput(signedTx *wire.MsgTx,
) )
} }
return HtlcSecondLevelAnchorInput{ input := MakeBaseInput(
inputKit: inputKit{ &signedTx.TxIn[0].PreviousOutPoint,
outpoint: signedTx.TxIn[0].PreviousOutPoint, TaprootHtlcAcceptedLocalSuccess,
witnessType: TaprootHtlcAcceptedLocalSuccess, &signDetails.SignDesc, heightHint, nil, opts...,
signDesc: signDetails.SignDesc, )
heightHint: heightHint, input.blockToMaturity = 1
// CSV delay is always 1 for these inputs. return HtlcSecondLevelAnchorInput{
blockToMaturity: 1, inputKit: input.inputKit,
},
SignedTx: signedTx, SignedTx: signedTx,
createWitness: createWitness, createWitness: createWitness,
} }
+13
View File
@@ -8,8 +8,10 @@ import (
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/tlv"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
) )
@@ -127,6 +129,17 @@ func (m *MockInput) UnconfParent() *TxInfo {
return info.(*TxInfo) return info.(*TxInfo)
} }
func (m *MockInput) ResolutionBlob() fn.Option[tlv.Blob] {
args := m.Called()
info := args.Get(0)
if info == nil {
return fn.None[tlv.Blob]()
}
return info.(fn.Option[tlv.Blob])
}
// MockWitnessType implements the `WitnessType` interface and is used by other // MockWitnessType implements the `WitnessType` interface and is used by other
// packages for mock testing. // packages for mock testing.
type MockWitnessType struct { type MockWitnessType struct {
+4
View File
@@ -31,6 +31,10 @@ type ProtocolOptions struct {
// experimental simple taproot chans commitment type. // experimental simple taproot chans commitment type.
TaprootChans bool `long:"simple-taproot-chans" description:"if set, then lnd will create and accept requests for channels using the simple taproot commitment type"` TaprootChans bool `long:"simple-taproot-chans" description:"if set, then lnd will create and accept requests for channels using the simple taproot commitment type"`
// TaprootOverlayChans should be set if we want to enable support for
// the experimental taproot overlay chan type.
TaprootOverlayChans bool `long:"simple-taproot-overlay-chans" description:"if set, then lnd will create and accept requests for channels using the taproot overlay commitment type"`
// NoAnchors should be set if we don't want to support opening or accepting // NoAnchors should be set if we don't want to support opening or accepting
// channels having the anchor commitment type. // channels having the anchor commitment type.
NoAnchors bool `long:"no-anchors" description:"disable support for anchor commitments"` NoAnchors bool `long:"no-anchors" description:"disable support for anchor commitments"`
+4
View File
@@ -33,6 +33,10 @@ type ProtocolOptions struct {
// experimental simple taproot chans commitment type. // experimental simple taproot chans commitment type.
TaprootChans bool `long:"simple-taproot-chans" description:"if set, then lnd will create and accept requests for channels using the simple taproot commitment type"` TaprootChans bool `long:"simple-taproot-chans" description:"if set, then lnd will create and accept requests for channels using the simple taproot commitment type"`
// TaprootOverlayChans should be set if we want to enable support for
// the experimental taproot overlay chan type.
TaprootOverlayChans bool `long:"simple-taproot-overlay-chans" description:"if set, then lnd will create and accept requests for channels using the taproot overlay commitment type"`
// Anchors enables anchor commitments. // Anchors enables anchor commitments.
// TODO(halseth): transition itests to anchors instead! // TODO(halseth): transition itests to anchors instead!
Anchors bool `long:"anchors" description:"enable support for anchor commitments"` Anchors bool `long:"anchors" description:"enable support for anchor commitments"`
+418 -409
View File
@@ -229,8 +229,13 @@ const (
// to guarantee that the channel initiator has no incentives to close a leased // to guarantee that the channel initiator has no incentives to close a leased
// channel before its maturity date. // channel before its maturity date.
CommitmentType_SCRIPT_ENFORCED_LEASE CommitmentType = 4 CommitmentType_SCRIPT_ENFORCED_LEASE CommitmentType = 4
// TODO(roasbeef): need script enforce mirror type for the above as well? // A channel that uses musig2 for the funding output, and the new tapscript
// features where relevant.
CommitmentType_SIMPLE_TAPROOT CommitmentType = 5 CommitmentType_SIMPLE_TAPROOT CommitmentType = 5
// Identical to the SIMPLE_TAPROOT channel type, but with extra functionality.
// This channel type also commits to additional meta data in the tapscript
// leaves for the scripts in a channel.
CommitmentType_SIMPLE_TAPROOT_OVERLAY CommitmentType = 6
) )
// Enum value maps for CommitmentType. // Enum value maps for CommitmentType.
@@ -242,6 +247,7 @@ var (
3: "ANCHORS", 3: "ANCHORS",
4: "SCRIPT_ENFORCED_LEASE", 4: "SCRIPT_ENFORCED_LEASE",
5: "SIMPLE_TAPROOT", 5: "SIMPLE_TAPROOT",
6: "SIMPLE_TAPROOT_OVERLAY",
} }
CommitmentType_value = map[string]int32{ CommitmentType_value = map[string]int32{
"UNKNOWN_COMMITMENT_TYPE": 0, "UNKNOWN_COMMITMENT_TYPE": 0,
@@ -250,6 +256,7 @@ var (
"ANCHORS": 3, "ANCHORS": 3,
"SCRIPT_ENFORCED_LEASE": 4, "SCRIPT_ENFORCED_LEASE": 4,
"SIMPLE_TAPROOT": 5, "SIMPLE_TAPROOT": 5,
"SIMPLE_TAPROOT_OVERLAY": 6,
} }
) )
@@ -21071,7 +21078,7 @@ var file_lightning_proto_rawDesc = []byte{
0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x12, 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, 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, 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, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x05, 0x2a, 0xa8, 0x01,
0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 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, 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, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a,
@@ -21080,420 +21087,422 @@ var file_lightning_proto_rawDesc = []byte{
0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x10, 0x03, 0x12, 0x19, 0x0a, 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, 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, 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, 0x4c, 0x45, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x05, 0x12, 0x1a, 0x0a, 0x16,
0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x49, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4f,
0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x56, 0x45, 0x52, 0x4c, 0x41, 0x59, 0x10, 0x06, 0x2a, 0x61, 0x0a, 0x09, 0x49, 0x6e, 0x69, 0x74,
0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x4f, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54,
0x43, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f,
0x4f, 0x52, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x10,
0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x52,
0x60, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x49, 0x54, 0x49,
0x65, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, 0x60, 0x0a, 0x0e, 0x52,
0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a,
0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x43, 0x4f, 0x4d, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12,
0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4f, 0x55, 0x54, 0x47, 0x4f, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x49,
0x54, 0x4c, 0x43, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x4e, 0x43, 0x4f, 0x4d, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, 0x02, 0x12, 0x11,
0x04, 0x2a, 0x71, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x0a, 0x0d, 0x4f, 0x55, 0x54, 0x47, 0x4f, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10,
0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04, 0x2a, 0x71, 0x0a,
0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x63, 0x6f,
0x4c, 0x41, 0x49, 0x4d, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x4c, 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x55, 0x4e,
0x41, 0x49, 0x4d, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, 0x41, 0x49, 0x4d,
0x4f, 0x4e, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x45,
0x53, 0x54, 0x41, 0x47, 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, 0x45, 0x44,
0x55, 0x54, 0x10, 0x05, 0x2a, 0x39, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x47,
0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x05,
0x4e, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x45, 0x54, 0x57, 0x45, 0x45, 0x4e, 0x4e, 0x45, 0x2a, 0x39, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79,
0x53, 0x53, 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x52, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x01, 0x2a, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12,
0x3b, 0x0a, 0x10, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x1a, 0x0a, 0x16, 0x42, 0x45, 0x54, 0x57, 0x45, 0x45, 0x4e, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x43,
0x61, 0x74, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x45, 0x4e, 0x54, 0x52, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x01, 0x2a, 0x3b, 0x0a, 0x10, 0x49,
0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
0x0a, 0x08, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xf6, 0x01, 0x0a, 0x0c, 0x0a, 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a,
0x14, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x41,
0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xf6, 0x01, 0x0a, 0x14, 0x50, 0x61, 0x79,
0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1a, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f,
0x0a, 0x16, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41,
0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x46, 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, 0x4e, 0x4f, 0x5f, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d,
0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52,
0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x4f, 0x55, 0x54,
0x03, 0x12, 0x2c, 0x0a, 0x28, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52,
0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x2c, 0x0a,
0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x28, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f,
0x27, 0x0a, 0x23, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e,
0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x27, 0x0a, 0x23, 0x46,
0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e,
0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e,
0x4c, 0x45, 0x44, 0x10, 0x06, 0x2a, 0x89, 0x05, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x43, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f,
0x65, 0x42, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10,
0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x00, 0x12, 0x18, 0x06, 0x2a, 0x89, 0x05, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, 0x74,
0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f,
0x43, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x49, 0x54, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41,
0x49, 0x41, 0x4c, 0x5f, 0x52, 0x4f, 0x55, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x4f,
0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x50, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x5f,
0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x52, 0x4f, 0x55, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x03, 0x12, 0x1f, 0x0a,
0x10, 0x04, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57,
0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x4f, 0x50, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x04, 0x12, 0x1f,
0x54, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f,
0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x05, 0x12,
0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45,
0x54, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49,
0x5f, 0x52, 0x45, 0x51, 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x07, 0x12,
0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x51,
0x5f, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f,
0x52, 0x45, 0x51, 0x10, 0x0a, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, 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, 0x4f, 0x50, 0x54, 0x10, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10,
0x0b, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x0a, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f,
0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0b, 0x12, 0x19, 0x0a,
0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b,
0x59, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x45, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54,
0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x50,
0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41,
0x54, 0x10, 0x0f, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x10, 0x44, 0x44, 0x52, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59,
0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x11, 0x12, 0x16, 0x0a, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0f, 0x12,
0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x10, 0x12, 0x0b, 0x0a, 0x07,
0x52, 0x45, 0x51, 0x10, 0x12, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x4d, 0x50, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x11, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d,
0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x13, 0x12, 0x0f, 0x0a, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10,
0x0b, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x14, 0x12, 0x0f, 0x12, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e,
0x0a, 0x0b, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x15, 0x12, 0x45, 0x4c, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x13, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x4e, 0x43,
0x1d, 0x0a, 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x14, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x4e,
0x46, 0x45, 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x16, 0x12, 0x1d, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x15, 0x12, 0x1d, 0x0a, 0x19, 0x41,
0x0a, 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, 0x45, 0x5f,
0x45, 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x17, 0x12, 0x1b, 0x0a, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x16, 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x4e,
0x17, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x48,
0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x17, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55,
0x55, 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4f, 0x50, 0x54, 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x52, 0x45, 0x51, 0x55,
0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x19, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x52, 0x49, 0x52, 0x45, 0x44, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f,
0x45, 0x51, 0x10, 0x1e, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x41,
0x1f, 0x2a, 0xac, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x4c, 0x10, 0x19, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x1e,
0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x1f, 0x2a, 0xac, 0x01,
0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 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, 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, 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, 0x4e, 0x4f, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x50, 0x45,
0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x44, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x55, 0x50, 0x44, 0x41, 0x54,
0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f,
0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f,
0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c,
0x41, 0x4c, 0x49, 0x44, 0x5f, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x10, 0x04, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45,
0x32, 0xb9, 0x27, 0x0a, 0x09, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x4a, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44,
0x0a, 0x0d, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x5f, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x10, 0x04, 0x32, 0xb9, 0x27, 0x0a,
0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x09, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x4a, 0x0a, 0x0d, 0x57, 0x61,
0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e,
0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65,
0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63,
0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52,
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43,
0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73,
0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e,
0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63,
0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e,
0x74, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69,
0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65,
0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61,
0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c,
0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65,
0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64,
0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65,
0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18,
0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73,
0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e,
0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x65, 0x12, 0x4c, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55,
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c,
0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73,
0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e,
0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52,
0x12, 0x3b, 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x16, 0x2e, 0x6c, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x54,
0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x12, 0x3b, 0x0a, 0x08,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63,
0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x0a, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e,
0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x4e, 0x65, 0x77,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e,
0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x12, 0x44, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64,
0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b,
0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e,
0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69,
0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d,
0x72, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44,
0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x19, 0x2e,
0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63,
0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70,
0x70, 0x63, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44,
0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x69, 0x73,
0x65, 0x65, 0x72, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73,
0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70,
0x72, 0x69, 0x62, 0x65, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72,
0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73,
0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6c, 0x6e, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63,
0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x38, 0x0a, 0x07,
0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e,
0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16,
0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65,
0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 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, 0x73, 0x70, 0x6f, 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65,
0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65,
0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x50, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65,
0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63,
0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e,
0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64,
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e,
0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 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, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x12, 0x56, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e,
0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x16,
0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43,
0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63,
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e,
0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61,
0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43,
0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f,
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x4f, 0x70, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70,
0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x53, 0x0a, 0x10, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x74, 0x61,
0x6e, 0x65, 0x6c, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x10, 0x42,
0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12,
0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65,
0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x10, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65,
0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x12, 0x4c, 0x0a, 0x10, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65,
0x6f, 0x6e, 0x4d, 0x73, 0x67, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x53, 0x74, 0x65, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73,
0x73, 0x70, 0x12, 0x50, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x67, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e,
0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x50,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x6f,
0x6e, 0x73, 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a,
0x28, 0x01, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41,
0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01,
0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68,
0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c,
0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x41, 0x62, 0x61, 0x6e,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72,
0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x53, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63,
0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52,
0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x50,
0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53,
0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0f, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72,
0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64,
0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x12, 0x2e, 0x6c, 0x6e,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70,
0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f,
0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64,
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, 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, 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, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x41, 0x0a, 0x0f,
0x65, 0x12, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x12,
0x65, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f,
0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72,
0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x37, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x2e,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x1a, 0x19, 0x2e,
0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65,
0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x0d, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74,
0x6f, 0x69, 0x63, 0x65, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63,
0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75,
0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74,
0x63, 0x72, 0x69, 0x62, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x33, 0x0a, 0x0d, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74,
0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x44, 0x48, 0x61, 0x73, 0x68, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76,
0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x13, 0x2e, 0x6c, 0x6e, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62,
0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70,
0x1a, 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e,
0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x44, 0x65, 0x63, 0x6f, 0x64,
0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 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, 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, 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, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61,
0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65,
0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x12, 0x56, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79,
0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65,
0x6b, 0x75, 0x70, 0x12, 0x21, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52,
0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73,
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x54, 0x0a, 0x17, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x73, 0x63,
0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70,
0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65,
0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x47, 0x0a, 0x0e, 0x47, 0x65,
0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x19, 0x2e, 0x6c,
0x6f, 0x74, 0x12, 0x4e, 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e,
0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x6e,
0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x49,
0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72,
0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x12, 0x36,
0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e,
0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65,
0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f,
0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x0b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52,
0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75,
0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f,
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0e,
0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19,
0x6f, 0x74, 0x30, 0x01, 0x12, 0x47, 0x0a, 0x0c, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e,
0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70,
0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x35, 0x0a,
0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x2e, 0x6c, 0x6e,
0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70,
0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62,
0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x20, 0x2e,
0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, 0x6f, 0x6c,
0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a,
0x53, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70,
0x6e, 0x49, 0x44, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x41, 0x0a,
0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x2e, 0x6c, 0x6e,
0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65,
0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x12, 0x3e, 0x0a, 0x09, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x2e,
0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46,
0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x18, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x12, 0x4e, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e,
0x6e, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75,
0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 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, 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, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72,
0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52,
0x77, 0x61, 0x72, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73,
0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65,
0x73, 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64,
0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x1b,
0x01, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65,
0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12,
0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x56, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73,
0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e,
0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65,
0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65,
0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52,
0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63,
0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63,
0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x61, 0x67, 0x65, 0x30, 0x01, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70,
0x61, 0x73, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x63, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x30,
0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x01, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73,
0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69,
0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x4c, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e,
0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52,
0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 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, 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, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68,
0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67,
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
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 ( var (
+7 -1
View File
@@ -1388,8 +1388,14 @@ enum CommitmentType {
A channel that uses musig2 for the funding output, and the new tapscript A channel that uses musig2 for the funding output, and the new tapscript
features where relevant. features where relevant.
*/ */
// TODO(roasbeef): need script enforce mirror type for the above as well?
SIMPLE_TAPROOT = 5; SIMPLE_TAPROOT = 5;
/*
Identical to the SIMPLE_TAPROOT channel type, but with extra functionality.
This channel type also commits to additional meta data in the tapscript
leaves for the scripts in a channel.
*/
SIMPLE_TAPROOT_OVERLAY = 6;
} }
message ChannelConstraints { message ChannelConstraints {
+3 -2
View File
@@ -4568,10 +4568,11 @@
"STATIC_REMOTE_KEY", "STATIC_REMOTE_KEY",
"ANCHORS", "ANCHORS",
"SCRIPT_ENFORCED_LEASE", "SCRIPT_ENFORCED_LEASE",
"SIMPLE_TAPROOT" "SIMPLE_TAPROOT",
"SIMPLE_TAPROOT_OVERLAY"
], ],
"default": "UNKNOWN_COMMITMENT_TYPE", "default": "UNKNOWN_COMMITMENT_TYPE",
"title": "- UNKNOWN_COMMITMENT_TYPE: Returned when the commitment type isn't known or unavailable.\n - LEGACY: A channel using the legacy commitment format having tweaked to_remote\nkeys.\n - STATIC_REMOTE_KEY: A channel that uses the modern commitment format where the key in the\noutput of the remote party does not change each state. This makes back\nup and recovery easier as when the channel is closed, the funds go\ndirectly to that key.\n - ANCHORS: A channel that uses a commitment format that has anchor outputs on the\ncommitments, allowing fee bumping after a force close transaction has\nbeen broadcast.\n - SCRIPT_ENFORCED_LEASE: A channel that uses a commitment type that builds upon the anchors\ncommitment format, but in addition requires a CLTV clause to spend outputs\npaying to the channel initiator. This is intended for use on leased channels\nto guarantee that the channel initiator has no incentives to close a leased\nchannel before its maturity date.\n - SIMPLE_TAPROOT: TODO(roasbeef): need script enforce mirror type for the above as well?" "description": " - UNKNOWN_COMMITMENT_TYPE: Returned when the commitment type isn't known or unavailable.\n - LEGACY: A channel using the legacy commitment format having tweaked to_remote\nkeys.\n - STATIC_REMOTE_KEY: A channel that uses the modern commitment format where the key in the\noutput of the remote party does not change each state. This makes back\nup and recovery easier as when the channel is closed, the funds go\ndirectly to that key.\n - ANCHORS: A channel that uses a commitment format that has anchor outputs on the\ncommitments, allowing fee bumping after a force close transaction has\nbeen broadcast.\n - SCRIPT_ENFORCED_LEASE: A channel that uses a commitment type that builds upon the anchors\ncommitment format, but in addition requires a CLTV clause to spend outputs\npaying to the channel initiator. This is intended for use on leased channels\nto guarantee that the channel initiator has no incentives to close a leased\nchannel before its maturity date.\n - SIMPLE_TAPROOT: A channel that uses musig2 for the funding output, and the new tapscript\nfeatures where relevant.\n - SIMPLE_TAPROOT_OVERLAY: Identical to the SIMPLE_TAPROOT channel type, but with extra functionality.\nThis channel type also commits to additional meta data in the tapscript\nleaves for the scripts in a channel."
}, },
"lnrpcConnectPeerRequest": { "lnrpcConnectPeerRequest": {
"type": "object", "type": "object",
+89
View File
@@ -0,0 +1,89 @@
package lnwallet
import (
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/tlv"
)
// CloseType is an enum that represents the type of close that we are trying to
// resolve.
type CloseType uint8
const (
// LocalForceClose represents a local force close.
LocalForceClose CloseType = iota
// RemoteForceClose represents a remote force close.
RemoteForceClose
// Breach represents a breach by the remote party.
Breach
)
// ResolutionReq is used to ask an outside sub-system for additional
// information needed to resolve a contract.
type ResolutionReq struct {
// ChanPoint is the channel point of the channel that we are trying to
// resolve.
ChanPoint wire.OutPoint
// ShortChanID is the short channel ID of the channel that we are
// trying to resolve.
ShortChanID lnwire.ShortChannelID
// Initiator is a bool if we're the initiator of the channel.
Initiator bool
// CommitBlob is an optional commit blob for the channel.
CommitBlob fn.Option[tlv.Blob]
// FundingBlob is an optional funding blob for the channel.
FundingBlob fn.Option[tlv.Blob]
// Type is the type of the witness that we are trying to resolve.
Type input.WitnessType
// CloseType is the type of close that we are trying to resolve.
CloseType CloseType
// CommitTx is the force close commitment transaction.
CommitTx *wire.MsgTx
// CommitFee is the fee that was paid for the commitment transaction.
CommitFee btcutil.Amount
// ContractPoint is the outpoint of the contract we're trying to
// resolve.
ContractPoint wire.OutPoint
// SignDesc is the sign descriptor for the contract.
SignDesc input.SignDescriptor
// KeyRing is the key ring for the channel.
KeyRing *CommitmentKeyRing
// CsvDelay is the CSV delay for the local output for this commitment.
CsvDelay uint32
// BreachCsvDelay is the CSV delay for the remote output. This is only
// set when the CloseType is Breach. This indicates the CSV delay to
// use for the remote party's to_local delayed output, that is now
// rightfully ours in a breach situation.
BreachCsvDelay fn.Option[uint32]
// CltvDelay is the CLTV delay for the outpoint.
CltvDelay fn.Option[uint32]
}
// AuxContractResolver is an interface that is used to resolve contracts that
// may need additional outside information to resolve correctly.
type AuxContractResolver interface {
// ResolveContract is called to resolve a contract that needs
// additional information to resolve properly. If no extra information
// is required, a nil Result error is returned.
ResolveContract(ResolutionReq) fn.Result[tlv.Blob]
}
+175 -19
View File
@@ -764,6 +764,10 @@ type LightningChannel struct {
// custom channel variants. // custom channel variants.
auxSigner fn.Option[AuxSigner] auxSigner fn.Option[AuxSigner]
// auxResolver is an optional component that can be used to modify the
// way contracts are resolved.
auxResolver fn.Option[AuxContractResolver]
// Capacity is the total capacity of this channel. // Capacity is the total capacity of this channel.
Capacity btcutil.Amount Capacity btcutil.Amount
@@ -824,8 +828,9 @@ type channelOpts struct {
localNonce *musig2.Nonces localNonce *musig2.Nonces
remoteNonce *musig2.Nonces remoteNonce *musig2.Nonces
leafStore fn.Option[AuxLeafStore] leafStore fn.Option[AuxLeafStore]
auxSigner fn.Option[AuxSigner] auxSigner fn.Option[AuxSigner]
auxResolver fn.Option[AuxContractResolver]
skipNonceInit bool skipNonceInit bool
} }
@@ -871,6 +876,14 @@ func WithAuxSigner(signer AuxSigner) ChannelOpt {
} }
} }
// WithAuxResolver is used to specify a custom aux contract resolver for the
// channel.
func WithAuxResolver(resolver AuxContractResolver) ChannelOpt {
return func(o *channelOpts) {
o.auxResolver = fn.Some[AuxContractResolver](resolver)
}
}
// defaultChannelOpts returns the set of default options for a new channel. // defaultChannelOpts returns the set of default options for a new channel.
func defaultChannelOpts() *channelOpts { func defaultChannelOpts() *channelOpts {
return &channelOpts{} return &channelOpts{}
@@ -924,6 +937,7 @@ func NewLightningChannel(signer input.Signer,
Signer: signer, Signer: signer,
leafStore: opts.leafStore, leafStore: opts.leafStore,
auxSigner: opts.auxSigner, auxSigner: opts.auxSigner,
auxResolver: opts.auxResolver,
sigPool: sigPool, sigPool: sigPool,
currentHeight: localCommit.CommitHeight, currentHeight: localCommit.CommitHeight,
commitChains: commitChains, commitChains: commitChains,
@@ -1884,6 +1898,10 @@ type HtlcRetribution struct {
// this HTLC was offered by us. This flag is used determine the exact // this HTLC was offered by us. This flag is used determine the exact
// witness type should be used to sweep the output. // witness type should be used to sweep the output.
IsIncoming bool IsIncoming bool
// ResolutionBlob is a blob used for aux channels that permits a
// spender of this output to claim all funds.
ResolutionBlob fn.Option[tlv.Blob]
} }
// BreachRetribution contains all the data necessary to bring a channel // BreachRetribution contains all the data necessary to bring a channel
@@ -1953,6 +1971,14 @@ type BreachRetribution struct {
// breaching commitment transaction. This allows downstream clients to // breaching commitment transaction. This allows downstream clients to
// have access to the public keys used in the scripts. // have access to the public keys used in the scripts.
KeyRing *CommitmentKeyRing KeyRing *CommitmentKeyRing
// LocalResolutionBlob is a blob used for aux channels that permits an
// honest party to sweep the local commitment output.
LocalResolutionBlob fn.Option[tlv.Blob]
// RemoteResolutionBlob is a blob used for aux channels that permits an
// honest party to sweep the remote commitment output.
RemoteResolutionBlob fn.Option[tlv.Blob]
} }
// NewBreachRetribution creates a new fully populated BreachRetribution for the // NewBreachRetribution creates a new fully populated BreachRetribution for the
@@ -1964,7 +1990,9 @@ type BreachRetribution struct {
// the required fields then ErrRevLogDataMissing will be returned. // the required fields then ErrRevLogDataMissing will be returned.
func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
breachHeight uint32, spendTx *wire.MsgTx, breachHeight uint32, spendTx *wire.MsgTx,
leafStore fn.Option[AuxLeafStore]) (*BreachRetribution, error) { leafStore fn.Option[AuxLeafStore],
auxResolver fn.Option[AuxContractResolver]) (*BreachRetribution,
error) {
// Query the on-disk revocation log for the snapshot which was recorded // Query the on-disk revocation log for the snapshot which was recorded
// at this particular state num. Based on whether a legacy revocation // at this particular state num. Based on whether a legacy revocation
@@ -2127,6 +2155,38 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
return nil, err return nil, err
} }
} }
// At this point, we'll check to see if we need any extra
// resolution data for this output.
resolveReq := ResolutionReq{
ChanPoint: chanState.FundingOutpoint,
ShortChanID: chanState.ShortChanID(),
Initiator: chanState.IsInitiator,
FundingBlob: chanState.CustomBlob,
Type: input.TaprootRemoteCommitSpend,
CloseType: Breach,
CommitTx: spendTx,
SignDesc: *br.LocalOutputSignDesc,
KeyRing: keyRing,
CsvDelay: ourDelay,
BreachCsvDelay: fn.Some(theirDelay),
CommitFee: chanState.RemoteCommitment.CommitFee,
}
if revokedLog != nil {
resolveReq.CommitBlob = revokedLog.CustomBlob.ValOpt()
}
resolveBlob := fn.MapOptionZ(
auxResolver,
func(a AuxContractResolver) fn.Result[tlv.Blob] {
return a.ResolveContract(resolveReq)
},
)
if err := resolveBlob.Err(); err != nil {
return nil, fmt.Errorf("unable to aux resolve: %w", err)
}
br.LocalResolutionBlob = resolveBlob.Option()
} }
// Similarly, if their balance exceeds the remote party's dust limit, // Similarly, if their balance exceeds the remote party's dust limit,
@@ -2174,6 +2234,37 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
return nil, err return nil, err
} }
} }
// At this point, we'll check to see if we need any extra
// resolution data for this output.
resolveReq := ResolutionReq{
ChanPoint: chanState.FundingOutpoint,
ShortChanID: chanState.ShortChanID(),
Initiator: chanState.IsInitiator,
FundingBlob: chanState.CustomBlob,
Type: input.TaprootCommitmentRevoke,
CloseType: Breach,
CommitTx: spendTx,
SignDesc: *br.RemoteOutputSignDesc,
KeyRing: keyRing,
CsvDelay: theirDelay,
BreachCsvDelay: fn.Some(theirDelay),
CommitFee: chanState.RemoteCommitment.CommitFee,
}
if revokedLog != nil {
resolveReq.CommitBlob = revokedLog.CustomBlob.ValOpt()
}
resolveBlob := fn.MapOptionZ(
auxResolver,
func(a AuxContractResolver) fn.Result[tlv.Blob] {
return a.ResolveContract(resolveReq)
},
)
if err := resolveBlob.Err(); err != nil {
return nil, fmt.Errorf("unable to aux resolve: %w", err)
}
br.RemoteResolutionBlob = resolveBlob.Option()
} }
// Finally, with all the necessary data constructed, we can pad the // Finally, with all the necessary data constructed, we can pad the
@@ -6473,6 +6564,11 @@ type CommitOutputResolution struct {
// that pay to the local party within the broadcast commitment // that pay to the local party within the broadcast commitment
// transaction. // transaction.
MaturityDelay uint32 MaturityDelay uint32
// ResolutionBlob is a blob used for aux channels that permits a
// spender of the output to properly resolve it in the case of a force
// close.
ResolutionBlob fn.Option[tlv.Blob]
} }
// UnilateralCloseSummary describes the details of a detected unilateral // UnilateralCloseSummary describes the details of a detected unilateral
@@ -6530,7 +6626,9 @@ type UnilateralCloseSummary struct {
func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel,
signer input.Signer, commitSpend *chainntnfs.SpendDetail, signer input.Signer, commitSpend *chainntnfs.SpendDetail,
remoteCommit channeldb.ChannelCommitment, commitPoint *btcec.PublicKey, remoteCommit channeldb.ChannelCommitment, commitPoint *btcec.PublicKey,
leafStore fn.Option[AuxLeafStore]) (*UnilateralCloseSummary, error) { leafStore fn.Option[AuxLeafStore],
auxResolver fn.Option[AuxContractResolver]) (*UnilateralCloseSummary,
error) {
// First, we'll generate the commitment point and the revocation point // First, we'll generate the commitment point and the revocation point
// so we can re-construct the HTLC state and also our payment key. // so we can re-construct the HTLC state and also our payment key.
@@ -6554,11 +6652,17 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel,
// Next, we'll obtain HTLC resolutions for all the outgoing HTLC's we // Next, we'll obtain HTLC resolutions for all the outgoing HTLC's we
// had on their commitment transaction. // had on their commitment transaction.
var leaseExpiry uint32 var (
leaseExpiry uint32
selfPoint *wire.OutPoint
localBalance int64
isRemoteInitiator = !chanState.IsInitiator
commitTxBroadcast = commitSpend.SpendingTx
)
if chanState.ChanType.HasLeaseExpiration() { if chanState.ChanType.HasLeaseExpiration() {
leaseExpiry = chanState.ThawHeight leaseExpiry = chanState.ThawHeight
} }
isRemoteInitiator := !chanState.IsInitiator
htlcResolutions, err := extractHtlcResolutions( htlcResolutions, err := extractHtlcResolutions(
chainfee.SatPerKWeight(remoteCommit.FeePerKw), commitType, chainfee.SatPerKWeight(remoteCommit.FeePerKw), commitType,
signer, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg, signer, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
@@ -6567,12 +6671,10 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel,
auxResult.AuxLeaves, auxResult.AuxLeaves,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to create htlc "+ return nil, fmt.Errorf("unable to create htlc resolutions: %w",
"resolutions: %v", err) err)
} }
commitTxBroadcast := commitSpend.SpendingTx
// Before we can generate the proper sign descriptor, we'll need to // Before we can generate the proper sign descriptor, we'll need to
// locate the output index of our non-delayed output on the commitment // locate the output index of our non-delayed output on the commitment
// transaction. // transaction.
@@ -6587,14 +6689,8 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to create self commit "+ return nil, fmt.Errorf("unable to create self commit "+
"script: %v", err) "script: %w", err)
} }
var (
selfPoint *wire.OutPoint
localBalance int64
)
for outputIndex, txOut := range commitTxBroadcast.TxOut { for outputIndex, txOut := range commitTxBroadcast.TxOut {
if bytes.Equal(txOut.PkScript, selfScript.PkScript()) { if bytes.Equal(txOut.PkScript, selfScript.PkScript()) {
selfPoint = &wire.OutPoint{ selfPoint = &wire.OutPoint{
@@ -6657,6 +6753,35 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel,
return nil, err return nil, err
} }
} }
// At this point, we'll check to see if we need any extra
// resolution data for this output.
resolveReq := ResolutionReq{
ChanPoint: chanState.FundingOutpoint,
ShortChanID: chanState.ShortChanID(),
Initiator: chanState.IsInitiator,
CommitBlob: chanState.RemoteCommitment.CustomBlob,
FundingBlob: chanState.CustomBlob,
Type: input.TaprootRemoteCommitSpend,
CloseType: RemoteForceClose,
CommitTx: commitTxBroadcast,
ContractPoint: *selfPoint,
SignDesc: commitResolution.SelfOutputSignDesc,
KeyRing: keyRing,
CsvDelay: maturityDelay,
CommitFee: chanState.RemoteCommitment.CommitFee,
}
resolveBlob := fn.MapOptionZ(
auxResolver,
func(a AuxContractResolver) fn.Result[tlv.Blob] {
return a.ResolveContract(resolveReq)
},
)
if err := resolveBlob.Err(); err != nil {
return nil, fmt.Errorf("unable to aux resolve: %w", err)
}
commitResolution.ResolutionBlob = resolveBlob.Option()
} }
closeSummary := channeldb.ChannelCloseSummary{ closeSummary := channeldb.ChannelCloseSummary{
@@ -7513,7 +7638,7 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) {
localCommitment := lc.channelState.LocalCommitment localCommitment := lc.channelState.LocalCommitment
summary, err := NewLocalForceCloseSummary( summary, err := NewLocalForceCloseSummary(
lc.channelState, lc.Signer, commitTx, lc.channelState, lc.Signer, commitTx,
localCommitment.CommitHeight, lc.leafStore, localCommitment.CommitHeight, lc.leafStore, lc.auxResolver,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to gen force close "+ return nil, fmt.Errorf("unable to gen force close "+
@@ -7531,7 +7656,9 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) {
// transaction corresponding to localCommit. // transaction corresponding to localCommit.
func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
signer input.Signer, commitTx *wire.MsgTx, stateNum uint64, signer input.Signer, commitTx *wire.MsgTx, stateNum uint64,
leafStore fn.Option[AuxLeafStore]) (*LocalForceCloseSummary, error) { leafStore fn.Option[AuxLeafStore],
auxResolver fn.Option[AuxContractResolver]) (*LocalForceCloseSummary,
error) {
// Re-derive the original pkScript for to-self output within the // Re-derive the original pkScript for to-self output within the
// commitment transaction. We'll need this to find the corresponding // commitment transaction. We'll need this to find the corresponding
@@ -7655,6 +7782,35 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
return nil, err return nil, err
} }
} }
// At this point, we'll check to see if we need any extra
// resolution data for this output.
resolveBlob := fn.MapOptionZ(
auxResolver,
func(a AuxContractResolver) fn.Result[tlv.Blob] {
//nolint:lll
return a.ResolveContract(ResolutionReq{
ChanPoint: chanState.FundingOutpoint,
ShortChanID: chanState.ShortChanID(),
Initiator: chanState.IsInitiator,
CommitBlob: chanState.LocalCommitment.CustomBlob,
FundingBlob: chanState.CustomBlob,
Type: input.TaprootLocalCommitSpend,
CloseType: LocalForceClose,
CommitTx: commitTx,
ContractPoint: commitResolution.SelfOutPoint,
SignDesc: commitResolution.SelfOutputSignDesc,
KeyRing: keyRing,
CsvDelay: csvTimeout,
CommitFee: chanState.LocalCommitment.CommitFee,
})
},
)
if err := resolveBlob.Err(); err != nil {
return nil, fmt.Errorf("unable to aux resolve: %w", err)
}
commitResolution.ResolutionBlob = resolveBlob.Option()
} }
// Once the delay output has been found (if it exists), then we'll also // Once the delay output has been found (if it exists), then we'll also
+10
View File
@@ -6043,6 +6043,7 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) {
aliceChannel.channelState.RemoteCommitment, aliceChannel.channelState.RemoteCommitment,
aliceChannel.channelState.RemoteCurrentRevocation, aliceChannel.channelState.RemoteCurrentRevocation,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}), fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
fn.Some[AuxContractResolver](&MockAuxContractResolver{}),
) )
require.NoError(t, err, "unable to create alice close summary") require.NoError(t, err, "unable to create alice close summary")
@@ -6193,6 +6194,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) {
aliceChannel.channelState.RemoteCommitment, aliceChannel.channelState.RemoteCommitment,
aliceChannel.channelState.RemoteCurrentRevocation, aliceChannel.channelState.RemoteCurrentRevocation,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}), fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
fn.Some[AuxContractResolver](&MockAuxContractResolver{}),
) )
require.NoError(t, err, "unable to create alice close summary") require.NoError(t, err, "unable to create alice close summary")
@@ -6211,6 +6213,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) {
aliceRemoteChainTip.Commitment, aliceRemoteChainTip.Commitment,
aliceChannel.channelState.RemoteNextRevocation, aliceChannel.channelState.RemoteNextRevocation,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}), fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
fn.Some[AuxContractResolver](&MockAuxContractResolver{}),
) )
require.NoError(t, err, "unable to create alice close summary") require.NoError(t, err, "unable to create alice close summary")
@@ -7092,6 +7095,7 @@ func TestNewBreachRetributionSkipsDustHtlcs(t *testing.T) {
breachRet, err := NewBreachRetribution( breachRet, err := NewBreachRetribution(
aliceChannel.channelState, revokedStateNum, 100, breachTx, aliceChannel.channelState, revokedStateNum, 100, breachTx,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}), fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
fn.Some[AuxContractResolver](&MockAuxContractResolver{}),
) )
require.NoError(t, err, "unable to create breach retribution") require.NoError(t, err, "unable to create breach retribution")
@@ -10653,6 +10657,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) {
_, err = NewBreachRetribution( _, err = NewBreachRetribution(
aliceChannel.channelState, stateNum, breachHeight, breachTx, aliceChannel.channelState, stateNum, breachHeight, breachTx,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}), fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
fn.Some[AuxContractResolver](&MockAuxContractResolver{}),
) )
require.ErrorIs(t, err, channeldb.ErrNoPastDeltas) require.ErrorIs(t, err, channeldb.ErrNoPastDeltas)
@@ -10661,6 +10666,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) {
_, err = NewBreachRetribution( _, err = NewBreachRetribution(
aliceChannel.channelState, stateNum, breachHeight, nil, aliceChannel.channelState, stateNum, breachHeight, nil,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}), fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
fn.Some[AuxContractResolver](&MockAuxContractResolver{}),
) )
require.ErrorIs(t, err, channeldb.ErrNoPastDeltas) require.ErrorIs(t, err, channeldb.ErrNoPastDeltas)
@@ -10707,6 +10713,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) {
br, err := NewBreachRetribution( br, err := NewBreachRetribution(
aliceChannel.channelState, stateNum, breachHeight, breachTx, aliceChannel.channelState, stateNum, breachHeight, breachTx,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}), fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
fn.Some[AuxContractResolver](&MockAuxContractResolver{}),
) )
require.NoError(t, err) require.NoError(t, err)
@@ -10719,6 +10726,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) {
br, err = NewBreachRetribution( br, err = NewBreachRetribution(
aliceChannel.channelState, stateNum, breachHeight, nil, aliceChannel.channelState, stateNum, breachHeight, nil,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}), fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
fn.Some[AuxContractResolver](&MockAuxContractResolver{}),
) )
require.NoError(t, err) require.NoError(t, err)
assertRetribution(br, 1, 0) assertRetribution(br, 1, 0)
@@ -10728,6 +10736,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) {
_, err = NewBreachRetribution( _, err = NewBreachRetribution(
aliceChannel.channelState, stateNum+1, breachHeight, breachTx, aliceChannel.channelState, stateNum+1, breachHeight, breachTx,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}), fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
fn.Some[AuxContractResolver](&MockAuxContractResolver{}),
) )
require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound) require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound)
@@ -10736,6 +10745,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) {
_, err = NewBreachRetribution( _, err = NewBreachRetribution(
aliceChannel.channelState, stateNum+1, breachHeight, nil, aliceChannel.channelState, stateNum+1, breachHeight, nil,
fn.Some[AuxLeafStore](&MockAuxLeafStore{}), fn.Some[AuxLeafStore](&MockAuxLeafStore{}),
fn.Some[AuxContractResolver](&MockAuxContractResolver{}),
) )
require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound) require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound)
} }
+63
View File
@@ -11,6 +11,7 @@ import (
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/btcutil/hdkeychain"
"github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
@@ -599,6 +600,68 @@ type MessageSigner interface {
doubleHash bool) (*ecdsa.Signature, error) doubleHash bool) (*ecdsa.Signature, error)
} }
// AddrWithKey wraps a normal addr, but also includes the internal key for the
// delivery addr if known.
type AddrWithKey struct {
lnwire.DeliveryAddress
InternalKey fn.Option[keychain.KeyDescriptor]
// TODO(roasbeef): consolidate w/ instance in chan closer
}
// InternalKeyForAddr returns the internal key associated with a taproot
// address.
func InternalKeyForAddr(wallet WalletController, netParams *chaincfg.Params,
deliveryScript []byte) (fn.Option[keychain.KeyDescriptor], error) {
none := fn.None[keychain.KeyDescriptor]()
pkScript, err := txscript.ParsePkScript(deliveryScript)
if err != nil {
return none, err
}
addr, err := pkScript.Address(netParams)
if err != nil {
return none, err
}
// If it's not a taproot address, we don't require to know the internal
// key in the first place. So we don't return an error here, but also no
// internal key.
_, isTaproot := addr.(*btcutil.AddressTaproot)
if !isTaproot {
return none, nil
}
walletAddr, err := wallet.AddressInfo(addr)
if err != nil {
return none, err
}
// No wallet addr. No error, but we'll return an nil error value here,
// as callers can use the .Option() method to get an option value.
if walletAddr == nil {
return none, nil
}
pubKeyAddr, ok := walletAddr.(waddrmgr.ManagedPubKeyAddress)
if !ok {
return none, fmt.Errorf("expected pubkey addr, got %T",
pubKeyAddr)
}
_, derivationPath, _ := pubKeyAddr.DerivationInfo()
return fn.Some[keychain.KeyDescriptor](keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{
Family: keychain.KeyFamily(derivationPath.Account),
Index: derivationPath.Index,
},
PubKey: pubKeyAddr.PubKey(),
}), nil
}
// WalletDriver represents a "driver" for a particular concrete // WalletDriver represents a "driver" for a particular concrete
// WalletController implementation. A driver is identified by a globally unique // WalletController implementation. A driver is identified by a globally unique
// string identifier along with a 'New()' method which is responsible for // string identifier along with a 'New()' method which is responsible for
+11
View File
@@ -493,3 +493,14 @@ func (a *MockAuxSigner) VerifySecondLevelSigs(chanState AuxChanState,
return args.Error(0) return args.Error(0)
} }
type MockAuxContractResolver struct{}
// ResolveContract is called to resolve a contract that needs
// additional information to resolve properly. If no extra information
// is required, a nil Result error is returned.
func (*MockAuxContractResolver) ResolveContract(
ResolutionReq) fn.Result[tlv.Blob] {
return fn.Ok[tlv.Blob](nil)
}
+28 -5
View File
@@ -2,6 +2,7 @@ package lnwallet
import ( import (
"errors" "errors"
"fmt"
"net" "net"
"sync" "sync"
@@ -50,6 +51,11 @@ const (
// channels that use a musig2 funding output and the tapscript tree // channels that use a musig2 funding output and the tapscript tree
// where relevant for the commitment transaction pk scripts. // where relevant for the commitment transaction pk scripts.
CommitmentTypeSimpleTaproot CommitmentTypeSimpleTaproot
// CommitmentTypeSimpleTaprootOverlay builds on the existing
// CommitmentTypeSimpleTaproot type but layers on a special overlay
// protocol.
CommitmentTypeSimpleTaprootOverlay
) )
// HasStaticRemoteKey returns whether the commitment type supports remote // HasStaticRemoteKey returns whether the commitment type supports remote
@@ -59,8 +65,11 @@ func (c CommitmentType) HasStaticRemoteKey() bool {
case CommitmentTypeTweakless, case CommitmentTypeTweakless,
CommitmentTypeAnchorsZeroFeeHtlcTx, CommitmentTypeAnchorsZeroFeeHtlcTx,
CommitmentTypeScriptEnforcedLease, CommitmentTypeScriptEnforcedLease,
CommitmentTypeSimpleTaproot: CommitmentTypeSimpleTaproot,
CommitmentTypeSimpleTaprootOverlay:
return true return true
default: default:
return false return false
} }
@@ -71,8 +80,11 @@ func (c CommitmentType) HasAnchors() bool {
switch c { switch c {
case CommitmentTypeAnchorsZeroFeeHtlcTx, case CommitmentTypeAnchorsZeroFeeHtlcTx,
CommitmentTypeScriptEnforcedLease, CommitmentTypeScriptEnforcedLease,
CommitmentTypeSimpleTaproot: CommitmentTypeSimpleTaproot,
CommitmentTypeSimpleTaprootOverlay:
return true return true
default: default:
return false return false
} }
@@ -80,7 +92,8 @@ func (c CommitmentType) HasAnchors() bool {
// IsTaproot returns true if the channel type is a taproot channel. // IsTaproot returns true if the channel type is a taproot channel.
func (c CommitmentType) IsTaproot() bool { func (c CommitmentType) IsTaproot() bool {
return c == CommitmentTypeSimpleTaproot return c == CommitmentTypeSimpleTaproot ||
c == CommitmentTypeSimpleTaprootOverlay
} }
// String returns the name of the CommitmentType. // String returns the name of the CommitmentType.
@@ -96,6 +109,8 @@ func (c CommitmentType) String() string {
return "script-enforced-lease" return "script-enforced-lease"
case CommitmentTypeSimpleTaproot: case CommitmentTypeSimpleTaproot:
return "simple-taproot" return "simple-taproot"
case CommitmentTypeSimpleTaprootOverlay:
return "simple-taproot-overlay"
default: default:
return "invalid" return "invalid"
} }
@@ -424,7 +439,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
chanType |= channeldb.FrozenBit chanType |= channeldb.FrozenBit
} }
if req.CommitType == CommitmentTypeSimpleTaproot { if req.CommitType.IsTaproot() {
chanType |= channeldb.SimpleTaprootFeatureBit chanType |= channeldb.SimpleTaprootFeatureBit
} }
@@ -440,7 +455,15 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
chanType |= channeldb.ScidAliasFeatureBit chanType |= channeldb.ScidAliasFeatureBit
} }
if req.TapscriptRoot.IsSome() { taprootOverlay := req.CommitType == CommitmentTypeSimpleTaprootOverlay
switch {
case taprootOverlay && req.TapscriptRoot.IsNone():
fallthrough
case !taprootOverlay && req.TapscriptRoot.IsSome():
return nil, fmt.Errorf("taproot overlay chans must be set " +
"with tapscript root")
case taprootOverlay && req.TapscriptRoot.IsSome():
chanType |= channeldb.TapscriptRootBit chanType |= channeldb.TapscriptRootBit
} }
+1 -1
View File
@@ -983,7 +983,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg
TaprootPubkey, true, DefaultAccountName, TaprootPubkey, true, DefaultAccountName,
) )
}, },
Musig2: req.CommitType == CommitmentTypeSimpleTaproot, Musig2: req.CommitType.IsTaproot(),
} }
fundingIntent, err = req.ChanFunder.ProvisionChannel( fundingIntent, err = req.ChanFunder.ProvisionChannel(
fundingReq, fundingReq,
+10
View File
@@ -273,6 +273,14 @@ const (
// a BOLT 11 invoice. // a BOLT 11 invoice.
Bolt11BlindedPathsOptional = 263 Bolt11BlindedPathsOptional = 263
// SimpleTaprootOverlayChansRequired is a required bit that indicates
// support for the special custom taproot overlay channel.
SimpleTaprootOverlayChansOptional = 2025
// SimpleTaprootOverlayChansRequired is a required bit that indicates
// support for the special custom taproot overlay channel.
SimpleTaprootOverlayChansRequired = 2026
// MaxBolt11Feature is the maximum feature bit value allowed in bolt 11 // MaxBolt11Feature is the maximum feature bit value allowed in bolt 11
// invoices. // invoices.
// //
@@ -339,6 +347,8 @@ var Features = map[FeatureBit]string{
SimpleTaprootChannelsOptionalFinal: "simple-taproot-chans", SimpleTaprootChannelsOptionalFinal: "simple-taproot-chans",
SimpleTaprootChannelsRequiredStaging: "simple-taproot-chans-x", SimpleTaprootChannelsRequiredStaging: "simple-taproot-chans-x",
SimpleTaprootChannelsOptionalStaging: "simple-taproot-chans-x", SimpleTaprootChannelsOptionalStaging: "simple-taproot-chans-x",
SimpleTaprootOverlayChansOptional: "taproot-overlay-chans",
SimpleTaprootOverlayChansRequired: "taproot-overlay-chans",
Bolt11BlindedPathsOptional: "bolt-11-blinded-paths", Bolt11BlindedPathsOptional: "bolt-11-blinded-paths",
Bolt11BlindedPathsRequired: "bolt-11-blinded-paths", Bolt11BlindedPathsRequired: "bolt-11-blinded-paths",
} }
+19 -13
View File
@@ -1920,22 +1920,28 @@ func TestLightningWireProtocol(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
var config *quick.Config t.Run(test.msgType.String(), func(t *testing.T) {
var config *quick.Config
// If the type defined is within the custom type gen map above, // If the type defined is within the custom type gen
// then we'll modify the default config to use this Value // map above, then we'll modify the default config to
// function that knows how to generate the proper types. // use this Value function that knows how to generate
if valueGen, ok := customTypeGen[test.msgType]; ok { // the proper types.
config = &quick.Config{ if valueGen, ok := customTypeGen[test.msgType]; ok {
Values: valueGen, config = &quick.Config{
Values: valueGen,
}
} }
}
t.Logf("Running fuzz tests for msgType=%v", test.msgType) t.Logf("Running fuzz tests for msgType=%v",
if err := quick.Check(test.scenario, config); err != nil { test.msgType)
t.Fatalf("fuzz checks for msg=%v failed: %v",
test.msgType, err) err := quick.Check(test.scenario, config)
} if err != nil {
t.Fatalf("fuzz checks for msg=%v failed: %v",
test.msgType, err)
}
})
} }
} }
+34 -60
View File
@@ -13,13 +13,11 @@ import (
"time" "time"
"github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/connmgr" "github.com/btcsuite/btcd/connmgr"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btclog" "github.com/btcsuite/btclog"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/buffer" "github.com/lightningnetwork/lnd/buffer"
"github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/build"
@@ -37,6 +35,7 @@ import (
"github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnutils"
@@ -397,6 +396,10 @@ type Config struct {
// leaves for certain custom channel types. // leaves for certain custom channel types.
AuxSigner fn.Option[lnwallet.AuxSigner] AuxSigner fn.Option[lnwallet.AuxSigner]
// AuxResolver is an optional interface that can be used to modify the
// way contracts are resolved.
AuxResolver fn.Option[lnwallet.AuxContractResolver]
// PongBuf is a slice we'll reuse instead of allocating memory on the // PongBuf is a slice we'll reuse instead of allocating memory on the
// heap. Since only reads will occur and no writes, there is no need // heap. Since only reads will occur and no writes, there is no need
// for any synchronization primitives. As a result, it's safe to share // for any synchronization primitives. As a result, it's safe to share
@@ -902,71 +905,31 @@ func (p *Brontide) QuitSignal() <-chan struct{} {
return p.quit return p.quit
} }
// internalKeyForAddr returns the internal key associated with a taproot
// address.
func internalKeyForAddr(wallet *lnwallet.LightningWallet,
deliveryScript []byte) (fn.Option[btcec.PublicKey], error) {
none := fn.None[btcec.PublicKey]()
pkScript, err := txscript.ParsePkScript(deliveryScript)
if err != nil {
return none, err
}
addr, err := pkScript.Address(&wallet.Cfg.NetParams)
if err != nil {
return none, err
}
// If it's not a taproot address, we don't require to know the internal
// key in the first place. So we don't return an error here, but also no
// internal key.
_, isTaproot := addr.(*btcutil.AddressTaproot)
if !isTaproot {
return none, nil
}
walletAddr, err := wallet.AddressInfo(addr)
if err != nil {
return none, err
}
// If the address isn't known to the wallet, we can't determine the
// internal key.
if walletAddr == nil {
return none, nil
}
pubKeyAddr, ok := walletAddr.(waddrmgr.ManagedPubKeyAddress)
if !ok {
return none, fmt.Errorf("expected pubkey addr, got %T",
pubKeyAddr)
}
return fn.Some(*pubKeyAddr.PubKey()), nil
}
// addrWithInternalKey takes a delivery script, then attempts to supplement it // addrWithInternalKey takes a delivery script, then attempts to supplement it
// with information related to the internal key for the addr, but only if it's // with information related to the internal key for the addr, but only if it's
// a taproot addr. // a taproot addr.
func (p *Brontide) addrWithInternalKey( func (p *Brontide) addrWithInternalKey(
deliveryScript []byte) fn.Result[chancloser.DeliveryAddrWithKey] { deliveryScript []byte) (*chancloser.DeliveryAddrWithKey, error) {
// TODO(roasbeef): not compatible with external shutdown addr?
// Currently, custom channels cannot be created with external upfront // Currently, custom channels cannot be created with external upfront
// shutdown addresses, so this shouldn't be an issue. We only require // shutdown addresses, so this shouldn't be an issue. We only require
// the internal key for taproot addresses to be able to provide a non // the internal key for taproot addresses to be able to provide a non
// inclusion proof of any scripts. // inclusion proof of any scripts.
internalKeyDesc, err := lnwallet.InternalKeyForAddr(
internalKey, err := internalKeyForAddr(p.cfg.Wallet, deliveryScript) p.cfg.Wallet, &p.cfg.Wallet.Cfg.NetParams, deliveryScript,
)
if err != nil { if err != nil {
return fn.Err[chancloser.DeliveryAddrWithKey](err) return nil, fmt.Errorf("unable to fetch internal key: %w", err)
} }
return fn.Ok(chancloser.DeliveryAddrWithKey{ return &chancloser.DeliveryAddrWithKey{
DeliveryAddress: deliveryScript, DeliveryAddress: deliveryScript,
InternalKey: internalKey, InternalKey: fn.MapOption(
}) func(desc keychain.KeyDescriptor) btcec.PublicKey {
return *desc.PubKey
},
)(internalKeyDesc),
}, nil
} }
// loadActiveChannels creates indexes within the peer for tracking all active // loadActiveChannels creates indexes within the peer for tracking all active
@@ -1047,6 +1010,14 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) (
p.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { p.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) {
chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s))
}) })
p.cfg.AuxResolver.WhenSome(
func(s lnwallet.AuxContractResolver) {
chanOpts = append(
chanOpts, lnwallet.WithAuxResolver(s),
)
},
)
lnChan, err := lnwallet.NewLightningChannel( lnChan, err := lnwallet.NewLightningChannel(
p.cfg.Signer, dbChan, p.cfg.SigPool, chanOpts..., p.cfg.Signer, dbChan, p.cfg.SigPool, chanOpts...,
) )
@@ -1211,7 +1182,7 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) (
addr, err := p.addrWithInternalKey( addr, err := p.addrWithInternalKey(
info.DeliveryScript.Val, info.DeliveryScript.Val,
).Unpack() )
if err != nil { if err != nil {
shutdownInfoErr = fmt.Errorf("unable to make "+ shutdownInfoErr = fmt.Errorf("unable to make "+
"delivery addr: %w", err) "delivery addr: %w", err)
@@ -2977,7 +2948,7 @@ func (p *Brontide) fetchActiveChanCloser(chanID lnwire.ChannelID) (
return nil, fmt.Errorf("unable to estimate fee") return nil, fmt.Errorf("unable to estimate fee")
} }
addr, err := p.addrWithInternalKey(deliveryScript).Unpack() addr, err := p.addrWithInternalKey(deliveryScript)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to parse addr: %w", err) return nil, fmt.Errorf("unable to parse addr: %w", err)
} }
@@ -3224,7 +3195,7 @@ func (p *Brontide) restartCoopClose(lnChan *lnwallet.LightningChannel) (
closingParty = lntypes.Local closingParty = lntypes.Local
} }
addr, err := p.addrWithInternalKey(deliveryScript).Unpack() addr, err := p.addrWithInternalKey(deliveryScript)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to parse addr: %w", err) return nil, fmt.Errorf("unable to parse addr: %w", err)
} }
@@ -3256,7 +3227,7 @@ func (p *Brontide) restartCoopClose(lnChan *lnwallet.LightningChannel) (
// createChanCloser constructs a ChanCloser from the passed parameters and is // createChanCloser constructs a ChanCloser from the passed parameters and is
// used to de-duplicate code. // used to de-duplicate code.
func (p *Brontide) createChanCloser(channel *lnwallet.LightningChannel, func (p *Brontide) createChanCloser(channel *lnwallet.LightningChannel,
deliveryScript chancloser.DeliveryAddrWithKey, deliveryScript *chancloser.DeliveryAddrWithKey,
fee chainfee.SatPerKWeight, req *htlcswitch.ChanClose, fee chainfee.SatPerKWeight, req *htlcswitch.ChanClose,
closer lntypes.ChannelParty) (*chancloser.ChanCloser, error) { closer lntypes.ChannelParty) (*chancloser.ChanCloser, error) {
@@ -3291,7 +3262,7 @@ func (p *Brontide) createChanCloser(channel *lnwallet.LightningChannel,
ChainParams: &p.cfg.Wallet.Cfg.NetParams, ChainParams: &p.cfg.Wallet.Cfg.NetParams,
Quit: p.quit, Quit: p.quit,
}, },
deliveryScript, *deliveryScript,
fee, fee,
uint32(startingHeight), uint32(startingHeight),
req, req,
@@ -3350,7 +3321,7 @@ func (p *Brontide) handleLocalCloseReq(req *htlcswitch.ChanClose) {
return return
} }
} }
addr, err := p.addrWithInternalKey(deliveryScript).Unpack() addr, err := p.addrWithInternalKey(deliveryScript)
if err != nil { if err != nil {
err = fmt.Errorf("unable to parse addr for channel "+ err = fmt.Errorf("unable to parse addr for channel "+
"%v: %w", req.ChanPoint, err) "%v: %w", req.ChanPoint, err)
@@ -4293,6 +4264,9 @@ func (p *Brontide) addActiveChannel(c *lnpeer.NewChannel) error {
p.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { p.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) {
chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s))
}) })
p.cfg.AuxResolver.WhenSome(func(s lnwallet.AuxContractResolver) {
chanOpts = append(chanOpts, lnwallet.WithAuxResolver(s))
})
// If not already active, we'll add this channel to the set of active // If not already active, we'll add this channel to the set of active
// channels, so we can look it up later easily according to its channel // channels, so we can look it up later easily according to its channel
+3 -1
View File
@@ -47,7 +47,8 @@ type TlvTrafficShaper interface {
// is a custom channel that should be handled by the traffic shaper, the // is a custom channel that should be handled by the traffic shaper, the
// HandleTraffic method should be called first. // HandleTraffic method should be called first.
PaymentBandwidth(htlcBlob, commitmentBlob fn.Option[tlv.Blob], PaymentBandwidth(htlcBlob, commitmentBlob fn.Option[tlv.Blob],
linkBandwidth lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error) linkBandwidth,
htlcAmt lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error)
} }
// AuxHtlcModifier is an interface that allows the sender to modify the outgoing // AuxHtlcModifier is an interface that allows the sender to modify the outgoing
@@ -191,6 +192,7 @@ func (b *bandwidthManager) getBandwidth(cid lnwire.ShortChannelID,
commitmentBlob := link.CommitmentCustomBlob() commitmentBlob := link.CommitmentCustomBlob()
auxBandwidth, err := ts.PaymentBandwidth( auxBandwidth, err := ts.PaymentBandwidth(
b.firstHopBlob, commitmentBlob, linkBandwidth, b.firstHopBlob, commitmentBlob, linkBandwidth,
amount,
) )
if err != nil { if err != nil {
return bandwidthErr(fmt.Errorf("failed to get "+ return bandwidthErr(fmt.Errorf("failed to get "+
+1 -1
View File
@@ -148,7 +148,7 @@ func (*mockTrafficShaper) ShouldHandleTraffic(_ lnwire.ShortChannelID,
// is a custom channel that should be handled by the traffic shaper, the // is a custom channel that should be handled by the traffic shaper, the
// HandleTraffic method should be called first. // HandleTraffic method should be called first.
func (*mockTrafficShaper) PaymentBandwidth(_, _ fn.Option[tlv.Blob], func (*mockTrafficShaper) PaymentBandwidth(_, _ fn.Option[tlv.Blob],
linkBandwidth lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error) { linkBandwidth, _ lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error) {
return linkBandwidth, nil return linkBandwidth, nil
} }
+27
View File
@@ -2309,6 +2309,29 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
*channelType = lnwire.ChannelType(*fv) *channelType = lnwire.ChannelType(*fv)
case lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY:
// If the taproot overlay channel type is being set, then the
// channel MUST be private.
if !in.Private {
return nil, fmt.Errorf("taproot overlay channels " +
"must be private")
}
channelType = new(lnwire.ChannelType)
fv := lnwire.NewRawFeatureVector(
lnwire.SimpleTaprootOverlayChansRequired,
)
if in.ZeroConf {
fv.Set(lnwire.ZeroConfRequired)
}
if in.ScidAlias {
fv.Set(lnwire.ScidAliasRequired)
}
*channelType = lnwire.ChannelType(*fv)
default: default:
return nil, fmt.Errorf("unhandled request channel type %v", return nil, fmt.Errorf("unhandled request channel type %v",
in.CommitmentType) in.CommitmentType)
@@ -4533,6 +4556,9 @@ func rpcCommitmentType(chanType channeldb.ChannelType) lnrpc.CommitmentType {
// first check whether it has anchors, since in that case it would also // first check whether it has anchors, since in that case it would also
// be tweakless. // be tweakless.
switch { switch {
case chanType.HasTapscriptRoot():
return lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY
case chanType.IsTaproot(): case chanType.IsTaproot():
return lnrpc.CommitmentType_SIMPLE_TAPROOT return lnrpc.CommitmentType_SIMPLE_TAPROOT
@@ -4544,6 +4570,7 @@ func rpcCommitmentType(chanType channeldb.ChannelType) lnrpc.CommitmentType {
case chanType.IsTweakless(): case chanType.IsTweakless():
return lnrpc.CommitmentType_STATIC_REMOTE_KEY return lnrpc.CommitmentType_STATIC_REMOTE_KEY
default: default:
return lnrpc.CommitmentType_LEGACY return lnrpc.CommitmentType_LEGACY
+50
View File
@@ -77,3 +77,53 @@ func TestAuxDataParser(t *testing.T) {
require.NotNil(t, resp) require.NotNil(t, resp)
require.Equal(t, []byte{0x00, 0x00}, resp.CustomChannelData) require.Equal(t, []byte{0x00, 0x00}, resp.CustomChannelData)
} }
// TestRpcCommitmentType tests the rpcCommitmentType returns the corect
// commitment type given a channel type.
func TestRpcCommitmentType(t *testing.T) {
tests := []struct {
name string
chanType channeldb.ChannelType
want lnrpc.CommitmentType
}{
{
name: "tapscript overlay",
chanType: channeldb.SimpleTaprootFeatureBit |
channeldb.TapscriptRootBit,
want: lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY,
},
{
name: "simple taproot",
chanType: channeldb.SimpleTaprootFeatureBit,
want: lnrpc.CommitmentType_SIMPLE_TAPROOT,
},
{
name: "lease expiration",
chanType: channeldb.LeaseExpirationBit,
want: lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE,
},
{
name: "anchors",
chanType: channeldb.AnchorOutputsBit,
want: lnrpc.CommitmentType_ANCHORS,
},
{
name: "tweakless",
chanType: channeldb.SingleFunderTweaklessBit,
want: lnrpc.CommitmentType_STATIC_REMOTE_KEY,
},
{
name: "legacy",
chanType: channeldb.SingleFunderBit,
want: lnrpc.CommitmentType_LEGACY,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(
t, tt.want, rpcCommitmentType(tt.chanType),
)
})
}
}
+3
View File
@@ -1324,6 +1324,9 @@
; Set to enable support for the experimental taproot channel type. ; Set to enable support for the experimental taproot channel type.
; protocol.simple-taproot-chans=false ; protocol.simple-taproot-chans=false
; Set to enable support for the experimental taproot overlay channel type.
; protocol.simple-taproot-overlay-chans=false
; Set to disable blinded route forwarding. ; Set to disable blinded route forwarding.
; protocol.no-route-blinding=false ; protocol.no-route-blinding=false
+76 -18
View File
@@ -18,6 +18,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/connmgr" "github.com/btcsuite/btcd/connmgr"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
@@ -521,6 +522,8 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
var serializedPubKey [33]byte var serializedPubKey [33]byte
copy(serializedPubKey[:], nodeKeyDesc.PubKey.SerializeCompressed()) copy(serializedPubKey[:], nodeKeyDesc.PubKey.SerializeCompressed())
netParams := cfg.ActiveNetParams.Params
// Initialize the sphinx router. // Initialize the sphinx router.
replayLog := htlcswitch.NewDecayedLog( replayLog := htlcswitch.NewDecayedLog(
dbs.DecayedLogDB, cc.ChainNotifier, dbs.DecayedLogDB, cc.ChainNotifier,
@@ -545,6 +548,15 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
readBufferPool, cfg.Workers.Read, pool.DefaultWorkerTimeout, readBufferPool, cfg.Workers.Read, pool.DefaultWorkerTimeout,
) )
// If the taproot overlay flag is set, but we don't have an aux funding
// controller, then we'll exit as this is incompatible.
if cfg.ProtocolOptions.TaprootOverlayChans &&
implCfg.AuxFundingController.IsNone() {
return nil, fmt.Errorf("taproot overlay flag set, but not " +
"aux controllers")
}
//nolint:lll //nolint:lll
featureMgr, err := feature.NewManager(feature.Config{ featureMgr, err := feature.NewManager(feature.Config{
NoTLVOnion: cfg.ProtocolOptions.LegacyOnion(), NoTLVOnion: cfg.ProtocolOptions.LegacyOnion(),
@@ -558,6 +570,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
NoAnySegwit: cfg.ProtocolOptions.NoAnySegwit(), NoAnySegwit: cfg.ProtocolOptions.NoAnySegwit(),
CustomFeatures: cfg.ProtocolOptions.CustomFeatures(), CustomFeatures: cfg.ProtocolOptions.CustomFeatures(),
NoTaprootChans: !cfg.ProtocolOptions.TaprootChans, NoTaprootChans: !cfg.ProtocolOptions.TaprootChans,
NoTaprootOverlay: !cfg.ProtocolOptions.TaprootOverlayChans,
NoRouteBlinding: cfg.ProtocolOptions.NoRouteBlinding(), NoRouteBlinding: cfg.ProtocolOptions.NoRouteBlinding(),
}) })
if err != nil { if err != nil {
@@ -1123,18 +1136,22 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
aggregator := sweep.NewBudgetAggregator( aggregator := sweep.NewBudgetAggregator(
cc.FeeEstimator, sweep.DefaultMaxInputsPerTx, cc.FeeEstimator, sweep.DefaultMaxInputsPerTx,
s.implCfg.AuxSweeper,
) )
s.txPublisher = sweep.NewTxPublisher(sweep.TxPublisherConfig{ s.txPublisher = sweep.NewTxPublisher(sweep.TxPublisherConfig{
Signer: cc.Wallet.Cfg.Signer, Signer: cc.Wallet.Cfg.Signer,
Wallet: cc.Wallet, Wallet: cc.Wallet,
Estimator: cc.FeeEstimator, Estimator: cc.FeeEstimator,
Notifier: cc.ChainNotifier, Notifier: cc.ChainNotifier,
AuxSweeper: s.implCfg.AuxSweeper,
}) })
s.sweeper = sweep.New(&sweep.UtxoSweeperConfig{ s.sweeper = sweep.New(&sweep.UtxoSweeperConfig{
FeeEstimator: cc.FeeEstimator, FeeEstimator: cc.FeeEstimator,
GenSweepScript: newSweepPkScriptGen(cc.Wallet), GenSweepScript: newSweepPkScriptGen(
cc.Wallet, s.cfg.ActiveNetParams.Params,
),
Signer: cc.Wallet.Cfg.Signer, Signer: cc.Wallet.Cfg.Signer,
Wallet: newSweeperWallet(cc.Wallet), Wallet: newSweeperWallet(cc.Wallet),
Mempool: cc.MempoolNotifier, Mempool: cc.MempoolNotifier,
@@ -1177,10 +1194,12 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
s.breachArbitrator = contractcourt.NewBreachArbitrator( s.breachArbitrator = contractcourt.NewBreachArbitrator(
&contractcourt.BreachConfig{ &contractcourt.BreachConfig{
CloseLink: closeLink, CloseLink: closeLink,
DB: s.chanStateDB, DB: s.chanStateDB,
Estimator: s.cc.FeeEstimator, Estimator: s.cc.FeeEstimator,
GenSweepScript: newSweepPkScriptGen(cc.Wallet), GenSweepScript: newSweepPkScriptGen(
cc.Wallet, s.cfg.ActiveNetParams.Params,
),
Notifier: cc.ChainNotifier, Notifier: cc.ChainNotifier,
PublishTransaction: cc.Wallet.PublishTransaction, PublishTransaction: cc.Wallet.PublishTransaction,
ContractBreaches: contractBreaches, ContractBreaches: contractBreaches,
@@ -1188,6 +1207,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
Store: contractcourt.NewRetributionStore( Store: contractcourt.NewRetributionStore(
dbs.ChanStateDB, dbs.ChanStateDB,
), ),
AuxSweeper: s.implCfg.AuxSweeper,
}, },
) )
@@ -1196,8 +1216,17 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
ChainHash: *s.cfg.ActiveNetParams.GenesisHash, ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
IncomingBroadcastDelta: lncfg.DefaultIncomingBroadcastDelta, IncomingBroadcastDelta: lncfg.DefaultIncomingBroadcastDelta,
OutgoingBroadcastDelta: lncfg.DefaultOutgoingBroadcastDelta, OutgoingBroadcastDelta: lncfg.DefaultOutgoingBroadcastDelta,
NewSweepAddr: newSweepPkScriptGen(cc.Wallet), NewSweepAddr: func() ([]byte, error) {
PublishTx: cc.Wallet.PublishTransaction, addr, err := newSweepPkScriptGen(
cc.Wallet, netParams,
)().Unpack()
if err != nil {
return nil, err
}
return addr.DeliveryAddress, nil
},
PublishTx: cc.Wallet.PublishTransaction,
DeliverResolutionMsg: func(msgs ...contractcourt.ResolutionMsg) error { DeliverResolutionMsg: func(msgs ...contractcourt.ResolutionMsg) error {
for _, msg := range msgs { for _, msg := range msgs {
err := s.htlcSwitch.ProcessContractResolution(msg) err := s.htlcSwitch.ProcessContractResolution(msg)
@@ -1304,6 +1333,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
}, },
AuxLeafStore: implCfg.AuxLeafStore, AuxLeafStore: implCfg.AuxLeafStore,
AuxSigner: implCfg.AuxSigner, AuxSigner: implCfg.AuxSigner,
AuxResolver: implCfg.AuxContractResolver,
}, dbs.ChanStateDB) }, dbs.ChanStateDB)
// Select the configuration and funding parameters for Bitcoin. // Select the configuration and funding parameters for Bitcoin.
@@ -1553,6 +1583,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
IsSweeperOutpoint: s.sweeper.IsSweeperOutpoint, IsSweeperOutpoint: s.sweeper.IsSweeperOutpoint,
AuxFundingController: implCfg.AuxFundingController, AuxFundingController: implCfg.AuxFundingController,
AuxSigner: implCfg.AuxSigner, AuxSigner: implCfg.AuxSigner,
AuxResolver: implCfg.AuxContractResolver,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@@ -1641,6 +1672,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
br, err := lnwallet.NewBreachRetribution( br, err := lnwallet.NewBreachRetribution(
channel, commitHeight, 0, nil, channel, commitHeight, 0, nil,
implCfg.AuxLeafStore, implCfg.AuxLeafStore,
implCfg.AuxContractResolver,
) )
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@@ -1674,8 +1706,17 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
return s.channelNotifier. return s.channelNotifier.
SubscribeChannelEvents() SubscribeChannelEvents()
}, },
Signer: cc.Wallet.Cfg.Signer, Signer: cc.Wallet.Cfg.Signer,
NewAddress: newSweepPkScriptGen(cc.Wallet), NewAddress: func() ([]byte, error) {
addr, err := newSweepPkScriptGen(
cc.Wallet, netParams,
)().Unpack()
if err != nil {
return nil, err
}
return addr.DeliveryAddress, nil
},
SecretKeyRing: s.cc.KeyRing, SecretKeyRing: s.cc.KeyRing,
Dial: cfg.net.Dial, Dial: cfg.net.Dial,
AuthDial: authDial, AuthDial: authDial,
@@ -4125,6 +4166,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq,
AuxSigner: s.implCfg.AuxSigner, AuxSigner: s.implCfg.AuxSigner,
MsgRouter: s.implCfg.MsgRouter, MsgRouter: s.implCfg.MsgRouter,
AuxChanCloser: s.implCfg.AuxChanCloser, AuxChanCloser: s.implCfg.AuxChanCloser,
AuxResolver: s.implCfg.AuxContractResolver,
} }
copy(pCfg.PubKeyBytes[:], peerAddr.IdentityKey.SerializeCompressed()) copy(pCfg.PubKeyBytes[:], peerAddr.IdentityKey.SerializeCompressed())
@@ -4944,18 +4986,34 @@ func (s *server) SendCustomMessage(peerPub [33]byte, msgType lnwire.MessageType,
// Specifically, the script generated is a version 0, pay-to-witness-pubkey-hash // Specifically, the script generated is a version 0, pay-to-witness-pubkey-hash
// (p2wkh) output. // (p2wkh) output.
func newSweepPkScriptGen( func newSweepPkScriptGen(
wallet lnwallet.WalletController) func() ([]byte, error) { wallet lnwallet.WalletController,
netParams *chaincfg.Params) func() fn.Result[lnwallet.AddrWithKey] {
return func() ([]byte, error) { return func() fn.Result[lnwallet.AddrWithKey] {
sweepAddr, err := wallet.NewAddress( sweepAddr, err := wallet.NewAddress(
lnwallet.TaprootPubkey, false, lnwallet.TaprootPubkey, false,
lnwallet.DefaultAccountName, lnwallet.DefaultAccountName,
) )
if err != nil { if err != nil {
return nil, err return fn.Err[lnwallet.AddrWithKey](err)
} }
return txscript.PayToAddrScript(sweepAddr) addr, err := txscript.PayToAddrScript(sweepAddr)
if err != nil {
return fn.Err[lnwallet.AddrWithKey](err)
}
internalKeyDesc, err := lnwallet.InternalKeyForAddr(
wallet, netParams, addr,
)
if err != nil {
return fn.Err[lnwallet.AddrWithKey](err)
}
return fn.Ok(lnwallet.AddrWithKey{
DeliveryAddress: addr,
InternalKey: internalKeyDesc,
})
} }
} }
+11 -5
View File
@@ -5,6 +5,7 @@ import (
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
@@ -31,6 +32,10 @@ type BudgetAggregator struct {
// maxInputs specifies the maximum number of inputs allowed in a single // maxInputs specifies the maximum number of inputs allowed in a single
// sweep tx. // sweep tx.
maxInputs uint32 maxInputs uint32
// auxSweeper is an optional interface that can be used to modify the
// way sweep transaction are generated.
auxSweeper fn.Option[AuxSweeper]
} }
// Compile-time constraint to ensure BudgetAggregator implements UtxoAggregator. // Compile-time constraint to ensure BudgetAggregator implements UtxoAggregator.
@@ -38,11 +43,12 @@ var _ UtxoAggregator = (*BudgetAggregator)(nil)
// NewBudgetAggregator creates a new instance of a BudgetAggregator. // NewBudgetAggregator creates a new instance of a BudgetAggregator.
func NewBudgetAggregator(estimator chainfee.Estimator, func NewBudgetAggregator(estimator chainfee.Estimator,
maxInputs uint32) *BudgetAggregator { maxInputs uint32, auxSweeper fn.Option[AuxSweeper]) *BudgetAggregator {
return &BudgetAggregator{ return &BudgetAggregator{
estimator: estimator, estimator: estimator,
maxInputs: maxInputs, maxInputs: maxInputs,
auxSweeper: auxSweeper,
} }
} }
@@ -159,7 +165,7 @@ func (b *BudgetAggregator) createInputSets(inputs []SweeperInput,
// Create an InputSet using the max allowed number of inputs. // Create an InputSet using the max allowed number of inputs.
set, err := NewBudgetInputSet( set, err := NewBudgetInputSet(
currentInputs, deadlineHeight, currentInputs, deadlineHeight, b.auxSweeper,
) )
if err != nil { if err != nil {
log.Errorf("unable to create input set: %v", err) log.Errorf("unable to create input set: %v", err)
@@ -173,7 +179,7 @@ func (b *BudgetAggregator) createInputSets(inputs []SweeperInput,
// Create an InputSet from the remaining inputs. // Create an InputSet from the remaining inputs.
if len(remainingInputs) > 0 { if len(remainingInputs) > 0 {
set, err := NewBudgetInputSet( set, err := NewBudgetInputSet(
remainingInputs, deadlineHeight, remainingInputs, deadlineHeight, b.auxSweeper,
) )
if err != nil { if err != nil {
log.Errorf("unable to create input set: %v", err) log.Errorf("unable to create input set: %v", err)
+6 -4
View File
@@ -150,7 +150,7 @@ func TestBudgetAggregatorFilterInputs(t *testing.T) {
// Init the budget aggregator with the mocked estimator and zero max // Init the budget aggregator with the mocked estimator and zero max
// num of inputs. // num of inputs.
b := NewBudgetAggregator(estimator, 0) b := NewBudgetAggregator(estimator, 0, fn.None[AuxSweeper]())
// Call the method under test. // Call the method under test.
result := b.filterInputs(inputs) result := b.filterInputs(inputs)
@@ -214,7 +214,7 @@ func TestBudgetAggregatorSortInputs(t *testing.T) {
} }
// Init the budget aggregator with zero max num of inputs. // Init the budget aggregator with zero max num of inputs.
b := NewBudgetAggregator(nil, 0) b := NewBudgetAggregator(nil, 0, fn.None[AuxSweeper]())
// Call the method under test. // Call the method under test.
result := b.sortInputs(inputs) result := b.sortInputs(inputs)
@@ -279,7 +279,7 @@ func TestBudgetAggregatorCreateInputSets(t *testing.T) {
} }
// Create a budget aggregator with max number of inputs set to 2. // Create a budget aggregator with max number of inputs set to 2.
b := NewBudgetAggregator(nil, 2) b := NewBudgetAggregator(nil, 2, fn.None[AuxSweeper]())
// Create test cases. // Create test cases.
testCases := []struct { testCases := []struct {
@@ -540,7 +540,9 @@ func TestBudgetInputSetClusterInputs(t *testing.T) {
} }
// Create a budget aggregator with a max number of inputs set to 100. // Create a budget aggregator with a max number of inputs set to 100.
b := NewBudgetAggregator(estimator, DefaultMaxInputsPerTx) b := NewBudgetAggregator(
estimator, DefaultMaxInputsPerTx, fn.None[AuxSweeper](),
)
// Call the method under test. // Call the method under test.
result := b.ClusterInputs(inputs) result := b.ClusterInputs(inputs)
+210 -57
View File
@@ -20,6 +20,7 @@ import (
"github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/tlv"
) )
var ( var (
@@ -43,6 +44,19 @@ var (
ErrThirdPartySpent = errors.New("third party spent the output") ErrThirdPartySpent = errors.New("third party spent the output")
) )
var (
// dummyChangePkScript is a dummy tapscript change script that's used
// when we don't need a real address, just something that can be used
// for fee estimation.
dummyChangePkScript = []byte{
0x51, 0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
)
// Bumper defines an interface that can be used by other subsystems for fee // Bumper defines an interface that can be used by other subsystems for fee
// bumping. // bumping.
type Bumper interface { type Bumper interface {
@@ -110,7 +124,7 @@ type BumpRequest struct {
DeadlineHeight int32 DeadlineHeight int32
// DeliveryAddress is the script to send the change output to. // DeliveryAddress is the script to send the change output to.
DeliveryAddress []byte DeliveryAddress lnwallet.AddrWithKey
// MaxFeeRate is the maximum fee rate that can be used for fee bumping. // MaxFeeRate is the maximum fee rate that can be used for fee bumping.
MaxFeeRate chainfee.SatPerKWeight MaxFeeRate chainfee.SatPerKWeight
@@ -118,6 +132,10 @@ type BumpRequest struct {
// StartingFeeRate is an optional parameter that can be used to specify // StartingFeeRate is an optional parameter that can be used to specify
// the initial fee rate to use for the fee function. // the initial fee rate to use for the fee function.
StartingFeeRate fn.Option[chainfee.SatPerKWeight] StartingFeeRate fn.Option[chainfee.SatPerKWeight]
// ExtraTxOut tracks if this bump request has an optional set of extra
// outputs to add to the transaction.
ExtraTxOut fn.Option[SweepOutput]
} }
// MaxFeeRateAllowed returns the maximum fee rate allowed for the given // MaxFeeRateAllowed returns the maximum fee rate allowed for the given
@@ -125,9 +143,34 @@ type BumpRequest struct {
// compares it with the specified MaxFeeRate, and returns the smaller of the // compares it with the specified MaxFeeRate, and returns the smaller of the
// two. // two.
func (r *BumpRequest) MaxFeeRateAllowed() (chainfee.SatPerKWeight, error) { func (r *BumpRequest) MaxFeeRateAllowed() (chainfee.SatPerKWeight, error) {
// We'll want to know if we have any blobs, as we need to factor this
// into the max fee rate for this bump request.
hasBlobs := fn.Any(func(i input.Input) bool {
return fn.MapOptionZ(
i.ResolutionBlob(), func(b tlv.Blob) bool {
return len(b) > 0
},
)
}, r.Inputs)
sweepAddrs := [][]byte{
r.DeliveryAddress.DeliveryAddress,
}
// If we have blobs, then we'll add an extra sweep addr for the size
// estimate below. We know that these blobs will also always be based on
// p2tr addrs.
if hasBlobs {
// We need to pass in a real address, so we'll use a dummy
// tapscript change script that's used elsewhere for tests.
sweepAddrs = append(sweepAddrs, dummyChangePkScript)
}
// Get the size of the sweep tx, which will be used to calculate the // Get the size of the sweep tx, which will be used to calculate the
// budget fee rate. // budget fee rate.
size, err := calcSweepTxWeight(r.Inputs, r.DeliveryAddress) size, err := calcSweepTxWeight(
r.Inputs, sweepAddrs,
)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -155,7 +198,7 @@ func (r *BumpRequest) MaxFeeRateAllowed() (chainfee.SatPerKWeight, error) {
// calcSweepTxWeight calculates the weight of the sweep tx. It assumes a // calcSweepTxWeight calculates the weight of the sweep tx. It assumes a
// sweeping tx always has a single output(change). // sweeping tx always has a single output(change).
func calcSweepTxWeight(inputs []input.Input, func calcSweepTxWeight(inputs []input.Input,
outputPkScript []byte) (lntypes.WeightUnit, error) { outputPkScript [][]byte) (lntypes.WeightUnit, error) {
// Use a const fee rate as we only use the weight estimator to // Use a const fee rate as we only use the weight estimator to
// calculate the size. // calculate the size.
@@ -248,6 +291,10 @@ type TxPublisherConfig struct {
// Notifier is used to monitor the confirmation status of the tx. // Notifier is used to monitor the confirmation status of the tx.
Notifier chainntnfs.ChainNotifier Notifier chainntnfs.ChainNotifier
// AuxSweeper is an optional interface that can be used to modify the
// way sweep transaction are generated.
AuxSweeper fn.Option[AuxSweeper]
} }
// TxPublisher is an implementation of the Bumper interface. It utilizes the // TxPublisher is an implementation of the Bumper interface. It utilizes the
@@ -401,16 +448,18 @@ func (t *TxPublisher) createRBFCompliantTx(req *BumpRequest,
for { for {
// Create a new tx with the given fee rate and check its // Create a new tx with the given fee rate and check its
// mempool acceptance. // mempool acceptance.
tx, fee, err := t.createAndCheckTx(req, f) sweepCtx, err := t.createAndCheckTx(req, f)
switch { switch {
case err == nil: case err == nil:
// The tx is valid, return the request ID. // The tx is valid, return the request ID.
requestID := t.storeRecord(tx, req, f, fee) requestID := t.storeRecord(
sweepCtx.tx, req, f, sweepCtx.fee,
)
log.Infof("Created tx %v for %v inputs: feerate=%v, "+ log.Infof("Created tx %v for %v inputs: feerate=%v, "+
"fee=%v, inputs=%v", tx.TxHash(), "fee=%v, inputs=%v", sweepCtx.tx.TxHash(),
len(req.Inputs), f.FeeRate(), fee, len(req.Inputs), f.FeeRate(), sweepCtx.fee,
inputTypeSummary(req.Inputs)) inputTypeSummary(req.Inputs))
return requestID, nil return requestID, nil
@@ -421,8 +470,8 @@ func (t *TxPublisher) createRBFCompliantTx(req *BumpRequest,
// We should at least start with a feerate above the // We should at least start with a feerate above the
// mempool min feerate, so if we get this error, it // mempool min feerate, so if we get this error, it
// means something is wrong earlier in the pipeline. // means something is wrong earlier in the pipeline.
log.Errorf("Current fee=%v, feerate=%v, %v", fee, log.Errorf("Current fee=%v, feerate=%v, %v",
f.FeeRate(), err) sweepCtx.fee, f.FeeRate(), err)
fallthrough fallthrough
@@ -434,8 +483,8 @@ func (t *TxPublisher) createRBFCompliantTx(req *BumpRequest,
// increased or maxed out. // increased or maxed out.
for !increased { for !increased {
log.Debugf("Increasing fee for next round, "+ log.Debugf("Increasing fee for next round, "+
"current fee=%v, feerate=%v", fee, "current fee=%v, feerate=%v",
f.FeeRate()) sweepCtx.fee, f.FeeRate())
// If the fee function tells us that we have // If the fee function tells us that we have
// used up the budget, we will return an error // used up the budget, we will return an error
@@ -484,30 +533,34 @@ func (t *TxPublisher) storeRecord(tx *wire.MsgTx, req *BumpRequest,
// script, and the fee rate. In addition, it validates the tx's mempool // script, and the fee rate. In addition, it validates the tx's mempool
// acceptance before returning a tx that can be published directly, along with // acceptance before returning a tx that can be published directly, along with
// its fee. // its fee.
func (t *TxPublisher) createAndCheckTx(req *BumpRequest, f FeeFunction) ( func (t *TxPublisher) createAndCheckTx(req *BumpRequest,
*wire.MsgTx, btcutil.Amount, error) { f FeeFunction) (*sweepTxCtx, error) {
// Create the sweep tx with max fee rate of 0 as the fee function // Create the sweep tx with max fee rate of 0 as the fee function
// guarantees the fee rate used here won't exceed the max fee rate. // guarantees the fee rate used here won't exceed the max fee rate.
tx, fee, err := t.createSweepTx( sweepCtx, err := t.createSweepTx(
req.Inputs, req.DeliveryAddress, f.FeeRate(), req.Inputs, req.DeliveryAddress, f.FeeRate(),
) )
if err != nil { if err != nil {
return nil, fee, fmt.Errorf("create sweep tx: %w", err) return sweepCtx, fmt.Errorf("create sweep tx: %w", err)
} }
// Sanity check the budget still covers the fee. // Sanity check the budget still covers the fee.
if fee > req.Budget { if sweepCtx.fee > req.Budget {
return nil, fee, fmt.Errorf("%w: budget=%v, fee=%v", return sweepCtx, fmt.Errorf("%w: budget=%v, fee=%v",
ErrNotEnoughBudget, req.Budget, fee) ErrNotEnoughBudget, req.Budget, sweepCtx.fee)
} }
// If we had an extra txOut, then we'll update the result to include
// it.
req.ExtraTxOut = sweepCtx.extraTxOut
// Validate the tx's mempool acceptance. // Validate the tx's mempool acceptance.
err = t.cfg.Wallet.CheckMempoolAcceptance(tx) err = t.cfg.Wallet.CheckMempoolAcceptance(sweepCtx.tx)
// Exit early if the tx is valid. // Exit early if the tx is valid.
if err == nil { if err == nil {
return tx, fee, nil return sweepCtx, nil
} }
// Print an error log if the chain backend doesn't support the mempool // Print an error log if the chain backend doesn't support the mempool
@@ -515,18 +568,18 @@ func (t *TxPublisher) createAndCheckTx(req *BumpRequest, f FeeFunction) (
if errors.Is(err, rpcclient.ErrBackendVersion) { if errors.Is(err, rpcclient.ErrBackendVersion) {
log.Errorf("TestMempoolAccept not supported by backend, " + log.Errorf("TestMempoolAccept not supported by backend, " +
"consider upgrading it to a newer version") "consider upgrading it to a newer version")
return tx, fee, nil return sweepCtx, nil
} }
// We are running on a backend that doesn't implement the RPC // We are running on a backend that doesn't implement the RPC
// testmempoolaccept, eg, neutrino, so we'll skip the check. // testmempoolaccept, eg, neutrino, so we'll skip the check.
if errors.Is(err, chain.ErrUnimplemented) { if errors.Is(err, chain.ErrUnimplemented) {
log.Debug("Skipped testmempoolaccept due to not implemented") log.Debug("Skipped testmempoolaccept due to not implemented")
return tx, fee, nil return sweepCtx, nil
} }
return nil, fee, fmt.Errorf("tx=%v failed mempool check: %w", return sweepCtx, fmt.Errorf("tx=%v failed mempool check: %w",
tx.TxHash(), err) sweepCtx.tx.TxHash(), err)
} }
// broadcast takes a monitored tx and publishes it to the network. Prior to the // broadcast takes a monitored tx and publishes it to the network. Prior to the
@@ -547,6 +600,15 @@ func (t *TxPublisher) broadcast(requestID uint64) (*BumpResult, error) {
log.Debugf("Publishing sweep tx %v, num_inputs=%v, height=%v", log.Debugf("Publishing sweep tx %v, num_inputs=%v, height=%v",
txid, len(tx.TxIn), t.currentHeight.Load()) txid, len(tx.TxIn), t.currentHeight.Load())
// Before we go to broadcast, we'll notify the aux sweeper, if it's
// present of this new broadcast attempt.
err := fn.MapOptionZ(t.cfg.AuxSweeper, func(aux AuxSweeper) error {
return aux.NotifyBroadcast(record.req, tx, record.fee)
})
if err != nil {
return nil, fmt.Errorf("unable to notify aux sweeper: %w", err)
}
// Set the event, and change it to TxFailed if the wallet fails to // Set the event, and change it to TxFailed if the wallet fails to
// publish it. // publish it.
event := TxPublished event := TxPublished
@@ -554,7 +616,7 @@ func (t *TxPublisher) broadcast(requestID uint64) (*BumpResult, error) {
// Publish the sweeping tx with customized label. If the publish fails, // Publish the sweeping tx with customized label. If the publish fails,
// this error will be saved in the `BumpResult` and it will be removed // this error will be saved in the `BumpResult` and it will be removed
// from being monitored. // from being monitored.
err := t.cfg.Wallet.PublishTransaction( err = t.cfg.Wallet.PublishTransaction(
tx, labels.MakeLabel(labels.LabelTypeSweepTransaction, nil), tx, labels.MakeLabel(labels.LabelTypeSweepTransaction, nil),
) )
if err != nil { if err != nil {
@@ -933,7 +995,7 @@ func (t *TxPublisher) createAndPublishTx(requestID uint64,
// NOTE: The fee function is expected to have increased its returned // NOTE: The fee function is expected to have increased its returned
// fee rate after calling the SkipFeeBump method. So we can use it // fee rate after calling the SkipFeeBump method. So we can use it
// directly here. // directly here.
tx, fee, err := t.createAndCheckTx(r.req, r.feeFunction) sweepCtx, err := t.createAndCheckTx(r.req, r.feeFunction)
// If the error is fee related, we will return no error and let the fee // If the error is fee related, we will return no error and let the fee
// bumper retry it at next block. // bumper retry it at next block.
@@ -980,17 +1042,17 @@ func (t *TxPublisher) createAndPublishTx(requestID uint64,
// The tx has been created without any errors, we now register a new // The tx has been created without any errors, we now register a new
// record by overwriting the same requestID. // record by overwriting the same requestID.
t.records.Store(requestID, &monitorRecord{ t.records.Store(requestID, &monitorRecord{
tx: tx, tx: sweepCtx.tx,
req: r.req, req: r.req,
feeFunction: r.feeFunction, feeFunction: r.feeFunction,
fee: fee, fee: sweepCtx.fee,
}) })
// Attempt to broadcast this new tx. // Attempt to broadcast this new tx.
result, err := t.broadcast(requestID) result, err := t.broadcast(requestID)
if err != nil { if err != nil {
log.Infof("Failed to broadcast replacement tx %v: %v", log.Infof("Failed to broadcast replacement tx %v: %v",
tx.TxHash(), err) sweepCtx.tx.TxHash(), err)
return fn.None[BumpResult]() return fn.None[BumpResult]()
} }
@@ -1016,7 +1078,8 @@ func (t *TxPublisher) createAndPublishTx(requestID uint64,
return fn.Some(*result) return fn.Some(*result)
} }
log.Infof("Replaced tx=%v with new tx=%v", oldTx.TxHash(), tx.TxHash()) log.Infof("Replaced tx=%v with new tx=%v", oldTx.TxHash(),
sweepCtx.tx.TxHash())
// Otherwise, it's a successful RBF, set the event and return. // Otherwise, it's a successful RBF, set the event and return.
result.Event = TxReplaced result.Event = TxReplaced
@@ -1129,17 +1192,28 @@ func calcCurrentConfTarget(currentHeight, deadline int32) uint32 {
return confTarget return confTarget
} }
// sweepTxCtx houses a sweep transaction with additional context.
type sweepTxCtx struct {
tx *wire.MsgTx
fee btcutil.Amount
extraTxOut fn.Option[SweepOutput]
}
// createSweepTx creates a sweeping tx based on the given inputs, change // createSweepTx creates a sweeping tx based on the given inputs, change
// address and fee rate. // address and fee rate.
func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte, func (t *TxPublisher) createSweepTx(inputs []input.Input,
feeRate chainfee.SatPerKWeight) (*wire.MsgTx, btcutil.Amount, error) { changePkScript lnwallet.AddrWithKey,
feeRate chainfee.SatPerKWeight) (*sweepTxCtx, error) {
// Validate and calculate the fee and change amount. // Validate and calculate the fee and change amount.
txFee, changeAmtOpt, locktimeOpt, err := prepareSweepTx( txFee, changeOutputsOpt, locktimeOpt, err := prepareSweepTx(
inputs, changePkScript, feeRate, t.currentHeight.Load(), inputs, changePkScript, feeRate, t.currentHeight.Load(),
t.cfg.AuxSweeper,
) )
if err != nil { if err != nil {
return nil, 0, err return nil, err
} }
var ( var (
@@ -1182,12 +1256,12 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte,
}) })
} }
// If there's a change amount, add it to the transaction. // If we have change outputs to add, then add it the sweep transaction
changeAmtOpt.WhenSome(func(changeAmt btcutil.Amount) { // here.
sweepTx.AddTxOut(&wire.TxOut{ changeOutputsOpt.WhenSome(func(changeOuts []SweepOutput) {
PkScript: changePkScript, for i := range changeOuts {
Value: int64(changeAmt), sweepTx.AddTxOut(&changeOuts[i].TxOut)
}) }
}) })
// We'll default to using the current block height as locktime, if none // We'll default to using the current block height as locktime, if none
@@ -1196,7 +1270,7 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte,
prevInputFetcher, err := input.MultiPrevOutFetcher(inputs) prevInputFetcher, err := input.MultiPrevOutFetcher(inputs)
if err != nil { if err != nil {
return nil, 0, fmt.Errorf("error creating prev input fetcher "+ return nil, fmt.Errorf("error creating prev input fetcher "+
"for hash cache: %v", err) "for hash cache: %v", err)
} }
hashCache := txscript.NewTxSigHashes(sweepTx, prevInputFetcher) hashCache := txscript.NewTxSigHashes(sweepTx, prevInputFetcher)
@@ -1224,35 +1298,87 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte,
for idx, inp := range idxs { for idx, inp := range idxs {
if err := addInputScript(idx, inp); err != nil { if err := addInputScript(idx, inp); err != nil {
return nil, 0, err return nil, err
} }
} }
log.Debugf("Created sweep tx %v for inputs:\n%v", sweepTx.TxHash(), log.Debugf("Created sweep tx %v for inputs:\n%v", sweepTx.TxHash(),
inputTypeSummary(inputs)) inputTypeSummary(inputs))
return sweepTx, txFee, nil // Try to locate the extra change output, though there might be None.
extraTxOut := fn.MapOption(
func(sweepOuts []SweepOutput) fn.Option[SweepOutput] {
for _, sweepOut := range sweepOuts {
if !sweepOut.IsExtra {
continue
}
// If we sweep outputs of a custom channel, the
// custom leaves in those outputs will be merged
// into a single output, even if we sweep
// multiple outputs (e.g. to_remote and breached
// to_local of a breached channel) at the same
// time. So there will only ever be one extra
// output.
log.Debugf("Sweep produced extra_sweep_out=%v",
lnutils.SpewLogClosure(sweepOut))
return fn.Some(sweepOut)
}
return fn.None[SweepOutput]()
},
)(changeOutputsOpt)
return &sweepTxCtx{
tx: sweepTx,
fee: txFee,
extraTxOut: fn.FlattenOption(extraTxOut),
}, nil
} }
// prepareSweepTx returns the tx fee, an optional change amount and an optional // prepareSweepTx returns the tx fee, a set of optional change outputs and an
// locktime after a series of validations: // optional locktime after a series of validations:
// 1. check the locktime has been reached. // 1. check the locktime has been reached.
// 2. check the locktimes are the same. // 2. check the locktimes are the same.
// 3. check the inputs cover the outputs. // 3. check the inputs cover the outputs.
// //
// NOTE: if the change amount is below dust, it will be added to the tx fee. // NOTE: if the change amount is below dust, it will be added to the tx fee.
func prepareSweepTx(inputs []input.Input, changePkScript []byte, func prepareSweepTx(inputs []input.Input, changePkScript lnwallet.AddrWithKey,
feeRate chainfee.SatPerKWeight, currentHeight int32) ( feeRate chainfee.SatPerKWeight, currentHeight int32,
btcutil.Amount, fn.Option[btcutil.Amount], fn.Option[int32], error) { auxSweeper fn.Option[AuxSweeper]) (
btcutil.Amount, fn.Option[[]SweepOutput], fn.Option[int32], error) {
noChange := fn.None[btcutil.Amount]() noChange := fn.None[[]SweepOutput]()
noLocktime := fn.None[int32]() noLocktime := fn.None[int32]()
// Given the set of inputs we have, if we have an aux sweeper, then
// we'll attempt to see if we have any other change outputs we'll need
// to add to the sweep transaction.
changePkScripts := [][]byte{changePkScript.DeliveryAddress}
var extraChangeOut fn.Option[SweepOutput]
err := fn.MapOptionZ(
auxSweeper, func(aux AuxSweeper) error {
extraOut := aux.DeriveSweepAddr(inputs, changePkScript)
if err := extraOut.Err(); err != nil {
return err
}
extraChangeOut = extraOut.LeftToOption()
return nil
},
)
if err != nil {
return 0, noChange, noLocktime, err
}
// Creating a weight estimator with nil outputs and zero max fee rate. // Creating a weight estimator with nil outputs and zero max fee rate.
// We don't allow adding customized outputs in the sweeping tx, and the // We don't allow adding customized outputs in the sweeping tx, and the
// fee rate is already being managed before we get here. // fee rate is already being managed before we get here.
inputs, estimator, err := getWeightEstimate( inputs, estimator, err := getWeightEstimate(
inputs, nil, feeRate, 0, changePkScript, inputs, nil, feeRate, 0, changePkScripts,
) )
if err != nil { if err != nil {
return 0, noChange, noLocktime, err return 0, noChange, noLocktime, err
@@ -1270,6 +1396,12 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
requiredOutput btcutil.Amount requiredOutput btcutil.Amount
) )
// If we have an extra change output, then we'll add it as a required
// output amt.
extraChangeOut.WhenSome(func(o SweepOutput) {
requiredOutput += btcutil.Amount(o.Value)
})
// Go through each input and check if the required lock times have // Go through each input and check if the required lock times have
// reached and are the same. // reached and are the same.
for _, o := range inputs { for _, o := range inputs {
@@ -1316,14 +1448,22 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
// The value remaining after the required output and fees is the // The value remaining after the required output and fees is the
// change output. // change output.
changeAmt := totalInput - requiredOutput - txFee changeAmt := totalInput - requiredOutput - txFee
changeAmtOpt := fn.Some(changeAmt) changeOuts := make([]SweepOutput, 0, 2)
extraChangeOut.WhenSome(func(o SweepOutput) {
changeOuts = append(changeOuts, o)
})
// We'll calculate the dust limit for the given changePkScript since it // We'll calculate the dust limit for the given changePkScript since it
// is variable. // is variable.
changeFloor := lnwallet.DustLimitForSize(len(changePkScript)) changeFloor := lnwallet.DustLimitForSize(
len(changePkScript.DeliveryAddress),
)
// If the change amount is dust, we'll move it into the fees. switch {
if changeAmt < changeFloor { // If the change amount is dust, we'll move it into the fees, and
// ignore it.
case changeAmt < changeFloor:
log.Infof("Change amt %v below dustlimit %v, not adding "+ log.Infof("Change amt %v below dustlimit %v, not adding "+
"change output", changeAmt, changeFloor) "change output", changeAmt, changeFloor)
@@ -1338,8 +1478,16 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
// The dust amount is added to the fee. // The dust amount is added to the fee.
txFee += changeAmt txFee += changeAmt
// Set the change amount to none. // Otherwise, we'll actually recognize it as a change output.
changeAmtOpt = fn.None[btcutil.Amount]() default:
changeOuts = append(changeOuts, SweepOutput{
TxOut: wire.TxOut{
Value: int64(changeAmt),
PkScript: changePkScript.DeliveryAddress,
},
IsExtra: false,
InternalKey: changePkScript.InternalKey,
})
} }
// Optionally set the locktime. // Optionally set the locktime.
@@ -1348,6 +1496,11 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
locktimeOpt = noLocktime locktimeOpt = noLocktime
} }
var changeOutsOpt fn.Option[[]SweepOutput]
if len(changeOuts) > 0 {
changeOutsOpt = fn.Some(changeOuts)
}
log.Debugf("Creating sweep tx for %v inputs (%s) using %v, "+ log.Debugf("Creating sweep tx for %v inputs (%s) using %v, "+
"tx_weight=%v, tx_fee=%v, locktime=%v, parents_count=%v, "+ "tx_weight=%v, tx_fee=%v, locktime=%v, parents_count=%v, "+
"parents_fee=%v, parents_weight=%v, current_height=%v", "parents_fee=%v, parents_weight=%v, current_height=%v",
@@ -1355,5 +1508,5 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
estimator.weight(), txFee, locktimeOpt, len(estimator.parents), estimator.weight(), txFee, locktimeOpt, len(estimator.parents),
estimator.parentsFee, estimator.parentsWeight, currentHeight) estimator.parentsFee, estimator.parentsWeight, currentHeight)
return txFee, changeAmtOpt, locktimeOpt, nil return txFee, changeOutsOpt, locktimeOpt, nil
} }
+32 -17
View File
@@ -11,6 +11,7 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/chain" "github.com/btcsuite/btcwallet/chain"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
@@ -21,12 +22,14 @@ import (
var ( var (
// Create a taproot change script. // Create a taproot change script.
changePkScript = []byte{ changePkScript = lnwallet.AddrWithKey{
0x51, 0x20, DeliveryAddress: []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
} }
testInputCount atomic.Uint64 testInputCount atomic.Uint64
@@ -112,12 +115,16 @@ func TestCalcSweepTxWeight(t *testing.T) {
inp := createTestInput(100, input.WitnessKeyHash) inp := createTestInput(100, input.WitnessKeyHash)
// Use a wrong change script to test the error case. // Use a wrong change script to test the error case.
weight, err := calcSweepTxWeight([]input.Input{&inp}, []byte{0}) weight, err := calcSweepTxWeight(
[]input.Input{&inp}, [][]byte{{0x00}},
)
require.Error(t, err) require.Error(t, err)
require.Zero(t, weight) require.Zero(t, weight)
// Use a correct change script to test the success case. // Use a correct change script to test the success case.
weight, err = calcSweepTxWeight([]input.Input{&inp}, changePkScript) weight, err = calcSweepTxWeight(
[]input.Input{&inp}, [][]byte{changePkScript.DeliveryAddress},
)
require.NoError(t, err) require.NoError(t, err)
// BaseTxSize 8 bytes // BaseTxSize 8 bytes
@@ -137,7 +144,9 @@ func TestBumpRequestMaxFeeRateAllowed(t *testing.T) {
inp := createTestInput(100, input.WitnessKeyHash) inp := createTestInput(100, input.WitnessKeyHash)
// The weight is 487. // The weight is 487.
weight, err := calcSweepTxWeight([]input.Input{&inp}, changePkScript) weight, err := calcSweepTxWeight(
[]input.Input{&inp}, [][]byte{changePkScript.DeliveryAddress},
)
require.NoError(t, err) require.NoError(t, err)
// Define a test budget and calculates its fee rate. // Define a test budget and calculates its fee rate.
@@ -154,7 +163,9 @@ func TestBumpRequestMaxFeeRateAllowed(t *testing.T) {
// Use a wrong change script to test the error case. // Use a wrong change script to test the error case.
name: "error calc weight", name: "error calc weight",
req: &BumpRequest{ req: &BumpRequest{
DeliveryAddress: []byte{1}, DeliveryAddress: lnwallet.AddrWithKey{
DeliveryAddress: []byte{1},
},
}, },
expectedMaxFeeRate: 0, expectedMaxFeeRate: 0,
expectedErr: true, expectedErr: true,
@@ -239,7 +250,8 @@ func TestInitializeFeeFunction(t *testing.T) {
// Create a publisher using the mocks. // Create a publisher using the mocks.
tp := NewTxPublisher(TxPublisherConfig{ tp := NewTxPublisher(TxPublisherConfig{
Estimator: estimator, Estimator: estimator,
AuxSweeper: fn.Some[AuxSweeper](&MockAuxSweeper{}),
}) })
// Create a test feerate. // Create a test feerate.
@@ -304,7 +316,9 @@ func TestStoreRecord(t *testing.T) {
tx := &wire.MsgTx{} tx := &wire.MsgTx{}
// Create a publisher using the mocks. // Create a publisher using the mocks.
tp := NewTxPublisher(TxPublisherConfig{}) tp := NewTxPublisher(TxPublisherConfig{
AuxSweeper: fn.Some[AuxSweeper](&MockAuxSweeper{}),
})
// Get the current counter and check it's increased later. // Get the current counter and check it's increased later.
initialCounter := tp.requestCounter.Load() initialCounter := tp.requestCounter.Load()
@@ -369,10 +383,11 @@ func createTestPublisher(t *testing.T) (*TxPublisher, *mockers) {
// Create a publisher using the mocks. // Create a publisher using the mocks.
tp := NewTxPublisher(TxPublisherConfig{ tp := NewTxPublisher(TxPublisherConfig{
Estimator: m.estimator, Estimator: m.estimator,
Signer: m.signer, Signer: m.signer,
Wallet: m.wallet, Wallet: m.wallet,
Notifier: m.notifier, Notifier: m.notifier,
AuxSweeper: fn.Some[AuxSweeper](&MockAuxSweeper{}),
}) })
return tp, m return tp, m
@@ -451,7 +466,7 @@ func TestCreateAndCheckTx(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
// Call the method under test. // Call the method under test.
_, _, err := tp.createAndCheckTx(tc.req, m.feeFunc) _, err := tp.createAndCheckTx(tc.req, m.feeFunc)
// Check the result is as expected. // Check the result is as expected.
require.ErrorIs(t, err, tc.expectedErr) require.ErrorIs(t, err, tc.expectedErr)
+38
View File
@@ -1,8 +1,12 @@
package sweep package sweep
import ( import (
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
) )
@@ -57,3 +61,37 @@ type Wallet interface {
// service. // service.
BackEnd() string BackEnd() string
} }
// SweepOutput is an output used to sweep funds from a channel output.
type SweepOutput struct { //nolint:revive
wire.TxOut
// IsExtra indicates whether this output is an extra output that was
// added by a party other than the sweeper.
IsExtra bool
// InternalKey is the taproot internal key of the extra output. This is
// None, if this isn't a taproot output.
InternalKey fn.Option[keychain.KeyDescriptor]
}
// AuxSweeper is used to enable a 3rd party to further shape the sweeping
// transaction by adding a set of extra outputs to the sweeping transaction.
type AuxSweeper interface {
// DeriveSweepAddr takes a set of inputs, and the change address we'd
// use to sweep them, and maybe results an extra sweep output that we
// should add to the sweeping transaction.
DeriveSweepAddr(inputs []input.Input,
change lnwallet.AddrWithKey) fn.Result[SweepOutput]
// ExtraBudgetForInputs is used to determine the extra budget that
// should be allocated to sweep the given set of inputs. This can be
// used to add extra funds to the sweep transaction, for example to
// cover fees for additional outputs of custom channels.
ExtraBudgetForInputs(inputs []input.Input) fn.Result[btcutil.Amount]
// NotifyBroadcast is used to notify external callers of the broadcast
// of a sweep transaction, generated by the passed BumpRequest.
NotifyBroadcast(req *BumpRequest, tx *wire.MsgTx,
totalFees btcutil.Amount) error
}
+42
View File
@@ -6,6 +6,7 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
@@ -314,3 +315,44 @@ func (m *MockFeeFunction) IncreaseFeeRate(confTarget uint32) (bool, error) {
return args.Bool(0), args.Error(1) return args.Bool(0), args.Error(1)
} }
type MockAuxSweeper struct {
mock.Mock
}
// DeriveSweepAddr takes a set of inputs, and the change address we'd
// use to sweep them, and maybe results an extra sweep output that we
// should add to the sweeping transaction.
func (m *MockAuxSweeper) DeriveSweepAddr(_ []input.Input,
_ lnwallet.AddrWithKey) fn.Result[SweepOutput] {
return fn.Ok(SweepOutput{
TxOut: wire.TxOut{
Value: 123,
PkScript: changePkScript.DeliveryAddress,
},
IsExtra: false,
InternalKey: fn.None[keychain.KeyDescriptor](),
})
}
// ExtraBudgetForInputs is used to determine the extra budget that
// should be allocated to sweep the given set of inputs. This can be
// used to add extra funds to the sweep transaction, for example to
// cover fees for additional outputs of custom channels.
func (m *MockAuxSweeper) ExtraBudgetForInputs(
_ []input.Input) fn.Result[btcutil.Amount] {
args := m.Called()
amt := args.Get(0)
return amt.(fn.Result[btcutil.Amount])
}
// NotifyBroadcast is used to notify external callers of the broadcast
// of a sweep transaction, generated by the passed BumpRequest.
func (*MockAuxSweeper) NotifyBroadcast(_ *BumpRequest, _ *wire.MsgTx,
_ btcutil.Amount) error {
return nil
}
+17 -10
View File
@@ -298,7 +298,7 @@ type UtxoSweeper struct {
// to sweep. // to sweep.
inputs InputsMap inputs InputsMap
currentOutputScript []byte currentOutputScript fn.Option[lnwallet.AddrWithKey]
relayFeeRate chainfee.SatPerKWeight relayFeeRate chainfee.SatPerKWeight
@@ -318,7 +318,7 @@ type UtxoSweeper struct {
type UtxoSweeperConfig struct { type UtxoSweeperConfig struct {
// GenSweepScript generates a P2WKH script belonging to the wallet where // GenSweepScript generates a P2WKH script belonging to the wallet where
// funds can be swept. // funds can be swept.
GenSweepScript func() ([]byte, error) GenSweepScript func() fn.Result[lnwallet.AddrWithKey]
// FeeEstimator is used when crafting sweep transactions to estimate // FeeEstimator is used when crafting sweep transactions to estimate
// the necessary fee relative to the expected size of the sweep // the necessary fee relative to the expected size of the sweep
@@ -802,12 +802,19 @@ func (s *UtxoSweeper) signalResult(pi *SweeperInput, result Result) {
// the tx. The output address is only marked as used if the publish succeeds. // the tx. The output address is only marked as used if the publish succeeds.
func (s *UtxoSweeper) sweep(set InputSet) error { func (s *UtxoSweeper) sweep(set InputSet) error {
// Generate an output script if there isn't an unused script available. // Generate an output script if there isn't an unused script available.
if s.currentOutputScript == nil { if s.currentOutputScript.IsNone() {
pkScript, err := s.cfg.GenSweepScript() addr, err := s.cfg.GenSweepScript().Unpack()
if err != nil { if err != nil {
return fmt.Errorf("gen sweep script: %w", err) return fmt.Errorf("gen sweep script: %w", err)
} }
s.currentOutputScript = pkScript s.currentOutputScript = fn.Some(addr)
}
sweepAddr, err := s.currentOutputScript.UnwrapOrErr(
fmt.Errorf("none sweep script"),
)
if err != nil {
return err
} }
// Create a fee bump request and ask the publisher to broadcast it. The // Create a fee bump request and ask the publisher to broadcast it. The
@@ -817,7 +824,7 @@ func (s *UtxoSweeper) sweep(set InputSet) error {
Inputs: set.Inputs(), Inputs: set.Inputs(),
Budget: set.Budget(), Budget: set.Budget(),
DeadlineHeight: set.DeadlineHeight(), DeadlineHeight: set.DeadlineHeight(),
DeliveryAddress: s.currentOutputScript, DeliveryAddress: sweepAddr,
MaxFeeRate: s.cfg.MaxFeeRate.FeePerKWeight(), MaxFeeRate: s.cfg.MaxFeeRate.FeePerKWeight(),
StartingFeeRate: set.StartingFeeRate(), StartingFeeRate: set.StartingFeeRate(),
// TODO(yy): pass the strategy here. // TODO(yy): pass the strategy here.
@@ -1708,10 +1715,10 @@ func (s *UtxoSweeper) handleBumpEventTxPublished(r *BumpResult) error {
log.Debugf("Published sweep tx %v, num_inputs=%v, height=%v", log.Debugf("Published sweep tx %v, num_inputs=%v, height=%v",
tx.TxHash(), len(tx.TxIn), s.currentHeight) tx.TxHash(), len(tx.TxIn), s.currentHeight)
// If there's no error, remove the output script. Otherwise // If there's no error, remove the output script. Otherwise keep it so
// keep it so that it can be reused for the next transaction // that it can be reused for the next transaction and causes no address
// and causes no address inflation. // inflation.
s.currentOutputScript = nil s.currentOutputScript = fn.None[lnwallet.AddrWithKey]()
return nil return nil
} }
+6 -2
View File
@@ -12,6 +12,7 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -667,8 +668,11 @@ func TestSweepPendingInputs(t *testing.T) {
Wallet: wallet, Wallet: wallet,
Aggregator: aggregator, Aggregator: aggregator,
Publisher: publisher, Publisher: publisher,
GenSweepScript: func() ([]byte, error) { GenSweepScript: func() fn.Result[lnwallet.AddrWithKey] {
return testPubKey.SerializeCompressed(), nil //nolint:lll
return fn.Ok(lnwallet.AddrWithKey{
DeliveryAddress: testPubKey.SerializeCompressed(),
})
}, },
NoDeadlineConfTarget: uint32(DefaultDeadlineDelta), NoDeadlineConfTarget: uint32(DefaultDeadlineDelta),
}) })
+39 -5
View File
@@ -111,17 +111,26 @@ type BudgetInputSet struct {
// deadlineHeight is the height which the inputs in this set must be // deadlineHeight is the height which the inputs in this set must be
// confirmed by. // confirmed by.
deadlineHeight int32 deadlineHeight int32
// extraBudget is a value that should be allocated to sweep the given
// set of inputs. This can be used to add extra funds to the sweep
// transaction, for example to cover fees for additional outputs of
// custom channels.
extraBudget btcutil.Amount
} }
// Compile-time constraint to ensure budgetInputSet implements InputSet. // Compile-time constraint to ensure budgetInputSet implements InputSet.
var _ InputSet = (*BudgetInputSet)(nil) var _ InputSet = (*BudgetInputSet)(nil)
// errEmptyInputs is returned when the input slice is empty.
var errEmptyInputs = fmt.Errorf("inputs slice is empty")
// validateInputs is used when creating new BudgetInputSet to ensure there are // validateInputs is used when creating new BudgetInputSet to ensure there are
// no duplicate inputs and they all share the same deadline heights, if set. // no duplicate inputs and they all share the same deadline heights, if set.
func validateInputs(inputs []SweeperInput, deadlineHeight int32) error { func validateInputs(inputs []SweeperInput, deadlineHeight int32) error {
// Sanity check the input slice to ensure it's non-empty. // Sanity check the input slice to ensure it's non-empty.
if len(inputs) == 0 { if len(inputs) == 0 {
return fmt.Errorf("inputs slice is empty") return errEmptyInputs
} }
// inputDeadline tracks the input's deadline height. It will be updated // inputDeadline tracks the input's deadline height. It will be updated
@@ -167,8 +176,8 @@ func validateInputs(inputs []SweeperInput, deadlineHeight int32) error {
} }
// NewBudgetInputSet creates a new BudgetInputSet. // NewBudgetInputSet creates a new BudgetInputSet.
func NewBudgetInputSet(inputs []SweeperInput, func NewBudgetInputSet(inputs []SweeperInput, deadlineHeight int32,
deadlineHeight int32) (*BudgetInputSet, error) { auxSweeper fn.Option[AuxSweeper]) (*BudgetInputSet, error) {
// Validate the supplied inputs. // Validate the supplied inputs.
if err := validateInputs(inputs, deadlineHeight); err != nil { if err := validateInputs(inputs, deadlineHeight); err != nil {
@@ -186,9 +195,32 @@ func NewBudgetInputSet(inputs []SweeperInput,
log.Tracef("Created %v", bi.String()) log.Tracef("Created %v", bi.String())
// Attach an optional budget. This will be a no-op if the auxSweeper
// is not set.
if err := bi.attachExtraBudget(auxSweeper); err != nil {
return nil, err
}
return bi, nil return bi, nil
} }
// attachExtraBudget attaches an extra budget to the input set, if the passed
// aux sweeper is set.
func (b *BudgetInputSet) attachExtraBudget(s fn.Option[AuxSweeper]) error {
extraBudget, err := fn.MapOptionZ(
s, func(aux AuxSweeper) fn.Result[btcutil.Amount] {
return aux.ExtraBudgetForInputs(b.Inputs())
},
).Unpack()
if err != nil {
return err
}
b.extraBudget = extraBudget
return nil
}
// String returns a human-readable description of the input set. // String returns a human-readable description of the input set.
func (b *BudgetInputSet) String() string { func (b *BudgetInputSet) String() string {
inputsDesc := "" inputsDesc := ""
@@ -212,8 +244,10 @@ func (b *BudgetInputSet) addInput(input SweeperInput) {
func (b *BudgetInputSet) NeedWalletInput() bool { func (b *BudgetInputSet) NeedWalletInput() bool {
var ( var (
// budgetNeeded is the amount that needs to be covered from // budgetNeeded is the amount that needs to be covered from
// other inputs. // other inputs. We start at the value of the extra budget,
budgetNeeded btcutil.Amount // which might be needed for custom channels that add extra
// outputs.
budgetNeeded = b.extraBudget
// budgetBorrowable is the amount that can be borrowed from // budgetBorrowable is the amount that can be borrowed from
// other inputs. // other inputs.
+86 -25
View File
@@ -28,7 +28,9 @@ func TestNewBudgetInputSet(t *testing.T) {
rt := require.New(t) rt := require.New(t)
// Pass an empty slice and expect an error. // Pass an empty slice and expect an error.
set, err := NewBudgetInputSet([]SweeperInput{}, testHeight) set, err := NewBudgetInputSet(
[]SweeperInput{}, testHeight, fn.None[AuxSweeper](),
)
rt.ErrorContains(err, "inputs slice is empty") rt.ErrorContains(err, "inputs slice is empty")
rt.Nil(set) rt.Nil(set)
@@ -66,23 +68,35 @@ func TestNewBudgetInputSet(t *testing.T) {
} }
// Pass a slice of inputs with different deadline heights. // Pass a slice of inputs with different deadline heights.
set, err = NewBudgetInputSet([]SweeperInput{input1, input2}, testHeight) set, err = NewBudgetInputSet(
[]SweeperInput{input1, input2}, testHeight,
fn.None[AuxSweeper](),
)
rt.ErrorContains(err, "input deadline height not matched") rt.ErrorContains(err, "input deadline height not matched")
rt.Nil(set) rt.Nil(set)
// Pass a slice of inputs that only one input has the deadline height, // Pass a slice of inputs that only one input has the deadline height,
// but it has a different value than the specified testHeight. // but it has a different value than the specified testHeight.
set, err = NewBudgetInputSet([]SweeperInput{input0, input2}, testHeight) set, err = NewBudgetInputSet(
[]SweeperInput{input0, input2}, testHeight,
fn.None[AuxSweeper](),
)
rt.ErrorContains(err, "input deadline height not matched") rt.ErrorContains(err, "input deadline height not matched")
rt.Nil(set) rt.Nil(set)
// Pass a slice of inputs that are duplicates. // Pass a slice of inputs that are duplicates.
set, err = NewBudgetInputSet([]SweeperInput{input3, input3}, testHeight) set, err = NewBudgetInputSet(
[]SweeperInput{input3, input3}, testHeight,
fn.None[AuxSweeper](),
)
rt.ErrorContains(err, "duplicate inputs") rt.ErrorContains(err, "duplicate inputs")
rt.Nil(set) rt.Nil(set)
// Pass a slice of inputs that only one input has the deadline height, // Pass a slice of inputs that only one input has the deadline height.
set, err = NewBudgetInputSet([]SweeperInput{input0, input3}, testHeight) set, err = NewBudgetInputSet(
[]SweeperInput{input0, input3}, testHeight,
fn.None[AuxSweeper](),
)
rt.NoError(err) rt.NoError(err)
rt.NotNil(set) rt.NotNil(set)
} }
@@ -102,7 +116,9 @@ func TestBudgetInputSetAddInput(t *testing.T) {
} }
// Initialize an input set, which adds the above input. // Initialize an input set, which adds the above input.
set, err := NewBudgetInputSet([]SweeperInput{*pi}, testHeight) set, err := NewBudgetInputSet(
[]SweeperInput{*pi}, testHeight, fn.None[AuxSweeper](),
)
require.NoError(t, err) require.NoError(t, err)
// Add the input to the set again. // Add the input to the set again.
@@ -125,48 +141,55 @@ func TestNeedWalletInput(t *testing.T) {
// Create a mock input that doesn't have required outputs. // Create a mock input that doesn't have required outputs.
mockInput := &input.MockInput{} mockInput := &input.MockInput{}
mockInput.On("RequiredTxOut").Return(nil) mockInput.On("RequiredTxOut").Return(nil)
mockInput.On("OutPoint").Return(wire.OutPoint{Hash: chainhash.Hash{1}})
defer mockInput.AssertExpectations(t) defer mockInput.AssertExpectations(t)
// Create a mock input that has required outputs. // Create a mock input that has required outputs.
mockInputRequireOutput := &input.MockInput{} mockInputRequireOutput := &input.MockInput{}
mockInputRequireOutput.On("RequiredTxOut").Return(&wire.TxOut{}) mockInputRequireOutput.On("RequiredTxOut").Return(&wire.TxOut{})
mockInputRequireOutput.On("OutPoint").Return(
wire.OutPoint{Hash: chainhash.Hash{2}},
)
defer mockInputRequireOutput.AssertExpectations(t) defer mockInputRequireOutput.AssertExpectations(t)
// We now create two pending inputs each has a budget of 100 satoshis. // We now create two pending inputs each has a budget of 100 satoshis.
const budget = 100 const budget = 100
// Create the pending input that doesn't have a required output. // Create the pending input that doesn't have a required output.
piBudget := &SweeperInput{ piBudget := SweeperInput{
Input: mockInput, Input: mockInput,
params: Params{Budget: budget}, params: Params{Budget: budget},
} }
// Create the pending input that has a required output. // Create the pending input that has a required output.
piRequireOutput := &SweeperInput{ piRequireOutput := SweeperInput{
Input: mockInputRequireOutput, Input: mockInputRequireOutput,
params: Params{Budget: budget}, params: Params{Budget: budget},
} }
testCases := []struct { testCases := []struct {
name string name string
setupInputs func() []*SweeperInput setupInputs func() []SweeperInput
extraBudget btcutil.Amount
need bool need bool
err error
}{ }{
{ {
// When there are no pending inputs, we won't need a // When there are no pending inputs, we won't need a
// wallet input. Technically this should be an invalid // wallet input. Technically this is be an invalid
// state. // state.
name: "no inputs", name: "no inputs",
setupInputs: func() []*SweeperInput { setupInputs: func() []SweeperInput {
return nil return nil
}, },
need: false, need: false,
err: errEmptyInputs,
}, },
{ {
// When there's no required output, we don't need a // When there's no required output, we don't need a
// wallet input. // wallet input.
name: "no required outputs", name: "no required outputs",
setupInputs: func() []*SweeperInput { setupInputs: func() []SweeperInput {
// Create a sign descriptor to be used in the // Create a sign descriptor to be used in the
// pending input when calculating budgets can // pending input when calculating budgets can
// be borrowed. // be borrowed.
@@ -177,15 +200,36 @@ func TestNeedWalletInput(t *testing.T) {
} }
mockInput.On("SignDesc").Return(sd).Once() mockInput.On("SignDesc").Return(sd).Once()
return []*SweeperInput{piBudget} return []SweeperInput{piBudget}
}, },
need: false, need: false,
}, },
{
// When there's no required normal outputs, but an extra
// budget from custom channels, we will need a wallet
// input.
name: "no required normal outputs but extra budget",
setupInputs: func() []SweeperInput {
// Create a sign descriptor to be used in the
// pending input when calculating budgets can
// be borrowed.
sd := &input.SignDescriptor{
Output: &wire.TxOut{
Value: budget,
},
}
mockInput.On("SignDesc").Return(sd).Once()
return []SweeperInput{piBudget}
},
extraBudget: 1000,
need: true,
},
{ {
// When the output value cannot cover the budget, we // When the output value cannot cover the budget, we
// need a wallet input. // need a wallet input.
name: "output value cannot cover budget", name: "output value cannot cover budget",
setupInputs: func() []*SweeperInput { setupInputs: func() []SweeperInput {
// Create a sign descriptor to be used in the // Create a sign descriptor to be used in the
// pending input when calculating budgets can // pending input when calculating budgets can
// be borrowed. // be borrowed.
@@ -194,8 +238,8 @@ func TestNeedWalletInput(t *testing.T) {
Value: budget - 1, Value: budget - 1,
}, },
} }
mockInput.On("SignDesc").Return(sd).Once()
mockInput.On("SignDesc").Return(sd).Once()
// These two methods are only invoked when the // These two methods are only invoked when the
// unit test is running with a logger. // unit test is running with a logger.
mockInput.On("OutPoint").Return( mockInput.On("OutPoint").Return(
@@ -205,7 +249,7 @@ func TestNeedWalletInput(t *testing.T) {
input.CommitmentAnchor, input.CommitmentAnchor,
).Maybe() ).Maybe()
return []*SweeperInput{piBudget} return []SweeperInput{piBudget}
}, },
need: true, need: true,
}, },
@@ -213,8 +257,8 @@ func TestNeedWalletInput(t *testing.T) {
// When there's only inputs that require outputs, we // When there's only inputs that require outputs, we
// need wallet inputs. // need wallet inputs.
name: "only required outputs", name: "only required outputs",
setupInputs: func() []*SweeperInput { setupInputs: func() []SweeperInput {
return []*SweeperInput{piRequireOutput} return []SweeperInput{piRequireOutput}
}, },
need: true, need: true,
}, },
@@ -223,7 +267,7 @@ func TestNeedWalletInput(t *testing.T) {
// budget cannot cover the required, we need a wallet // budget cannot cover the required, we need a wallet
// input. // input.
name: "not enough budget to be borrowed", name: "not enough budget to be borrowed",
setupInputs: func() []*SweeperInput { setupInputs: func() []SweeperInput {
// Create a sign descriptor to be used in the // Create a sign descriptor to be used in the
// pending input when calculating budgets can // pending input when calculating budgets can
// be borrowed. // be borrowed.
@@ -237,7 +281,7 @@ func TestNeedWalletInput(t *testing.T) {
} }
mockInput.On("SignDesc").Return(sd).Once() mockInput.On("SignDesc").Return(sd).Once()
return []*SweeperInput{ return []SweeperInput{
piBudget, piRequireOutput, piBudget, piRequireOutput,
} }
}, },
@@ -248,7 +292,7 @@ func TestNeedWalletInput(t *testing.T) {
// borrowed covers the required, we don't need wallet // borrowed covers the required, we don't need wallet
// inputs. // inputs.
name: "enough budget to be borrowed", name: "enough budget to be borrowed",
setupInputs: func() []*SweeperInput { setupInputs: func() []SweeperInput {
// Create a sign descriptor to be used in the // Create a sign descriptor to be used in the
// pending input when calculating budgets can // pending input when calculating budgets can
// be borrowed. // be borrowed.
@@ -263,7 +307,7 @@ func TestNeedWalletInput(t *testing.T) {
mockInput.On("SignDesc").Return(sd).Once() mockInput.On("SignDesc").Return(sd).Once()
piBudget.Input = mockInput piBudget.Input = mockInput
return []*SweeperInput{ return []SweeperInput{
piBudget, piRequireOutput, piBudget, piRequireOutput,
} }
}, },
@@ -276,12 +320,27 @@ func TestNeedWalletInput(t *testing.T) {
// Setup testing inputs. // Setup testing inputs.
inputs := tc.setupInputs() inputs := tc.setupInputs()
// If an extra budget is set, then we'll update the mock
// to expect the extra budget.
mockAuxSweeper := &MockAuxSweeper{}
mockAuxSweeper.On("ExtraBudgetForInputs").Return(
fn.Ok(tc.extraBudget),
)
// Initialize an input set, which adds the testing // Initialize an input set, which adds the testing
// inputs. // inputs.
set := &BudgetInputSet{inputs: inputs} set, err := NewBudgetInputSet(
inputs, 0, fn.Some[AuxSweeper](mockAuxSweeper),
)
if err != nil {
require.ErrorIs(t, err, tc.err)
return
}
result := set.NeedWalletInput() result := set.NeedWalletInput()
require.Equal(t, tc.need, result) require.Equal(t, tc.need, result)
mockAuxSweeper.AssertExpectations(t)
}) })
} }
} }
@@ -434,7 +493,9 @@ func TestAddWalletInputSuccess(t *testing.T) {
min, max).Return([]*lnwallet.Utxo{utxo, utxo}, nil).Once() min, max).Return([]*lnwallet.Utxo{utxo, utxo}, nil).Once()
// Initialize an input set with the pending input. // Initialize an input set with the pending input.
set, err := NewBudgetInputSet([]SweeperInput{*pi}, deadline) set, err := NewBudgetInputSet(
[]SweeperInput{*pi}, deadline, fn.None[AuxSweeper](),
)
require.NoError(t, err) require.NoError(t, err)
// Add wallet inputs to the input set, which should give us an error as // Add wallet inputs to the input set, which should give us an error as
+25 -22
View File
@@ -38,7 +38,7 @@ func createSweepTx(inputs []input.Input, outputs []*wire.TxOut,
signer input.Signer) (*wire.MsgTx, btcutil.Amount, error) { signer input.Signer) (*wire.MsgTx, btcutil.Amount, error) {
inputs, estimator, err := getWeightEstimate( inputs, estimator, err := getWeightEstimate(
inputs, outputs, feeRate, maxFeeRate, changePkScript, inputs, outputs, feeRate, maxFeeRate, [][]byte{changePkScript},
) )
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@@ -221,7 +221,7 @@ func createSweepTx(inputs []input.Input, outputs []*wire.TxOut,
// Additionally, it returns counts for the number of csv and cltv inputs. // Additionally, it returns counts for the number of csv and cltv inputs.
func getWeightEstimate(inputs []input.Input, outputs []*wire.TxOut, func getWeightEstimate(inputs []input.Input, outputs []*wire.TxOut,
feeRate, maxFeeRate chainfee.SatPerKWeight, feeRate, maxFeeRate chainfee.SatPerKWeight,
outputPkScript []byte) ([]input.Input, *weightEstimator, error) { outputPkScripts [][]byte) ([]input.Input, *weightEstimator, error) {
// We initialize a weight estimator so we can accurately asses the // We initialize a weight estimator so we can accurately asses the
// amount of fees we need to pay for this sweep transaction. // amount of fees we need to pay for this sweep transaction.
@@ -237,31 +237,34 @@ func getWeightEstimate(inputs []input.Input, outputs []*wire.TxOut,
// If there is any leftover change after paying to the given outputs // If there is any leftover change after paying to the given outputs
// and required outputs, it will go to a single segwit p2wkh or p2tr // and required outputs, it will go to a single segwit p2wkh or p2tr
// address. This will be our change address, so ensure it contributes to // address. This will be our change address, so ensure it contributes
// our weight estimate. Note that if we have other outputs, we might end // to our weight estimate. Note that if we have other outputs, we might
// up creating a sweep tx without a change output. It is okay to add the // end up creating a sweep tx without a change output. It is okay to
// change output to the weight estimate regardless, since the estimated // add the change output to the weight estimate regardless, since the
// fee will just be subtracted from this already dust output, and // estimated fee will just be subtracted from this already dust output,
// trimmed. // and trimmed.
switch { for _, outputPkScript := range outputPkScripts {
case txscript.IsPayToTaproot(outputPkScript): switch {
weightEstimate.addP2TROutput() case txscript.IsPayToTaproot(outputPkScript):
weightEstimate.addP2TROutput()
case txscript.IsPayToWitnessScriptHash(outputPkScript): case txscript.IsPayToWitnessScriptHash(outputPkScript):
weightEstimate.addP2WSHOutput() weightEstimate.addP2WSHOutput()
case txscript.IsPayToWitnessPubKeyHash(outputPkScript): case txscript.IsPayToWitnessPubKeyHash(outputPkScript):
weightEstimate.addP2WKHOutput() weightEstimate.addP2WKHOutput()
case txscript.IsPayToPubKeyHash(outputPkScript): case txscript.IsPayToPubKeyHash(outputPkScript):
weightEstimate.estimator.AddP2PKHOutput() weightEstimate.estimator.AddP2PKHOutput()
case txscript.IsPayToScriptHash(outputPkScript): case txscript.IsPayToScriptHash(outputPkScript):
weightEstimate.estimator.AddP2SHOutput() weightEstimate.estimator.AddP2SHOutput()
default: default:
// Unknown script type. // Unknown script type.
return nil, nil, errors.New("unknown script type") return nil, nil, fmt.Errorf("unknown script "+
"type: %x", outputPkScript)
}
} }
// For each output, use its witness type to determine the estimate // For each output, use its witness type to determine the estimate
+2 -2
View File
@@ -51,7 +51,7 @@ func TestWeightEstimate(t *testing.T) {
} }
_, estimator, err := getWeightEstimate( _, estimator, err := getWeightEstimate(
inputs, nil, 0, 0, changePkScript, inputs, nil, 0, 0, [][]byte{changePkScript},
) )
require.NoError(t, err) require.NoError(t, err)
@@ -153,7 +153,7 @@ func testUnknownScriptInner(t *testing.T, pkscript []byte, expectFail bool) {
)) ))
} }
_, _, err := getWeightEstimate(inputs, nil, 0, 0, pkscript) _, _, err := getWeightEstimate(inputs, nil, 0, 0, [][]byte{pkscript})
if expectFail { if expectFail {
require.Error(t, err) require.Error(t, err)
} else { } else {