diff --git a/loopd/daemon.go b/loopd/daemon.go index 6ac443e74..6dd912880 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -617,7 +617,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { Signer: d.lnd.Signer, Store: withdrawalStore, } - withdrawalManager = withdraw.NewManager(withdrawalCfg, blockHeight) + withdrawalManager = withdraw.NewManager(withdrawalCfg) // Static address loop-in manager setup. staticAddressLoopInStore := loopin.NewSqlStore( diff --git a/staticaddr/address/manager_test.go b/staticaddr/address/manager_test.go index 017ad4c85..cb8a38fa6 100644 --- a/staticaddr/address/manager_test.go +++ b/staticaddr/address/manager_test.go @@ -76,6 +76,17 @@ func (m *mockStaticAddressClient) ServerWithdrawDeposits(ctx context.Context, args.Error(1) } +func (m *mockStaticAddressClient) ServerPsbtWithdrawDeposits(ctx context.Context, + in *swapserverrpc.ServerPsbtWithdrawRequest, + opts ...grpc.CallOption) (*swapserverrpc.ServerPsbtWithdrawResponse, + error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.ServerPsbtWithdrawResponse), + args.Error(1) +} + func (m *mockStaticAddressClient) ServerNewAddress(ctx context.Context, in *swapserverrpc.ServerNewAddressRequest, opts ...grpc.CallOption) ( *swapserverrpc.ServerNewAddressResponse, error) { diff --git a/staticaddr/deposit/manager_test.go b/staticaddr/deposit/manager_test.go index 1f0b2cf95..61079142b 100644 --- a/staticaddr/deposit/manager_test.go +++ b/staticaddr/deposit/manager_test.go @@ -93,6 +93,17 @@ func (m *mockStaticAddressClient) ServerWithdrawDeposits(ctx context.Context, args.Error(1) } +func (m *mockStaticAddressClient) ServerPsbtWithdrawDeposits(ctx context.Context, + in *swapserverrpc.ServerPsbtWithdrawRequest, + opts ...grpc.CallOption) (*swapserverrpc.ServerPsbtWithdrawResponse, + error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.ServerPsbtWithdrawResponse), + args.Error(1) +} + func (m *mockStaticAddressClient) ServerNewAddress(ctx context.Context, in *swapserverrpc.ServerNewAddressRequest, opts ...grpc.CallOption) ( *swapserverrpc.ServerNewAddressResponse, error) { diff --git a/staticaddr/loopin/actions.go b/staticaddr/loopin/actions.go index bf8963301..9286485b1 100644 --- a/staticaddr/loopin/actions.go +++ b/staticaddr/loopin/actions.go @@ -17,6 +17,7 @@ import ( "github.com/lightninglabs/loop" "github.com/lightninglabs/loop/fsm" "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/staticutil" "github.com/lightninglabs/loop/staticaddr/version" "github.com/lightninglabs/loop/swap" "github.com/lightninglabs/loop/swapserverrpc" @@ -318,8 +319,11 @@ func (f *FSM) SignHtlcTxAction(ctx context.Context, // Create a musig2 session for each deposit and different htlc tx fee // rates. - createSession := f.loopIn.createMusig2Sessions - htlcSessions, clientHtlcNonces, err := createSession(ctx, f.cfg.Signer) + createSession := staticutil.CreateMusig2Sessions + htlcSessions, clientHtlcNonces, err := createSession( + ctx, f.cfg.Signer, f.loopIn.Deposits, f.loopIn.AddressParams, + f.loopIn.Address, + ) if err != nil { err = fmt.Errorf("unable to create musig2 sessions: %w", err) @@ -328,7 +332,8 @@ func (f *FSM) SignHtlcTxAction(ctx context.Context, defer f.cleanUpSessions(ctx, htlcSessions) htlcSessionsHighFee, highFeeNonces, err := createSession( - ctx, f.cfg.Signer, + ctx, f.cfg.Signer, f.loopIn.Deposits, f.loopIn.AddressParams, + f.loopIn.Address, ) if err != nil { return f.HandleError(err) @@ -336,7 +341,8 @@ func (f *FSM) SignHtlcTxAction(ctx context.Context, defer f.cleanUpSessions(ctx, htlcSessionsHighFee) htlcSessionsExtremelyHighFee, extremelyHighNonces, err := createSession( - ctx, f.cfg.Signer, + ctx, f.cfg.Signer, f.loopIn.Deposits, f.loopIn.AddressParams, + f.loopIn.Address, ) if err != nil { err = fmt.Errorf("unable to convert nonces: %w", err) diff --git a/staticaddr/loopin/loopin.go b/staticaddr/loopin/loopin.go index cca73bab4..9be25730a 100644 --- a/staticaddr/loopin/loopin.go +++ b/staticaddr/loopin/loopin.go @@ -21,6 +21,7 @@ import ( "github.com/lightninglabs/loop/staticaddr/address" "github.com/lightninglabs/loop/staticaddr/deposit" "github.com/lightninglabs/loop/staticaddr/script" + "github.com/lightninglabs/loop/staticaddr/staticutil" "github.com/lightninglabs/loop/staticaddr/version" "github.com/lightninglabs/loop/swap" "github.com/lightningnetwork/lnd/input" @@ -169,47 +170,6 @@ func (l *StaticAddressLoopIn) getHtlc(chainParams *chaincfg.Params) (*swap.Htlc, ) } -// createMusig2Sessions creates a musig2 session for a number of deposits. -func (l *StaticAddressLoopIn) createMusig2Sessions(ctx context.Context, - signer lndclient.SignerClient) ([]*input.MuSig2SessionInfo, [][]byte, - error) { - - musig2Sessions := make([]*input.MuSig2SessionInfo, len(l.Deposits)) - clientNonces := make([][]byte, len(l.Deposits)) - - // Create the sessions and nonces from the deposits. - for i := 0; i < len(l.Deposits); i++ { - session, err := l.createMusig2Session(ctx, signer) - if err != nil { - return nil, nil, err - } - - musig2Sessions[i] = session - clientNonces[i] = session.PublicNonce[:] - } - - return musig2Sessions, clientNonces, nil -} - -// Musig2CreateSession creates a musig2 session for the deposit. -func (l *StaticAddressLoopIn) createMusig2Session(ctx context.Context, - signer lndclient.SignerClient) (*input.MuSig2SessionInfo, error) { - - signers := [][]byte{ - l.AddressParams.ClientPubkey.SerializeCompressed(), - l.AddressParams.ServerPubkey.SerializeCompressed(), - } - - expiryLeaf := l.Address.TimeoutLeaf - - rootHash := expiryLeaf.TapHash() - - return signer.MuSig2CreateSession( - ctx, input.MuSig2Version100RC2, &l.AddressParams.KeyLocator, - signers, lndclient.MuSig2TaprootTweakOpt(rootHash[:], false), - ) -} - // signMusig2Tx adds the server nonces to the musig2 sessions and signs the // transaction. func (l *StaticAddressLoopIn) signMusig2Tx(ctx context.Context, @@ -217,7 +177,9 @@ func (l *StaticAddressLoopIn) signMusig2Tx(ctx context.Context, musig2sessions []*input.MuSig2SessionInfo, counterPartyNonces [][musig2.PubNonceSize]byte) ([][]byte, error) { - prevOuts, err := l.toPrevOuts(l.Deposits, l.AddressParams.PkScript) + prevOuts, err := staticutil.ToPrevOuts( + l.Deposits, l.AddressParams.PkScript, + ) if err != nil { return nil, err } @@ -523,29 +485,6 @@ func (l *StaticAddressLoopIn) Outpoints() []wire.OutPoint { return outpoints } -func (l *StaticAddressLoopIn) toPrevOuts(deposits []*deposit.Deposit, - pkScript []byte) (map[wire.OutPoint]*wire.TxOut, error) { - - prevOuts := make(map[wire.OutPoint]*wire.TxOut, len(deposits)) - for _, d := range deposits { - outpoint := wire.OutPoint{ - Hash: d.Hash, - Index: d.Index, - } - txOut := &wire.TxOut{ - Value: int64(d.Value), - PkScript: pkScript, - } - if _, ok := prevOuts[outpoint]; ok { - return nil, fmt.Errorf("duplicate outpoint %v", - outpoint) - } - prevOuts[outpoint] = txOut - } - - return prevOuts, nil -} - // GetState returns the current state of the loop-in swap. func (l *StaticAddressLoopIn) GetState() fsm.StateType { l.mu.Lock() diff --git a/staticaddr/loopin/manager.go b/staticaddr/loopin/manager.go index 05f28f1c9..e40245b4e 100644 --- a/staticaddr/loopin/manager.go +++ b/staticaddr/loopin/manager.go @@ -21,6 +21,7 @@ import ( "github.com/lightninglabs/loop/labels" "github.com/lightninglabs/loop/staticaddr/address" "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/staticutil" "github.com/lightninglabs/loop/swapserverrpc" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lntypes" @@ -386,8 +387,8 @@ func (m *Manager) handleLoopInSweepReq(ctx context.Context, ) copy(serverNonce[:], nonce) - musig2Session, err := loopIn.createMusig2Session( - ctx, m.cfg.Signer, + musig2Session, err := staticutil.CreateMusig2Session( + ctx, m.cfg.Signer, loopIn.AddressParams, loopIn.Address, ) if err != nil { return err diff --git a/staticaddr/staticutil/outpoints.go b/staticaddr/staticutil/outpoints.go new file mode 100644 index 000000000..69325dad4 --- /dev/null +++ b/staticaddr/staticutil/outpoints.go @@ -0,0 +1,25 @@ +package staticutil + +import ( + "fmt" + + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/lnrpc" +) + +// ToWireOutpoints converts lnrpc.OutPoint protos into wire.OutPoint structs so +// they can be consumed by lower level transaction building code. +func ToWireOutpoints(outpoints []*lnrpc.OutPoint) ([]wire.OutPoint, error) { + serverOutpoints := make([]wire.OutPoint, 0, len(outpoints)) + for _, o := range outpoints { + outpointStr := fmt.Sprintf("%s:%d", o.TxidStr, o.OutputIndex) + newOutpoint, err := wire.NewOutPointFromString(outpointStr) + if err != nil { + return nil, err + } + + serverOutpoints = append(serverOutpoints, *newOutpoint) + } + + return serverOutpoints, nil +} diff --git a/staticaddr/staticutil/utils.go b/staticaddr/staticutil/utils.go new file mode 100644 index 000000000..0d4b8c5c3 --- /dev/null +++ b/staticaddr/staticutil/utils.go @@ -0,0 +1,205 @@ +package staticutil + +import ( + "bytes" + "context" + "fmt" + "sort" + + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop/staticaddr/address" + "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/script" + "github.com/lightninglabs/loop/swapserverrpc" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lnwallet" +) + +// ToPrevOuts converts a slice of deposits to a map of outpoints to TxOuts. +func ToPrevOuts(deposits []*deposit.Deposit, + pkScript []byte) (map[wire.OutPoint]*wire.TxOut, error) { + + prevOuts := make(map[wire.OutPoint]*wire.TxOut, len(deposits)) + for _, d := range deposits { + outpoint := wire.OutPoint{ + Hash: d.Hash, + Index: d.Index, + } + txOut := &wire.TxOut{ + Value: int64(d.Value), + PkScript: pkScript, + } + if _, ok := prevOuts[outpoint]; ok { + return nil, fmt.Errorf("duplicate outpoint %v", + outpoint) + } + prevOuts[outpoint] = txOut + } + + return prevOuts, nil +} + +// CreateMusig2Sessions creates a musig2 session for a number of deposits. +func CreateMusig2Sessions(ctx context.Context, + signer lndclient.SignerClient, deposits []*deposit.Deposit, + addrParams *address.Parameters, + staticAddress *script.StaticAddress) ([]*input.MuSig2SessionInfo, + [][]byte, error) { + + musig2Sessions := make([]*input.MuSig2SessionInfo, len(deposits)) + clientNonces := make([][]byte, len(deposits)) + + // Create the sessions and nonces from the deposits. + for i := 0; i < len(deposits); i++ { + session, err := CreateMusig2Session( + ctx, signer, addrParams, staticAddress, + ) + if err != nil { + return nil, nil, err + } + + musig2Sessions[i] = session + clientNonces[i] = session.PublicNonce[:] + } + + return musig2Sessions, clientNonces, nil +} + +// CreateMusig2SessionsPerDeposit creates a musig2 session for a number of +// deposits. +func CreateMusig2SessionsPerDeposit(ctx context.Context, + signer lndclient.SignerClient, deposits []*deposit.Deposit, + addrParams *address.Parameters, + staticAddress *script.StaticAddress) ( + map[string]*input.MuSig2SessionInfo, map[string][]byte, map[string]int, + error) { + + sessions := make(map[string]*input.MuSig2SessionInfo) + nonces := make(map[string][]byte) + depositToIdx := make(map[string]int) + + // Create the musig2 sessions for the sweepless sweep tx. + for i, deposit := range deposits { + session, err := CreateMusig2Session( + ctx, signer, addrParams, staticAddress, + ) + if err != nil { + return nil, nil, nil, err + } + + sessions[deposit.String()] = session + nonces[deposit.String()] = session.PublicNonce[:] + depositToIdx[deposit.String()] = i + } + + return sessions, nonces, depositToIdx, nil +} + +// CreateMusig2Session creates a musig2 session for the deposit. +func CreateMusig2Session(ctx context.Context, + signer lndclient.SignerClient, addrParams *address.Parameters, + staticAddress *script.StaticAddress) (*input.MuSig2SessionInfo, error) { + + signers := [][]byte{ + addrParams.ClientPubkey.SerializeCompressed(), + addrParams.ServerPubkey.SerializeCompressed(), + } + + expiryLeaf := staticAddress.TimeoutLeaf + + rootHash := expiryLeaf.TapHash() + + return signer.MuSig2CreateSession( + ctx, input.MuSig2Version100RC2, &addrParams.KeyLocator, + signers, lndclient.MuSig2TaprootTweakOpt(rootHash[:], false), + ) +} + +// GetPrevoutInfo converts a map of prevOuts to protobuf. +func GetPrevoutInfo(prevOuts map[wire.OutPoint]*wire.TxOut, +) []*swapserverrpc.PrevoutInfo { + + prevoutInfos := make([]*swapserverrpc.PrevoutInfo, 0, len(prevOuts)) + + for outpoint, txOut := range prevOuts { + prevoutInfo := &swapserverrpc.PrevoutInfo{ + TxidBytes: outpoint.Hash[:], + OutputIndex: outpoint.Index, + Value: uint64(txOut.Value), + PkScript: txOut.PkScript, + } + prevoutInfos = append(prevoutInfos, prevoutInfo) + } + + // Sort UTXOs by txid:index using BIP-0069 rule. The function is used + // in unit tests a lot, and it is useful to make it deterministic. + sort.Slice(prevoutInfos, func(i, j int) bool { + return bip69inputLess(prevoutInfos[i], prevoutInfos[j]) + }) + + return prevoutInfos +} + +// bip69inputLess returns true if input1 < input2 according to BIP-0069 +// First sort based on input hash (reversed / rpc-style), then index. +// The code is based on btcd/btcutil/txsort/txsort.go. +func bip69inputLess(input1, input2 *swapserverrpc.PrevoutInfo) bool { + // Input hashes are the same, so compare the index. + var ihash, jhash chainhash.Hash + copy(ihash[:], input1.TxidBytes) + copy(jhash[:], input2.TxidBytes) + if ihash == jhash { + return input1.OutputIndex < input2.OutputIndex + } + + // At this point, the hashes are not equal, so reverse them to + // big-endian and return the result of the comparison. + const hashSize = chainhash.HashSize + for b := 0; b < hashSize/2; b++ { + ihash[b], ihash[hashSize-1-b] = ihash[hashSize-1-b], ihash[b] + jhash[b], jhash[hashSize-1-b] = jhash[hashSize-1-b], jhash[b] + } + return bytes.Compare(ihash[:], jhash[:]) == -1 +} + +// SelectDeposits sorts the deposits by amount in descending order. It then +// selects the deposits that are needed to cover the amount requested without +// leaving a dust change. It returns an error if the sum of deposits minus dust +// is less than the requested amount. +func SelectDeposits(deposits []*deposit.Deposit, amount int64) ( + []*deposit.Deposit, error) { + + // Check that sum of deposits covers the swap amount while leaving no + // dust change. + dustLimit := lnwallet.DustLimitForSize(input.P2TRSize) + var depositSum btcutil.Amount + for _, deposit := range deposits { + depositSum += deposit.Value + } + if depositSum-dustLimit < btcutil.Amount(amount) { + return nil, fmt.Errorf("insufficient funds to cover swap " + + "amount, try manually selecting deposits") + } + + // Sort the deposits by amount in descending order. + sort.Slice(deposits, func(i, j int) bool { + return deposits[i].Value > deposits[j].Value + }) + + // Select the deposits that are needed to cover the swap amount without + // leaving a dust change. + var selectedDeposits []*deposit.Deposit + var selectedAmount btcutil.Amount + for _, deposit := range deposits { + if selectedAmount >= btcutil.Amount(amount)+dustLimit { + break + } + selectedDeposits = append(selectedDeposits, deposit) + selectedAmount += deposit.Value + } + + return selectedDeposits, nil +} diff --git a/staticaddr/staticutil/utils_test.go b/staticaddr/staticutil/utils_test.go new file mode 100644 index 000000000..43da817f7 --- /dev/null +++ b/staticaddr/staticutil/utils_test.go @@ -0,0 +1,236 @@ +package staticutil + +import ( + "bytes" + "context" + "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/loop/staticaddr/address" + "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/script" + "github.com/lightninglabs/loop/swapserverrpc" + looptest "github.com/lightninglabs/loop/test" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" + "github.com/stretchr/testify/require" +) + +// mustHash converts a hex string to a chainhash.Hash and panics on error. +func mustHash(t *testing.T, s string) chainhash.Hash { + t.Helper() + h, err := chainhash.NewHashFromStr(s) + require.NoError(t, err) + return *h +} + +func TestToPrevOuts_Success(t *testing.T) { + // Prepare two distinct deposits with different outpoints and values. + d1 := &deposit.Deposit{ + OutPoint: wire.OutPoint{ + Hash: mustHash(t, "0000000000000000000000000000000000000000000000000000000000000001"), + Index: 0, + }, + Value: btcutil.Amount(12345), + } + + d2 := &deposit.Deposit{ + OutPoint: wire.OutPoint{ + Hash: mustHash(t, "1111111111111111111111111111111111111111111111111111111111111111"), + Index: 7, + }, + Value: btcutil.Amount(987654321), + } + + pkScript := []byte{0x51, 0x21, 0x02, 0x52} // arbitrary bytes + + prevOuts, err := ToPrevOuts([]*deposit.Deposit{d1, d2}, pkScript) + require.NoError(t, err) + + // We expect two entries. + require.Len(t, prevOuts, 2) + + // Check the first outpoint mapping. + txOut1, ok := prevOuts[d1.OutPoint] + require.True(t, ok, "expected outpoint d1 to be present") + require.EqualValues(t, int64(d1.Value), txOut1.Value) + require.Equal(t, pkScript, txOut1.PkScript) + + // Check the second outpoint mapping. + txOut2, ok := prevOuts[d2.OutPoint] + require.True(t, ok, "expected outpoint d2 to be present") + require.EqualValues(t, int64(d2.Value), txOut2.Value) + require.Equal(t, pkScript, txOut2.PkScript) + + // Ensure the keys in the map are exactly the outpoints we provided. + for op := range prevOuts { + require.True(t, op == d1.OutPoint || op == d2.OutPoint) + } +} + +func TestToPrevOuts_DuplicateOutpoint(t *testing.T) { + // Two deposits that share the exact same outpoint should cause an error. + shared := wire.OutPoint{ + Hash: mustHash(t, "2222222222222222222222222222222222222222222222222222222222222222"), + Index: 2, + } + + d1 := &deposit.Deposit{OutPoint: shared, Value: btcutil.Amount(100)} + d2 := &deposit.Deposit{OutPoint: shared, Value: btcutil.Amount(200)} + + _, err := ToPrevOuts([]*deposit.Deposit{d1, d2}, []byte{0x00}) + require.Error(t, err) +} + +func TestGetPrevoutInfo_ConversionAndSorting(t *testing.T) { + // Helper to create a hash from string. + must := func(s string) chainhash.Hash { + h, err := chainhash.NewHashFromStr(s) + require.NoError(t, err) + return *h + } + + // Choose txids such that after reversal, ordering is determined by the + // last byte of the original hex string. + txidA := must("0000000000000000000000000000000000000000000000000000000000000001") + txidB := must("0000000000000000000000000000000000000000000000000000000000000002") + + pkScript := []byte{0xaa, 0xbb} + + prevOuts := map[wire.OutPoint]*wire.TxOut{ + {Hash: txidA, Index: 5}: {Value: 11, PkScript: pkScript}, + {Hash: txidA, Index: 2}: {Value: 22, PkScript: pkScript}, + {Hash: txidB, Index: 0}: {Value: 33, PkScript: pkScript}, + } + + infos := GetPrevoutInfo(prevOuts) + + // Expect deterministic ordering: + // 1) All entries with txidA (..01) before txidB (..02) due to BIP-69 + // compare on reversed hashes. + // 2) Within txidA, index 2 before index 5. + require.Len(t, infos, 3) + + require.Equal(t, &swapserverrpc.PrevoutInfo{ + TxidBytes: txidA[:], + OutputIndex: 2, + Value: 22, + PkScript: pkScript, + }, infos[0]) + + require.Equal(t, &swapserverrpc.PrevoutInfo{ + TxidBytes: txidA[:], + OutputIndex: 5, + Value: 11, + PkScript: pkScript, + }, infos[1]) + + require.Equal(t, &swapserverrpc.PrevoutInfo{ + TxidBytes: txidB[:], + OutputIndex: 0, + Value: 33, + PkScript: pkScript, + }, infos[2]) +} + +func TestBip69InputLess_SameHashIndexOrder(t *testing.T) { + txid := make([]byte, 32) + txid[31] = 0x7f // Arbitrary value. + + a := &swapserverrpc.PrevoutInfo{TxidBytes: txid, OutputIndex: 1} + b := &swapserverrpc.PrevoutInfo{TxidBytes: txid, OutputIndex: 3} + + require.True(t, bip69inputLess(a, b)) + require.False(t, bip69inputLess(b, a)) +} + +func TestBip69InputLess_DifferentHashes(t *testing.T) { + // txid1 ends with 0x01, txid2 ends with 0x02. After reversing for + // comparison, txid1 should still come before txid2 in lexicographic + // order. + h1, _ := chainhash.NewHashFromStr("0000000000000000000000000000000000000000000000000000000000000001") + h2, _ := chainhash.NewHashFromStr("0000000000000000000000000000000000000000000000000000000000000002") + + a := &swapserverrpc.PrevoutInfo{TxidBytes: h1[:], OutputIndex: 9} + b := &swapserverrpc.PrevoutInfo{TxidBytes: h2[:], OutputIndex: 0} + + require.True(t, bip69inputLess(a, b)) + require.False(t, bip69inputLess(b, a)) +} + +func TestCreateMusig2Session_Success(t *testing.T) { + // Set up mock signer from loop/test package. + lnd := looptest.NewMockLnd() + signer := lnd.Signer + + // Create dummy key material for address parameters. + clientKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + serverKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + + params := &address.Parameters{ + ClientPubkey: clientKey.PubKey(), + ServerPubkey: serverKey.PubKey(), + Expiry: 10, + PkScript: []byte{0x51}, + KeyLocator: keychain.KeyLocator{Family: 1, Index: 2}, + } + + // Build a static address for tweak options. + staticAddr, err := script.NewStaticAddress( + input.MuSig2Version100RC2, int64(params.Expiry), params.ClientPubkey, params.ServerPubkey, + ) + require.NoError(t, err) + + sess, err := CreateMusig2Session(context.Background(), signer, params, staticAddr) + require.NoError(t, err) + require.NotNil(t, sess) +} + +func TestCreateMusig2Sessions_Multiple(t *testing.T) { + lnd := looptest.NewMockLnd() + signer := lnd.Signer + + // Keys/params/static address. + clientKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + serverKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + + params := &address.Parameters{ + ClientPubkey: clientKey.PubKey(), + ServerPubkey: serverKey.PubKey(), + Expiry: 12, + PkScript: []byte{0xaa}, + KeyLocator: keychain.KeyLocator{Family: 9, Index: 8}, + } + + staticAddr, err := script.NewStaticAddress( + input.MuSig2Version100RC2, int64(params.Expiry), params.ClientPubkey, params.ServerPubkey, + ) + require.NoError(t, err) + + // Prepare N deposits; only the length matters for session count. + deposits := []*deposit.Deposit{ + {OutPoint: wire.OutPoint{Index: 0}}, + {OutPoint: wire.OutPoint{Index: 1}}, + {OutPoint: wire.OutPoint{Index: 2}}, + } + + sessions, nonces, err := CreateMusig2Sessions( + context.Background(), signer, deposits, params, staticAddr, + ) + require.NoError(t, err) + require.Len(t, sessions, len(deposits)) + require.Len(t, nonces, len(deposits)) + + // The mock signer returns a zero-value PublicNonce; assert consistency. + for i := range sessions { + require.NotNil(t, sessions[i]) + require.True(t, bytes.Equal(nonces[i], sessions[i].PublicNonce[:])) + } +} diff --git a/staticaddr/withdraw/manager.go b/staticaddr/withdraw/manager.go index 09a567cfe..8c001b831 100644 --- a/staticaddr/withdraw/manager.go +++ b/staticaddr/withdraw/manager.go @@ -1,17 +1,17 @@ package withdraw import ( + "bytes" "context" "errors" "fmt" - "reflect" "strings" "sync" - "sync/atomic" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" @@ -19,9 +19,12 @@ import ( "github.com/btcsuite/btcwallet/chain" "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/staticutil" staticaddressrpc "github.com/lightninglabs/loop/swapserverrpc" "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" @@ -129,16 +132,13 @@ type Manager struct { // errChan forwards errors from the withdrawal manager to the server. errChan chan error - // initiationHeight stores the currently best known block height. - initiationHeight atomic.Uint32 - // finalizedWithdrawalTx are the finalized withdrawal transactions that // are published to the network and re-published on block arrivals. finalizedWithdrawalTxns map[chainhash.Hash]*wire.MsgTx } // NewManager creates a new deposit withdrawal manager. -func NewManager(cfg *ManagerConfig, currentHeight uint32) *Manager { +func NewManager(cfg *ManagerConfig) *Manager { m := &Manager{ cfg: cfg, finalizedWithdrawalTxns: make(map[chainhash.Hash]*wire.MsgTx), @@ -146,7 +146,6 @@ func NewManager(cfg *ManagerConfig, currentHeight uint32) *Manager { newWithdrawalRequestChan: make(chan newWithdrawalRequest), errChan: make(chan error), } - m.initiationHeight.Store(currentHeight) return m } @@ -324,7 +323,7 @@ func (m *Manager) WithdrawDeposits(ctx context.Context, // If not all passed outpoints are in state Deposited, we'll check if // they are all in state Withdrawing. If they are, then the user is - // requesting a fee bump, if not we'll return an error as we only allow + // requesting a fee bump, if not, we'll return an error as we only allow // fee bumping deposits in state Withdrawing. if !allDeposited { deposits, allWithdrawing = m.cfg.DepositManager.AllOutpointsActiveDeposits( @@ -401,7 +400,7 @@ func (m *Manager) WithdrawDeposits(ctx context.Context, } } - finalizedTx, err := m.createFinalizedWithdrawalTx( + finalizedTx, _, err := m.CreateFinalizedWithdrawalTx( ctx, deposits, withdrawalAddress, satPerVbyte, amount, ) if err != nil { @@ -496,17 +495,27 @@ func (m *Manager) WithdrawDeposits(ctx context.Context, return finalizedTx.TxID(), withdrawalAddress.String(), nil } -func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context, +func (m *Manager) CreateFinalizedWithdrawalTx(ctx context.Context, deposits []*deposit.Deposit, withdrawalAddress btcutil.Address, - satPerVbyte int64, selectedWithdrawalAmount int64) (*wire.MsgTx, + satPerVbyte int64, selectedWithdrawalAmount int64) (*wire.MsgTx, []byte, error) { // Create a musig2 session for each deposit. - withdrawalSessions, clientNonces, err := m.createMusig2Sessions( - ctx, deposits, + addrParams, err := m.cfg.AddressManager.GetStaticAddressParameters(ctx) + if err != nil { + return nil, nil, err + } + + staticAddress, err := m.cfg.AddressManager.GetStaticAddress(ctx) + if err != nil { + return nil, nil, err + } + + sessions, clientNonces, idx, err := staticutil.CreateMusig2SessionsPerDeposit( + ctx, m.cfg.Signer, deposits, addrParams, staticAddress, ) if err != nil { - return nil, err + return nil, nil, err } var withdrawalSweepFeeRate chainfee.SatPerKWeight @@ -516,7 +525,7 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context, ctx, defaultConfTarget, ) if err != nil { - return nil, err + return nil, nil, err } } else { withdrawalSweepFeeRate = chainfee.SatPerKVByte( @@ -526,19 +535,23 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context, params, err := m.cfg.AddressManager.GetStaticAddressParameters(ctx) if err != nil { - return nil, fmt.Errorf("couldn't get confirmation height for "+ - "deposit, %w", err) + return nil, nil, fmt.Errorf("couldn't get confirmation "+ + "height for deposit, %w", err) } outpoints := toOutpoints(deposits) - prevOuts := m.toPrevOuts(deposits, params.PkScript) - withdrawalTx, withdrawAmount, changeAmount, err := m.createWithdrawalTx( - ctx, outpoints, prevOuts, + prevOuts, err := staticutil.ToPrevOuts(deposits, params.PkScript) + if err != nil { + return nil, nil, err + } + + withdrawalTx, unsignedPsbt, err := m.createWithdrawalTx( + ctx, outpoints, deposits, prevOuts, btcutil.Amount(selectedWithdrawalAmount), withdrawalAddress, withdrawalSweepFeeRate, ) if err != nil { - return nil, err + return nil, nil, err } // Request the server to sign the withdrawal transaction. @@ -547,45 +560,40 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context, // expectation that the server just signs the transaction, without // performing fee calculations and dust considerations. The client is // responsible for that. - resp, err := m.cfg.StaticAddressServerClient.ServerWithdrawDeposits( - ctx, &staticaddressrpc.ServerWithdrawRequest{ - Outpoints: toPrevoutInfo(outpoints), - ClientNonces: clientNonces, - ClientSweepAddr: withdrawalAddress.String(), - TxFeeRate: uint64(withdrawalSweepFeeRate), - WithdrawAmount: int64(withdrawAmount), - ChangeAmount: int64(changeAmount), + // nolint:lll + sigResp, err := m.cfg.StaticAddressServerClient.ServerPsbtWithdrawDeposits( + ctx, &staticaddressrpc.ServerPsbtWithdrawRequest{ + WithdrawalPsbt: unsignedPsbt, + DepositToNonces: clientNonces, + PrevoutInfo: staticutil.GetPrevoutInfo(prevOuts), }, ) if err != nil { - return nil, err + return nil, nil, err } - coopServerNonces, err := toNonces(resp.ServerNonces) - if err != nil { - return nil, err + // Do some sanity checks. + txHash := withdrawalTx.TxHash() + if !bytes.Equal(txHash.CloneBytes(), sigResp.Txid) { + return nil, nil, errors.New("txid doesn't match") } - // Next we'll get our sweep tx signatures. - prevOutFetcher := txscript.NewMultiPrevOutFetcher(prevOuts) - _, err = m.signMusig2Tx( - ctx, prevOutFetcher, outpoints, m.cfg.Signer, withdrawalTx, - withdrawalSessions, coopServerNonces, - ) - if err != nil { - return nil, err + if len(sigResp.SigningInfo) != len(deposits) { + return nil, nil, errors.New("invalid number of " + + "deposit signatures") } - // Now we'll finalize the sweepless sweep transaction. - finalizedTx, err := m.finalizeMusig2Transaction( - ctx, outpoints, m.cfg.Signer, withdrawalSessions, - withdrawalTx, resp.Musig2SweepSigs, + // Next we'll get our sweep tx signatures. + prevOutFetcher := txscript.NewMultiPrevOutFetcher(prevOuts) + finalizedTx, err := m.signMusig2Tx( + ctx, prevOutFetcher, m.cfg.Signer, withdrawalTx, sessions, + sigResp.SigningInfo, idx, ) if err != nil { - return nil, err + return nil, nil, err } - return finalizedTx, nil + return finalizedTx, unsignedPsbt, nil } func (m *Manager) publishFinalizedWithdrawalTx(ctx context.Context, @@ -656,7 +664,7 @@ func (m *Manager) handleWithdrawal(ctx context.Context, m.cfg.ChainNotifier.RegisterConfirmationsNtfn( ctx, spentTx.SpenderTxHash, withdrawalPkscript, MinConfs, - int32(m.initiationHeight.Load()), + int32(d.ConfirmationHeight), ) select { case tx := <-confChan: @@ -721,105 +729,87 @@ func toOutpoints(deposits []*deposit.Deposit) []wire.OutPoint { // signMusig2Tx adds the server nonces to the musig2 sessions and signs the // transaction. func (m *Manager) signMusig2Tx(ctx context.Context, - prevOutFetcher *txscript.MultiPrevOutFetcher, outpoints []wire.OutPoint, + prevOutFetcher *txscript.MultiPrevOutFetcher, signer lndclient.SignerClient, tx *wire.MsgTx, - musig2sessions []*input.MuSig2SessionInfo, - counterPartyNonces [][musig2.PubNonceSize]byte) ([][]byte, error) { + sessions map[string]*input.MuSig2SessionInfo, + sigInfo map[string]*staticaddressrpc.ServerPsbtWithdrawSigningInfo, + depositsToIdx map[string]int) (*wire.MsgTx, error) { sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher) - sigs := make([][]byte, len(outpoints)) - for idx, outpoint := range outpoints { - if !reflect.DeepEqual(tx.TxIn[idx].PreviousOutPoint, - outpoint) { + // Create our digest. + var sigHash [32]byte - return nil, fmt.Errorf("tx input does not match " + - "deposits") + // We'll now add the nonce to our session and sign the tx. + for deposit, sigAndNonce := range sigInfo { + session, ok := sessions[deposit] + if !ok { + return nil, errors.New("session not found") } - taprootSigHash, err := txscript.CalcTaprootSignatureHash( - sigHashes, txscript.SigHashDefault, tx, idx, - prevOutFetcher, - ) - if err != nil { - return nil, err - } - - var digest [32]byte - copy(digest[:], taprootSigHash) - - // Register the server's nonce before attempting to create our - // partial signature. + nonce := [musig2.PubNonceSize]byte{} + copy(nonce[:], sigAndNonce.Nonce) haveAllNonces, err := signer.MuSig2RegisterNonces( - ctx, musig2sessions[idx].SessionID, - [][musig2.PubNonceSize]byte{counterPartyNonces[idx]}, + ctx, session.SessionID, + [][musig2.PubNonceSize]byte{nonce}, ) if err != nil { - return nil, err + return nil, fmt.Errorf("error registering nonces: "+ + "%w", err) } - // Sanity check that we have all the nonces. if !haveAllNonces { - return nil, fmt.Errorf("invalid MuSig2 session: " + - "nonces missing") + return nil, errors.New("expected all nonces to be " + + "registered") } - // Since our MuSig2 session has all nonces, we can now create - // the local partial signature by signing the sig hash. - sig, err := signer.MuSig2Sign( - ctx, musig2sessions[idx].SessionID, digest, false, + taprootSigHash, err := txscript.CalcTaprootSignatureHash( + sigHashes, txscript.SigHashDefault, tx, + depositsToIdx[deposit], prevOutFetcher, ) if err != nil { - return nil, err + return nil, fmt.Errorf("error calculating taproot "+ + "sig hash: %w", err) } - sigs[idx] = sig - } - - return sigs, nil -} - -func withdrawalValue(prevOuts map[wire.OutPoint]*wire.TxOut) btcutil.Amount { - var totalValue btcutil.Amount - for _, prevOut := range prevOuts { - totalValue += btcutil.Amount(prevOut.Value) - } - return totalValue -} + copy(sigHash[:], taprootSigHash) -// toNonces converts a byte slice to a 66 byte slice. -func toNonces(nonces [][]byte) ([][musig2.PubNonceSize]byte, error) { - res := make([][musig2.PubNonceSize]byte, 0, len(nonces)) - for _, n := range nonces { - nonce, err := byteSliceTo66ByteSlice(n) + // Sign the tx. + _, err = signer.MuSig2Sign( + ctx, session.SessionID, sigHash, false, + ) if err != nil { - return nil, err + return nil, fmt.Errorf("error signing tx: %w", err) } - res = append(res, nonce) - } + // Combine the signature with the client signature. + haveAllSigs, sig, err := signer.MuSig2CombineSig( + ctx, session.SessionID, + [][]byte{sigAndNonce.Sig}, + ) + if err != nil { + return nil, fmt.Errorf("error combining signature: "+ + "%w", err) + } - return res, nil -} + if !haveAllSigs { + return nil, errors.New("expected all signatures to " + + "be combined") + } -// byteSliceTo66ByteSlice converts a byte slice to a 66 byte slice. -func byteSliceTo66ByteSlice(b []byte) ([musig2.PubNonceSize]byte, error) { - if len(b) != musig2.PubNonceSize { - return [musig2.PubNonceSize]byte{}, - fmt.Errorf("invalid byte slice length") + tx.TxIn[depositsToIdx[deposit]].Witness = wire.TxWitness{ + sig, + } } - var res [musig2.PubNonceSize]byte - copy(res[:], b) - - return res, nil + return tx, nil } func (m *Manager) createWithdrawalTx(ctx context.Context, - outpoints []wire.OutPoint, prevOuts map[wire.OutPoint]*wire.TxOut, + outpoints []wire.OutPoint, deposits []*deposit.Deposit, + prevOuts map[wire.OutPoint]*wire.TxOut, selectedWithdrawalAmount btcutil.Amount, withdrawAddr btcutil.Address, - feeRate chainfee.SatPerKWeight) (*wire.MsgTx, btcutil.Amount, - btcutil.Amount, error) { + feeRate chainfee.SatPerKWeight) (*wire.MsgTx, []byte, error) { // First Create the tx. msgTx := wire.NewMsgTx(2) @@ -832,81 +822,23 @@ func (m *Manager) createWithdrawalTx(ctx context.Context, }) } - var ( - hasChange bool - dustLimit = lnwallet.DustLimitForSize(input.P2TRSize) - withdrawalAmount btcutil.Amount - changeAmount btcutil.Amount + withdrawalAmount, changeAmount, err := CalculateWithdrawalTxValaues( + deposits, selectedWithdrawalAmount, feeRate, + withdrawAddr, lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE, ) - - // Estimate the transaction weight without change. - weight, err := withdrawalTxWeight(len(outpoints), withdrawAddr, false) if err != nil { - return nil, 0, 0, err + return nil, nil, fmt.Errorf("error calculating funding tx "+ + "values: %w", err) } - feeWithoutChange := feeRate.FeeForWeightRoundUp(weight) - - // If the user selected a fraction of the sum of the selected deposits - // to withdraw, check if a change output is needed. - totalWithdrawalAmount := withdrawalValue(prevOuts) - if selectedWithdrawalAmount > 0 { - // Estimate the transaction weight with change. - weight, err = withdrawalTxWeight( - len(outpoints), withdrawAddr, true, - ) - if err != nil { - return nil, 0, 0, err - } - feeWithChange := feeRate.FeeForWeightRoundUp(weight) - - // The available change that can cover fees is the total - // selected deposit amount minus the selected withdrawal amount. - change := totalWithdrawalAmount - selectedWithdrawalAmount - switch { - case change-feeWithChange >= dustLimit: - // If the change can cover the fees without turning into - // dust, add a non-dust change output. - hasChange = true - changeAmount = change - feeWithChange - withdrawalAmount = selectedWithdrawalAmount - - case change-feeWithoutChange >= 0: - // If the change is dust, we give it to the miners. - hasChange = false - withdrawalAmount = selectedWithdrawalAmount - - default: - // If the fees eat into our withdrawal amount, we fail - // the withdrawal. - return nil, 0, 0, fmt.Errorf("the change doesn't " + - "cover for fees. Consider lowering the fee " + - "rate or decrease the withdrawal amount") - } - } else { - // If the user wants to withdraw the full amount, we don't need - // a change output. - hasChange = false - withdrawalAmount = totalWithdrawalAmount - feeWithoutChange - } - - if withdrawalAmount < dustLimit { - return nil, 0, 0, fmt.Errorf("withdrawal amount is below " + - "dust limit") - } - - if changeAmount < 0 { - return nil, 0, 0, fmt.Errorf("change amount is negative") - } - - // For the users convenience we check that the change amount is lower + // For the user's convenience, we check that the change amount is lower // than each input's value. If the change amount is higher than an - // input's value, we wouldn't have to include that input into the + // input's value, we wouldn't have to include that input in the // transaction, saving fees. for outpoint, txOut := range prevOuts { if changeAmount >= btcutil.Amount(txOut.Value) { - return nil, 0, 0, fmt.Errorf("change amount %v is "+ - "higher than an input value %v of input %v", + return nil, nil, fmt.Errorf("change amount %v "+ + "is higher than an input value %v of input %v", changeAmount, btcutil.Amount(txOut.Value), outpoint) } @@ -914,7 +846,7 @@ func (m *Manager) createWithdrawalTx(ctx context.Context, withdrawScript, err := txscript.PayToAddrScript(withdrawAddr) if err != nil { - return nil, 0, 0, err + return nil, nil, err } // Create the withdrawal output. @@ -923,13 +855,13 @@ func (m *Manager) createWithdrawalTx(ctx context.Context, PkScript: withdrawScript, }) - if hasChange { + if changeAmount > 0 { // Send change back to the same static address. staticAddress, err := m.cfg.AddressManager.GetStaticAddress(ctx) if err != nil { log.Errorf("error retrieving taproot address %v", err) - return nil, 0, 0, fmt.Errorf("withdrawal failed") + return nil, nil, fmt.Errorf("withdrawal failed") } changeAddress, err := btcutil.NewAddressTaproot( @@ -937,12 +869,12 @@ func (m *Manager) createWithdrawalTx(ctx context.Context, m.cfg.ChainParams, ) if err != nil { - return nil, 0, 0, err + return nil, nil, err } changeScript, err := txscript.PayToAddrScript(changeAddress) if err != nil { - return nil, 0, 0, err + return nil, nil, err } msgTx.AddTxOut(&wire.TxOut{ @@ -951,153 +883,187 @@ func (m *Manager) createWithdrawalTx(ctx context.Context, }) } - return msgTx, withdrawalAmount, changeAmount, nil -} + // Create psbt for the withdrawal. + psbtx, err := psbt.NewFromUnsignedTx(msgTx) + if err != nil { + return nil, nil, err + } -// withdrawalFee returns the weight for the withdrawal transaction. -func withdrawalTxWeight(numInputs int, sweepAddress btcutil.Address, - hasChange bool) (lntypes.WeightUnit, error) { + pInputs := make([]psbt.PInput, len(outpoints)) + for i, op := range outpoints { + prevOut := prevOuts[op] + pInputs[i] = psbt.PInput{ + WitnessUtxo: &wire.TxOut{ + Value: prevOut.Value, + PkScript: prevOut.PkScript, + }, + } + } + psbtx.Inputs = pInputs - var weightEstimator input.TxWeightEstimator - for i := 0; i < numInputs; i++ { - weightEstimator.AddTaprootKeySpendInput( - txscript.SigHashDefault, - ) + // Serialize the psbt to send it to the client. + var psbtBuf bytes.Buffer + err = psbtx.Serialize(&psbtBuf) + if err != nil { + return nil, nil, err } - // Get the weight of the sweep output. - switch sweepAddress.(type) { - case *btcutil.AddressWitnessPubKeyHash: - weightEstimator.AddP2WKHOutput() + return msgTx, psbtBuf.Bytes(), nil +} - case *btcutil.AddressTaproot: - weightEstimator.AddP2TROutput() +func CalculateWithdrawalTxValaues(deposits []*deposit.Deposit, + localAmount btcutil.Amount, feeRate chainfee.SatPerKWeight, + withdrawalAddress btcutil.Address, + commitmentType lnrpc.CommitmentType) (btcutil.Amount, btcutil.Amount, + error) { - default: - return 0, fmt.Errorf("invalid sweep address type %T", - sweepAddress) + if withdrawalAddress == nil && + commitmentType == lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE { + + return 0, 0, fmt.Errorf("either address or commitment type " + + "must be specified") } - // If there's a change output add the weight of the static address. - if hasChange { - weightEstimator.AddP2TROutput() + var ( + err error + withdrawalFundingAmt btcutil.Amount + changeAmount btcutil.Amount + dustLimit = lnwallet.DustLimitForSize(input.P2TRSize) + isChannelOpen = commitmentType != lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE + ) + + totalDepositAmount := btcutil.Amount(0) + for _, d := range deposits { + totalDepositAmount += d.Value } - return weightEstimator.Weight(), nil -} + // Estimate the open channel transaction fee without change. + hasChange := false + weight, err := WithdrawalTxWeight( + len(deposits), withdrawalAddress, commitmentType, hasChange, + ) + if err != nil { + return 0, 0, err + } + feeWithoutChange := feeRate.FeeForWeight(weight) -// finalizeMusig2Transaction creates the finalized transactions for either -// the htlc or the cooperative close. -func (m *Manager) finalizeMusig2Transaction(ctx context.Context, - outpoints []wire.OutPoint, signer lndclient.SignerClient, - musig2Sessions []*input.MuSig2SessionInfo, - tx *wire.MsgTx, serverSigs [][]byte) (*wire.MsgTx, error) { - - for idx := range outpoints { - haveAllSigs, finalSig, err := signer.MuSig2CombineSig( - ctx, musig2Sessions[idx].SessionID, - [][]byte{serverSigs[idx]}, + // If the user selected a local amount for the channel, check if a + // change output is needed. + if localAmount > 0 { + // Estimate the transaction weight with change. + hasChange = true + weightWithChange, err := WithdrawalTxWeight( + len(deposits), withdrawalAddress, commitmentType, + hasChange, ) if err != nil { - return nil, err + return 0, 0, err } + feeWithChange := feeRate.FeeForWeight(weightWithChange) - if !haveAllSigs { - return nil, fmt.Errorf("missing sigs") - } + // The available change that can cover fees is the total + // selected deposit amount minus the local channel amount. + change := totalDepositAmount - localAmount - tx.TxIn[idx].Witness = wire.TxWitness{finalSig} - } + switch { + case change-feeWithChange >= dustLimit: + // If the change can cover the fees without turning into + // dust, add a non-dust change output. + changeAmount = change - feeWithChange + withdrawalFundingAmt = localAmount - return tx, nil -} + case change-feeWithoutChange >= 0: + // If the change is dust, we give it to the miners. + withdrawalFundingAmt = localAmount -func toPrevoutInfo(outpoints []wire.OutPoint) []*staticaddressrpc.PrevoutInfo { - var result []*staticaddressrpc.PrevoutInfo - for _, o := range outpoints { - outP := o - outpoint := &staticaddressrpc.PrevoutInfo{ - TxidBytes: outP.Hash[:], - OutputIndex: outP.Index, + default: + // If the fees eat into our local channel amount, we + // fail to open the channel. + return 0, 0, fmt.Errorf("the change doesn't " + + "cover for fees. Consider lowering the fee " + + "rate or decrease the local amount") } - result = append(result, outpoint) + } else { + // If the user wants to open the channel with the total value of + // deposits, we don't need a change output. + withdrawalFundingAmt = totalDepositAmount - feeWithoutChange } - return result -} + if withdrawalFundingAmt < dustLimit { + return 0, 0, fmt.Errorf("withdrawal amount is below dust limit") + } -// createMusig2Sessions creates a musig2 session for a number of deposits. -func (m *Manager) createMusig2Sessions(ctx context.Context, - deposits []*deposit.Deposit) ([]*input.MuSig2SessionInfo, [][]byte, - error) { + if changeAmount < 0 { + return 0, 0, fmt.Errorf("change amount is negative") + } - musig2Sessions := make([]*input.MuSig2SessionInfo, len(deposits)) - clientNonces := make([][]byte, len(deposits)) + // Ensure that the channel funding amount is at least in the amount of + // lnd's minimum channel size. + if isChannelOpen && withdrawalFundingAmt < funding.MinChanFundingSize { + return 0, 0, fmt.Errorf("channel funding amount %v is lower "+ + "than the minimum channel funding size %v", + withdrawalFundingAmt, funding.MinChanFundingSize) + } - // Create the sessions and nonces from the deposits. - for i := 0; i < len(deposits); i++ { - session, err := m.createMusig2Session(ctx) - if err != nil { - return nil, nil, err + // For the user's convenience, we check that the change amount is lower + // than each input's value. If the change amount is higher than an + // input's value, we wouldn't have to include that input in the + // transaction, saving fees. + for _, d := range deposits { + if changeAmount >= d.Value { + return 0, 0, fmt.Errorf("change amount %v is "+ + "higher than an input value %v of input %v", + changeAmount, d.Value, d.OutPoint.String()) } - - musig2Sessions[i] = session - clientNonces[i] = session.PublicNonce[:] } - return musig2Sessions, clientNonces, nil + return withdrawalFundingAmt, changeAmount, nil } -// Musig2CreateSession creates a musig2 session for the deposit. -func (m *Manager) createMusig2Session(ctx context.Context) ( - *input.MuSig2SessionInfo, error) { - - addressParams, err := m.cfg.AddressManager.GetStaticAddressParameters( - ctx, - ) - if err != nil { - return nil, fmt.Errorf("couldn't get confirmation height for "+ - "deposit, %w", err) - } - - signers := [][]byte{ - addressParams.ClientPubkey.SerializeCompressed(), - addressParams.ServerPubkey.SerializeCompressed(), - } +// WithdrawalTxWeight returns the weight for the withdrawal transaction. +func WithdrawalTxWeight(numInputs int, sweepAddress btcutil.Address, + commitmentType lnrpc.CommitmentType, + hasChange bool) (lntypes.WeightUnit, error) { - address, err := m.cfg.AddressManager.GetStaticAddress(ctx) - if err != nil { - return nil, fmt.Errorf("couldn't get confirmation height for "+ - "deposit, %w", err) + var weightEstimator input.TxWeightEstimator + for i := 0; i < numInputs; i++ { + weightEstimator.AddTaprootKeySpendInput( + txscript.SigHashDefault, + ) } - expiryLeaf := address.TimeoutLeaf + if commitmentType != lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE { + switch commitmentType { + case lnrpc.CommitmentType_SIMPLE_TAPROOT: + weightEstimator.AddP2TROutput() - rootHash := expiryLeaf.TapHash() + default: + weightEstimator.AddP2WSHOutput() + } + } else { + // Get the weight of the sweep output. + switch sweepAddress.(type) { + case *btcutil.AddressWitnessPubKeyHash: + weightEstimator.AddP2WKHOutput() - return m.cfg.Signer.MuSig2CreateSession( - ctx, input.MuSig2Version100RC2, &addressParams.KeyLocator, - signers, lndclient.MuSig2TaprootTweakOpt(rootHash[:], false), - ) -} + case *btcutil.AddressWitnessScriptHash: + weightEstimator.AddP2WSHOutput() -func (m *Manager) toPrevOuts(deposits []*deposit.Deposit, - pkScript []byte) map[wire.OutPoint]*wire.TxOut { + case *btcutil.AddressTaproot: + weightEstimator.AddP2TROutput() - prevOuts := make(map[wire.OutPoint]*wire.TxOut, len(deposits)) - for _, d := range deposits { - outpoint := wire.OutPoint{ - Hash: d.Hash, - Index: d.Index, - } - txOut := &wire.TxOut{ - Value: int64(d.Value), - PkScript: pkScript, + default: + return 0, fmt.Errorf("invalid sweep address type %T", + sweepAddress) } - prevOuts[outpoint] = txOut } - return prevOuts + // If there's a change output add the weight of the static address. + if hasChange { + weightEstimator.AddP2TROutput() + } + + return weightEstimator.Weight(), nil } func (m *Manager) republishWithdrawals(ctx context.Context) error { diff --git a/swapserverrpc/staticaddr.pb.go b/swapserverrpc/staticaddr.pb.go index af7061865..5dc7ed300 100644 --- a/swapserverrpc/staticaddr.pb.go +++ b/swapserverrpc/staticaddr.pb.go @@ -336,9 +336,9 @@ type ServerWithdrawResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The sweep sigs that the server generated for the htlc. + // The sweep sigs that the server generated for the withdrawal tx. Musig2SweepSigs [][]byte `protobuf:"bytes,1,rep,name=musig2_sweep_sigs,json=musig2SweepSigs,proto3" json:"musig2_sweep_sigs,omitempty"` - // The nonces that the server used to generate the sweepless sweep sigs. + // The nonces that the server used to generate the withdrawal sigs. ServerNonces [][]byte `protobuf:"bytes,2,rep,name=server_nonces,json=serverNonces,proto3" json:"server_nonces,omitempty"` } @@ -388,6 +388,187 @@ func (x *ServerWithdrawResponse) GetServerNonces() [][]byte { return nil } +type ServerPsbtWithdrawRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The withdrawal psbt. + WithdrawalPsbt []byte `protobuf:"bytes,1,opt,name=withdrawal_psbt,json=withdrawalPsbt,proto3" json:"withdrawal_psbt,omitempty"` + // The map of deposit txid:idx to the nonce used by the client. + DepositToNonces map[string][]byte `protobuf:"bytes,2,rep,name=deposit_to_nonces,json=depositToNonces,proto3" json:"deposit_to_nonces,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // The prevout information of the psbt. + PrevoutInfo []*PrevoutInfo `protobuf:"bytes,3,rep,name=prevout_info,json=prevoutInfo,proto3" json:"prevout_info,omitempty"` +} + +func (x *ServerPsbtWithdrawRequest) Reset() { + *x = ServerPsbtWithdrawRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_staticaddr_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerPsbtWithdrawRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerPsbtWithdrawRequest) ProtoMessage() {} + +func (x *ServerPsbtWithdrawRequest) ProtoReflect() protoreflect.Message { + mi := &file_staticaddr_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServerPsbtWithdrawRequest.ProtoReflect.Descriptor instead. +func (*ServerPsbtWithdrawRequest) Descriptor() ([]byte, []int) { + return file_staticaddr_proto_rawDescGZIP(), []int{5} +} + +func (x *ServerPsbtWithdrawRequest) GetWithdrawalPsbt() []byte { + if x != nil { + return x.WithdrawalPsbt + } + return nil +} + +func (x *ServerPsbtWithdrawRequest) GetDepositToNonces() map[string][]byte { + if x != nil { + return x.DepositToNonces + } + return nil +} + +func (x *ServerPsbtWithdrawRequest) GetPrevoutInfo() []*PrevoutInfo { + if x != nil { + return x.PrevoutInfo + } + return nil +} + +type ServerPsbtWithdrawResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The txid of the psbt that the client wants to push the sigs for. + Txid []byte `protobuf:"bytes,1,opt,name=txid,proto3" json:"txid,omitempty"` + // A map of deposits in format txid:idx to the nonces. + SigningInfo map[string]*ServerPsbtWithdrawSigningInfo `protobuf:"bytes,2,rep,name=signing_info,json=signingInfo,proto3" json:"signing_info,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *ServerPsbtWithdrawResponse) Reset() { + *x = ServerPsbtWithdrawResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_staticaddr_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerPsbtWithdrawResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerPsbtWithdrawResponse) ProtoMessage() {} + +func (x *ServerPsbtWithdrawResponse) ProtoReflect() protoreflect.Message { + mi := &file_staticaddr_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServerPsbtWithdrawResponse.ProtoReflect.Descriptor instead. +func (*ServerPsbtWithdrawResponse) Descriptor() ([]byte, []int) { + return file_staticaddr_proto_rawDescGZIP(), []int{6} +} + +func (x *ServerPsbtWithdrawResponse) GetTxid() []byte { + if x != nil { + return x.Txid + } + return nil +} + +func (x *ServerPsbtWithdrawResponse) GetSigningInfo() map[string]*ServerPsbtWithdrawSigningInfo { + if x != nil { + return x.SigningInfo + } + return nil +} + +type ServerPsbtWithdrawSigningInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The nonces that the client used to generate the partial withdrawal tx + // sigs. + Nonce []byte `protobuf:"bytes,1,opt,name=nonce,proto3" json:"nonce,omitempty"` + // The musig2 htlc sigs that the client generated for the withdrawal tx. + Sig []byte `protobuf:"bytes,2,opt,name=sig,proto3" json:"sig,omitempty"` +} + +func (x *ServerPsbtWithdrawSigningInfo) Reset() { + *x = ServerPsbtWithdrawSigningInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_staticaddr_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerPsbtWithdrawSigningInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerPsbtWithdrawSigningInfo) ProtoMessage() {} + +func (x *ServerPsbtWithdrawSigningInfo) ProtoReflect() protoreflect.Message { + mi := &file_staticaddr_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServerPsbtWithdrawSigningInfo.ProtoReflect.Descriptor instead. +func (*ServerPsbtWithdrawSigningInfo) Descriptor() ([]byte, []int) { + return file_staticaddr_proto_rawDescGZIP(), []int{7} +} + +func (x *ServerPsbtWithdrawSigningInfo) GetNonce() []byte { + if x != nil { + return x.Nonce + } + return nil +} + +func (x *ServerPsbtWithdrawSigningInfo) GetSig() []byte { + if x != nil { + return x.Sig + } + return nil +} + type ServerStaticAddressLoopInRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -442,7 +623,7 @@ type ServerStaticAddressLoopInRequest struct { func (x *ServerStaticAddressLoopInRequest) Reset() { *x = ServerStaticAddressLoopInRequest{} if protoimpl.UnsafeEnabled { - mi := &file_staticaddr_proto_msgTypes[5] + mi := &file_staticaddr_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -455,7 +636,7 @@ func (x *ServerStaticAddressLoopInRequest) String() string { func (*ServerStaticAddressLoopInRequest) ProtoMessage() {} func (x *ServerStaticAddressLoopInRequest) ProtoReflect() protoreflect.Message { - mi := &file_staticaddr_proto_msgTypes[5] + mi := &file_staticaddr_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -468,7 +649,7 @@ func (x *ServerStaticAddressLoopInRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ServerStaticAddressLoopInRequest.ProtoReflect.Descriptor instead. func (*ServerStaticAddressLoopInRequest) Descriptor() ([]byte, []int) { - return file_staticaddr_proto_rawDescGZIP(), []int{5} + return file_staticaddr_proto_rawDescGZIP(), []int{8} } func (x *ServerStaticAddressLoopInRequest) GetHtlcClientPubKey() []byte { @@ -564,7 +745,7 @@ type ServerStaticAddressLoopInResponse struct { func (x *ServerStaticAddressLoopInResponse) Reset() { *x = ServerStaticAddressLoopInResponse{} if protoimpl.UnsafeEnabled { - mi := &file_staticaddr_proto_msgTypes[6] + mi := &file_staticaddr_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -577,7 +758,7 @@ func (x *ServerStaticAddressLoopInResponse) String() string { func (*ServerStaticAddressLoopInResponse) ProtoMessage() {} func (x *ServerStaticAddressLoopInResponse) ProtoReflect() protoreflect.Message { - mi := &file_staticaddr_proto_msgTypes[6] + mi := &file_staticaddr_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -590,7 +771,7 @@ func (x *ServerStaticAddressLoopInResponse) ProtoReflect() protoreflect.Message // Deprecated: Use ServerStaticAddressLoopInResponse.ProtoReflect.Descriptor instead. func (*ServerStaticAddressLoopInResponse) Descriptor() ([]byte, []int) { - return file_staticaddr_proto_rawDescGZIP(), []int{6} + return file_staticaddr_proto_rawDescGZIP(), []int{9} } func (x *ServerStaticAddressLoopInResponse) GetHtlcServerPubKey() []byte { @@ -642,7 +823,7 @@ type ServerHtlcSigningInfo struct { func (x *ServerHtlcSigningInfo) Reset() { *x = ServerHtlcSigningInfo{} if protoimpl.UnsafeEnabled { - mi := &file_staticaddr_proto_msgTypes[7] + mi := &file_staticaddr_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -655,7 +836,7 @@ func (x *ServerHtlcSigningInfo) String() string { func (*ServerHtlcSigningInfo) ProtoMessage() {} func (x *ServerHtlcSigningInfo) ProtoReflect() protoreflect.Message { - mi := &file_staticaddr_proto_msgTypes[7] + mi := &file_staticaddr_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -668,7 +849,7 @@ func (x *ServerHtlcSigningInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ServerHtlcSigningInfo.ProtoReflect.Descriptor instead. func (*ServerHtlcSigningInfo) Descriptor() ([]byte, []int) { - return file_staticaddr_proto_rawDescGZIP(), []int{7} + return file_staticaddr_proto_rawDescGZIP(), []int{10} } func (x *ServerHtlcSigningInfo) GetNonces() [][]byte { @@ -703,7 +884,7 @@ type PushStaticAddressHtlcSigsRequest struct { func (x *PushStaticAddressHtlcSigsRequest) Reset() { *x = PushStaticAddressHtlcSigsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_staticaddr_proto_msgTypes[8] + mi := &file_staticaddr_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -716,7 +897,7 @@ func (x *PushStaticAddressHtlcSigsRequest) String() string { func (*PushStaticAddressHtlcSigsRequest) ProtoMessage() {} func (x *PushStaticAddressHtlcSigsRequest) ProtoReflect() protoreflect.Message { - mi := &file_staticaddr_proto_msgTypes[8] + mi := &file_staticaddr_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -729,7 +910,7 @@ func (x *PushStaticAddressHtlcSigsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PushStaticAddressHtlcSigsRequest.ProtoReflect.Descriptor instead. func (*PushStaticAddressHtlcSigsRequest) Descriptor() ([]byte, []int) { - return file_staticaddr_proto_rawDescGZIP(), []int{8} + return file_staticaddr_proto_rawDescGZIP(), []int{11} } func (x *PushStaticAddressHtlcSigsRequest) GetSwapHash() []byte { @@ -774,7 +955,7 @@ type ClientHtlcSigningInfo struct { func (x *ClientHtlcSigningInfo) Reset() { *x = ClientHtlcSigningInfo{} if protoimpl.UnsafeEnabled { - mi := &file_staticaddr_proto_msgTypes[9] + mi := &file_staticaddr_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -787,7 +968,7 @@ func (x *ClientHtlcSigningInfo) String() string { func (*ClientHtlcSigningInfo) ProtoMessage() {} func (x *ClientHtlcSigningInfo) ProtoReflect() protoreflect.Message { - mi := &file_staticaddr_proto_msgTypes[9] + mi := &file_staticaddr_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -800,7 +981,7 @@ func (x *ClientHtlcSigningInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ClientHtlcSigningInfo.ProtoReflect.Descriptor instead. func (*ClientHtlcSigningInfo) Descriptor() ([]byte, []int) { - return file_staticaddr_proto_rawDescGZIP(), []int{9} + return file_staticaddr_proto_rawDescGZIP(), []int{12} } func (x *ClientHtlcSigningInfo) GetNonces() [][]byte { @@ -826,7 +1007,7 @@ type PushStaticAddressHtlcSigsResponse struct { func (x *PushStaticAddressHtlcSigsResponse) Reset() { *x = PushStaticAddressHtlcSigsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_staticaddr_proto_msgTypes[10] + mi := &file_staticaddr_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -839,7 +1020,7 @@ func (x *PushStaticAddressHtlcSigsResponse) String() string { func (*PushStaticAddressHtlcSigsResponse) ProtoMessage() {} func (x *PushStaticAddressHtlcSigsResponse) ProtoReflect() protoreflect.Message { - mi := &file_staticaddr_proto_msgTypes[10] + mi := &file_staticaddr_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -852,7 +1033,7 @@ func (x *PushStaticAddressHtlcSigsResponse) ProtoReflect() protoreflect.Message // Deprecated: Use PushStaticAddressHtlcSigsResponse.ProtoReflect.Descriptor instead. func (*PushStaticAddressHtlcSigsResponse) Descriptor() ([]byte, []int) { - return file_staticaddr_proto_rawDescGZIP(), []int{10} + return file_staticaddr_proto_rawDescGZIP(), []int{13} } type PushStaticAddressSweeplessSigsRequest struct { @@ -876,7 +1057,7 @@ type PushStaticAddressSweeplessSigsRequest struct { func (x *PushStaticAddressSweeplessSigsRequest) Reset() { *x = PushStaticAddressSweeplessSigsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_staticaddr_proto_msgTypes[11] + mi := &file_staticaddr_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -889,7 +1070,7 @@ func (x *PushStaticAddressSweeplessSigsRequest) String() string { func (*PushStaticAddressSweeplessSigsRequest) ProtoMessage() {} func (x *PushStaticAddressSweeplessSigsRequest) ProtoReflect() protoreflect.Message { - mi := &file_staticaddr_proto_msgTypes[11] + mi := &file_staticaddr_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -902,7 +1083,7 @@ func (x *PushStaticAddressSweeplessSigsRequest) ProtoReflect() protoreflect.Mess // Deprecated: Use PushStaticAddressSweeplessSigsRequest.ProtoReflect.Descriptor instead. func (*PushStaticAddressSweeplessSigsRequest) Descriptor() ([]byte, []int) { - return file_staticaddr_proto_rawDescGZIP(), []int{11} + return file_staticaddr_proto_rawDescGZIP(), []int{14} } func (x *PushStaticAddressSweeplessSigsRequest) GetSwapHash() []byte { @@ -948,7 +1129,7 @@ type ClientSweeplessSigningInfo struct { func (x *ClientSweeplessSigningInfo) Reset() { *x = ClientSweeplessSigningInfo{} if protoimpl.UnsafeEnabled { - mi := &file_staticaddr_proto_msgTypes[12] + mi := &file_staticaddr_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -961,7 +1142,7 @@ func (x *ClientSweeplessSigningInfo) String() string { func (*ClientSweeplessSigningInfo) ProtoMessage() {} func (x *ClientSweeplessSigningInfo) ProtoReflect() protoreflect.Message { - mi := &file_staticaddr_proto_msgTypes[12] + mi := &file_staticaddr_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -974,7 +1155,7 @@ func (x *ClientSweeplessSigningInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ClientSweeplessSigningInfo.ProtoReflect.Descriptor instead. func (*ClientSweeplessSigningInfo) Descriptor() ([]byte, []int) { - return file_staticaddr_proto_rawDescGZIP(), []int{12} + return file_staticaddr_proto_rawDescGZIP(), []int{15} } func (x *ClientSweeplessSigningInfo) GetNonce() []byte { @@ -1000,7 +1181,7 @@ type PushStaticAddressSweeplessSigsResponse struct { func (x *PushStaticAddressSweeplessSigsResponse) Reset() { *x = PushStaticAddressSweeplessSigsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_staticaddr_proto_msgTypes[13] + mi := &file_staticaddr_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1013,7 +1194,7 @@ func (x *PushStaticAddressSweeplessSigsResponse) String() string { func (*PushStaticAddressSweeplessSigsResponse) ProtoMessage() {} func (x *PushStaticAddressSweeplessSigsResponse) ProtoReflect() protoreflect.Message { - mi := &file_staticaddr_proto_msgTypes[13] + mi := &file_staticaddr_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1026,7 +1207,7 @@ func (x *PushStaticAddressSweeplessSigsResponse) ProtoReflect() protoreflect.Mes // Deprecated: Use PushStaticAddressSweeplessSigsResponse.ProtoReflect.Descriptor instead. func (*PushStaticAddressSweeplessSigsResponse) Descriptor() ([]byte, []int) { - return file_staticaddr_proto_rawDescGZIP(), []int{13} + return file_staticaddr_proto_rawDescGZIP(), []int{16} } var File_staticaddr_proto protoreflect.FileDescriptor @@ -1077,156 +1258,200 @@ var file_staticaddr_proto_rawDesc = []byte{ 0x52, 0x0f, 0x6d, 0x75, 0x73, 0x69, 0x67, 0x32, 0x53, 0x77, 0x65, 0x65, 0x70, 0x53, 0x69, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x22, 0xae, 0x03, 0x0a, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, - 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x68, - 0x74, 0x6c, 0x63, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x68, 0x74, 0x6c, 0x63, 0x43, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, - 0x61, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, - 0x77, 0x61, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x10, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x4f, 0x75, 0x74, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x69, 0x6e, 0x76, - 0x6f, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x77, 0x61, 0x70, - 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x68, 0x6f, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x48, - 0x6f, 0x70, 0x12, 0x50, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x6c, - 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, - 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x41, 0x67, - 0x65, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x17, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, - 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x61, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x04, 0x66, 0x61, 0x73, 0x74, 0x22, 0xe1, 0x02, 0x0a, 0x21, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, - 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, - 0x13, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x68, 0x74, 0x6c, 0x63, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0b, - 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0a, 0x68, 0x74, 0x6c, 0x63, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x4c, 0x0a, - 0x12, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, + 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x22, 0xa6, 0x02, 0x0a, 0x19, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x50, 0x73, 0x62, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x61, 0x6c, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x77, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x50, 0x73, 0x62, 0x74, 0x12, 0x63, 0x0a, + 0x11, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x73, 0x62, 0x74, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x54, 0x6f, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x0f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x54, 0x6f, 0x4e, 0x6f, 0x6e, 0x63, + 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x76, 0x6f, 0x75, 0x74, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x72, 0x65, 0x76, 0x6f, 0x75, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, + 0x70, 0x72, 0x65, 0x76, 0x6f, 0x75, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x42, 0x0a, 0x14, 0x44, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x54, 0x6f, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0xf1, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x73, 0x62, 0x74, 0x57, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, + 0x69, 0x64, 0x12, 0x57, 0x0a, 0x0c, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x73, 0x62, 0x74, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x69, + 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, + 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x66, 0x0a, 0x10, 0x53, + 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x3c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x26, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x50, 0x73, 0x62, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x53, 0x69, 0x67, + 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x47, 0x0a, 0x1d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x73, 0x62, + 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0xae, 0x03, 0x0a, + 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, + 0x68, 0x74, 0x6c, 0x63, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, + 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2b, 0x0a, + 0x11, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x77, + 0x61, 0x70, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x73, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x19, 0x0a, + 0x08, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x07, 0x6c, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x50, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x75, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x17, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x70, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x61, 0x73, + 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x66, 0x61, 0x73, 0x74, 0x22, 0xe1, 0x02, + 0x0a, 0x21, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x13, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x10, 0x68, 0x74, 0x6c, 0x63, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, 0x4b, + 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x68, 0x74, 0x6c, 0x63, 0x45, 0x78, 0x70, + 0x69, 0x72, 0x79, 0x12, 0x4c, 0x0a, 0x12, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, + 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x10, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x4b, 0x0a, 0x12, 0x68, 0x69, 0x67, 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x68, 0x74, + 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x74, + 0x6c, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x68, + 0x69, 0x67, 0x68, 0x46, 0x65, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x51, + 0x0a, 0x15, 0x65, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x68, 0x74, + 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x74, + 0x6c, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x12, 0x65, + 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, 0x46, 0x65, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, + 0x6f, 0x22, 0x4a, 0x0a, 0x15, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x74, 0x6c, 0x63, 0x53, + 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x6f, + 0x6e, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x22, 0xad, 0x02, + 0x0a, 0x20, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, + 0x4c, 0x0a, 0x12, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x6f, + 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x74, 0x6c, 0x63, + 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x73, 0x74, 0x61, + 0x6e, 0x64, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4b, 0x0a, + 0x12, 0x68, 0x69, 0x67, 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, - 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x73, 0x74, 0x61, 0x6e, 0x64, - 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4b, 0x0a, 0x12, 0x68, - 0x69, 0x67, 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x6e, - 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x68, 0x69, 0x67, 0x68, 0x46, 0x65, 0x65, - 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x51, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x72, - 0x65, 0x6d, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x6e, - 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x12, 0x65, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, - 0x46, 0x65, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x4a, 0x0a, 0x15, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x19, 0x0a, 0x08, - 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, - 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x22, 0xad, 0x02, 0x0a, 0x20, 0x50, 0x75, 0x73, 0x68, - 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, - 0x63, 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x73, 0x77, 0x61, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x08, 0x73, 0x77, 0x61, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x4c, 0x0a, 0x12, 0x73, 0x74, 0x61, - 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, - 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x48, - 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4b, 0x0a, 0x12, 0x68, 0x69, 0x67, 0x68, 0x5f, - 0x66, 0x65, 0x65, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x68, 0x69, 0x67, 0x68, 0x46, 0x65, 0x65, 0x48, 0x74, 0x6c, 0x63, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x51, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, 0x5f, - 0x66, 0x65, 0x65, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x12, 0x65, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, 0x46, 0x65, 0x65, 0x48, - 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x43, 0x0a, 0x15, 0x43, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, - 0x52, 0x06, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x69, 0x67, 0x73, 0x22, 0x23, 0x0a, 0x21, - 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0xc6, 0x02, 0x0a, 0x25, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, - 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, - 0x77, 0x61, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, - 0x73, 0x77, 0x61, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x62, 0x0a, 0x0c, - 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, - 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, - 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x63, 0x0a, 0x10, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, - 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, - 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x44, 0x0a, 0x1a, 0x43, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, - 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, - 0x22, 0x28, 0x0a, 0x26, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, - 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x26, 0x0a, 0x1c, 0x53, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, 0x56, 0x30, - 0x10, 0x00, 0x32, 0xb5, 0x04, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x57, 0x0a, 0x10, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, - 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, - 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x16, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x1e, 0x2e, - 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x57, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, - 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x57, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, - 0x0a, 0x19, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x12, 0x29, 0x2e, 0x6c, 0x6f, - 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x72, 0x0a, 0x19, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x73, 0x12, - 0x29, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, - 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, - 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6c, 0x6f, 0x6f, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x1e, 0x50, 0x75, 0x73, 0x68, 0x53, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, - 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x73, 0x12, 0x2e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, - 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, - 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, - 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x73, 0x77, 0x61, 0x70, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, + 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x68, 0x69, 0x67, 0x68, 0x46, + 0x65, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x51, 0x0a, 0x15, 0x65, 0x78, + 0x74, 0x72, 0x65, 0x6d, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, + 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x12, 0x65, 0x78, 0x74, 0x72, 0x65, + 0x6d, 0x65, 0x46, 0x65, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x43, 0x0a, + 0x15, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x69, + 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x73, 0x69, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x69, + 0x67, 0x73, 0x22, 0x23, 0x0a, 0x21, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc6, 0x02, 0x0a, 0x25, 0x50, 0x75, 0x73, 0x68, + 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, + 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, + 0x69, 0x64, 0x12, 0x62, 0x0a, 0x0c, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, + 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x69, + 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x63, 0x0a, 0x10, 0x53, + 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x39, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, + 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x44, 0x0a, 0x1a, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, + 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, + 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x03, 0x73, 0x69, 0x67, 0x22, 0x28, 0x0a, 0x26, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, + 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2a, 0x26, 0x0a, 0x1c, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x06, 0x0a, 0x02, 0x56, 0x30, 0x10, 0x00, 0x32, 0x9c, 0x05, 0x0a, 0x13, 0x53, 0x74, 0x61, + 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x12, 0x57, 0x0a, 0x10, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x16, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x1a, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x73, + 0x62, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x50, 0x73, 0x62, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x73, 0x62, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, 0x19, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x12, 0x29, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x72, 0x0a, 0x19, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x73, 0x12, 0x29, 0x2e, 0x6c, + 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x1e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, + 0x73, 0x73, 0x53, 0x69, 0x67, 0x73, 0x12, 0x2e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x75, 0x73, 0x68, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, + 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x73, 0x77, 0x61, 0x70, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1242,7 +1467,7 @@ func file_staticaddr_proto_rawDescGZIP() []byte { } var file_staticaddr_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_staticaddr_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_staticaddr_proto_msgTypes = make([]protoimpl.MessageInfo, 20) var file_staticaddr_proto_goTypes = []any{ (StaticAddressProtocolVersion)(0), // 0: looprpc.StaticAddressProtocolVersion (*ServerNewAddressRequest)(nil), // 1: looprpc.ServerNewAddressRequest @@ -1250,46 +1475,57 @@ var file_staticaddr_proto_goTypes = []any{ (*ServerAddressParameters)(nil), // 3: looprpc.ServerAddressParameters (*ServerWithdrawRequest)(nil), // 4: looprpc.ServerWithdrawRequest (*ServerWithdrawResponse)(nil), // 5: looprpc.ServerWithdrawResponse - (*ServerStaticAddressLoopInRequest)(nil), // 6: looprpc.ServerStaticAddressLoopInRequest - (*ServerStaticAddressLoopInResponse)(nil), // 7: looprpc.ServerStaticAddressLoopInResponse - (*ServerHtlcSigningInfo)(nil), // 8: looprpc.ServerHtlcSigningInfo - (*PushStaticAddressHtlcSigsRequest)(nil), // 9: looprpc.PushStaticAddressHtlcSigsRequest - (*ClientHtlcSigningInfo)(nil), // 10: looprpc.ClientHtlcSigningInfo - (*PushStaticAddressHtlcSigsResponse)(nil), // 11: looprpc.PushStaticAddressHtlcSigsResponse - (*PushStaticAddressSweeplessSigsRequest)(nil), // 12: looprpc.PushStaticAddressSweeplessSigsRequest - (*ClientSweeplessSigningInfo)(nil), // 13: looprpc.ClientSweeplessSigningInfo - (*PushStaticAddressSweeplessSigsResponse)(nil), // 14: looprpc.PushStaticAddressSweeplessSigsResponse - nil, // 15: looprpc.PushStaticAddressSweeplessSigsRequest.SigningInfoEntry - (*PrevoutInfo)(nil), // 16: looprpc.PrevoutInfo + (*ServerPsbtWithdrawRequest)(nil), // 6: looprpc.ServerPsbtWithdrawRequest + (*ServerPsbtWithdrawResponse)(nil), // 7: looprpc.ServerPsbtWithdrawResponse + (*ServerPsbtWithdrawSigningInfo)(nil), // 8: looprpc.ServerPsbtWithdrawSigningInfo + (*ServerStaticAddressLoopInRequest)(nil), // 9: looprpc.ServerStaticAddressLoopInRequest + (*ServerStaticAddressLoopInResponse)(nil), // 10: looprpc.ServerStaticAddressLoopInResponse + (*ServerHtlcSigningInfo)(nil), // 11: looprpc.ServerHtlcSigningInfo + (*PushStaticAddressHtlcSigsRequest)(nil), // 12: looprpc.PushStaticAddressHtlcSigsRequest + (*ClientHtlcSigningInfo)(nil), // 13: looprpc.ClientHtlcSigningInfo + (*PushStaticAddressHtlcSigsResponse)(nil), // 14: looprpc.PushStaticAddressHtlcSigsResponse + (*PushStaticAddressSweeplessSigsRequest)(nil), // 15: looprpc.PushStaticAddressSweeplessSigsRequest + (*ClientSweeplessSigningInfo)(nil), // 16: looprpc.ClientSweeplessSigningInfo + (*PushStaticAddressSweeplessSigsResponse)(nil), // 17: looprpc.PushStaticAddressSweeplessSigsResponse + nil, // 18: looprpc.ServerPsbtWithdrawRequest.DepositToNoncesEntry + nil, // 19: looprpc.ServerPsbtWithdrawResponse.SigningInfoEntry + nil, // 20: looprpc.PushStaticAddressSweeplessSigsRequest.SigningInfoEntry + (*PrevoutInfo)(nil), // 21: looprpc.PrevoutInfo } var file_staticaddr_proto_depIdxs = []int32{ 0, // 0: looprpc.ServerNewAddressRequest.protocol_version:type_name -> looprpc.StaticAddressProtocolVersion 3, // 1: looprpc.ServerNewAddressResponse.params:type_name -> looprpc.ServerAddressParameters - 16, // 2: looprpc.ServerWithdrawRequest.outpoints:type_name -> looprpc.PrevoutInfo - 0, // 3: looprpc.ServerStaticAddressLoopInRequest.protocol_version:type_name -> looprpc.StaticAddressProtocolVersion - 8, // 4: looprpc.ServerStaticAddressLoopInResponse.standard_htlc_info:type_name -> looprpc.ServerHtlcSigningInfo - 8, // 5: looprpc.ServerStaticAddressLoopInResponse.high_fee_htlc_info:type_name -> looprpc.ServerHtlcSigningInfo - 8, // 6: looprpc.ServerStaticAddressLoopInResponse.extreme_fee_htlc_info:type_name -> looprpc.ServerHtlcSigningInfo - 10, // 7: looprpc.PushStaticAddressHtlcSigsRequest.standard_htlc_info:type_name -> looprpc.ClientHtlcSigningInfo - 10, // 8: looprpc.PushStaticAddressHtlcSigsRequest.high_fee_htlc_info:type_name -> looprpc.ClientHtlcSigningInfo - 10, // 9: looprpc.PushStaticAddressHtlcSigsRequest.extreme_fee_htlc_info:type_name -> looprpc.ClientHtlcSigningInfo - 15, // 10: looprpc.PushStaticAddressSweeplessSigsRequest.signing_info:type_name -> looprpc.PushStaticAddressSweeplessSigsRequest.SigningInfoEntry - 13, // 11: looprpc.PushStaticAddressSweeplessSigsRequest.SigningInfoEntry.value:type_name -> looprpc.ClientSweeplessSigningInfo - 1, // 12: looprpc.StaticAddressServer.ServerNewAddress:input_type -> looprpc.ServerNewAddressRequest - 4, // 13: looprpc.StaticAddressServer.ServerWithdrawDeposits:input_type -> looprpc.ServerWithdrawRequest - 6, // 14: looprpc.StaticAddressServer.ServerStaticAddressLoopIn:input_type -> looprpc.ServerStaticAddressLoopInRequest - 9, // 15: looprpc.StaticAddressServer.PushStaticAddressHtlcSigs:input_type -> looprpc.PushStaticAddressHtlcSigsRequest - 12, // 16: looprpc.StaticAddressServer.PushStaticAddressSweeplessSigs:input_type -> looprpc.PushStaticAddressSweeplessSigsRequest - 2, // 17: looprpc.StaticAddressServer.ServerNewAddress:output_type -> looprpc.ServerNewAddressResponse - 5, // 18: looprpc.StaticAddressServer.ServerWithdrawDeposits:output_type -> looprpc.ServerWithdrawResponse - 7, // 19: looprpc.StaticAddressServer.ServerStaticAddressLoopIn:output_type -> looprpc.ServerStaticAddressLoopInResponse - 11, // 20: looprpc.StaticAddressServer.PushStaticAddressHtlcSigs:output_type -> looprpc.PushStaticAddressHtlcSigsResponse - 14, // 21: looprpc.StaticAddressServer.PushStaticAddressSweeplessSigs:output_type -> looprpc.PushStaticAddressSweeplessSigsResponse - 17, // [17:22] is the sub-list for method output_type - 12, // [12:17] is the sub-list for method input_type - 12, // [12:12] is the sub-list for extension type_name - 12, // [12:12] is the sub-list for extension extendee - 0, // [0:12] is the sub-list for field type_name + 21, // 2: looprpc.ServerWithdrawRequest.outpoints:type_name -> looprpc.PrevoutInfo + 18, // 3: looprpc.ServerPsbtWithdrawRequest.deposit_to_nonces:type_name -> looprpc.ServerPsbtWithdrawRequest.DepositToNoncesEntry + 21, // 4: looprpc.ServerPsbtWithdrawRequest.prevout_info:type_name -> looprpc.PrevoutInfo + 19, // 5: looprpc.ServerPsbtWithdrawResponse.signing_info:type_name -> looprpc.ServerPsbtWithdrawResponse.SigningInfoEntry + 0, // 6: looprpc.ServerStaticAddressLoopInRequest.protocol_version:type_name -> looprpc.StaticAddressProtocolVersion + 11, // 7: looprpc.ServerStaticAddressLoopInResponse.standard_htlc_info:type_name -> looprpc.ServerHtlcSigningInfo + 11, // 8: looprpc.ServerStaticAddressLoopInResponse.high_fee_htlc_info:type_name -> looprpc.ServerHtlcSigningInfo + 11, // 9: looprpc.ServerStaticAddressLoopInResponse.extreme_fee_htlc_info:type_name -> looprpc.ServerHtlcSigningInfo + 13, // 10: looprpc.PushStaticAddressHtlcSigsRequest.standard_htlc_info:type_name -> looprpc.ClientHtlcSigningInfo + 13, // 11: looprpc.PushStaticAddressHtlcSigsRequest.high_fee_htlc_info:type_name -> looprpc.ClientHtlcSigningInfo + 13, // 12: looprpc.PushStaticAddressHtlcSigsRequest.extreme_fee_htlc_info:type_name -> looprpc.ClientHtlcSigningInfo + 20, // 13: looprpc.PushStaticAddressSweeplessSigsRequest.signing_info:type_name -> looprpc.PushStaticAddressSweeplessSigsRequest.SigningInfoEntry + 8, // 14: looprpc.ServerPsbtWithdrawResponse.SigningInfoEntry.value:type_name -> looprpc.ServerPsbtWithdrawSigningInfo + 16, // 15: looprpc.PushStaticAddressSweeplessSigsRequest.SigningInfoEntry.value:type_name -> looprpc.ClientSweeplessSigningInfo + 1, // 16: looprpc.StaticAddressServer.ServerNewAddress:input_type -> looprpc.ServerNewAddressRequest + 4, // 17: looprpc.StaticAddressServer.ServerWithdrawDeposits:input_type -> looprpc.ServerWithdrawRequest + 6, // 18: looprpc.StaticAddressServer.ServerPsbtWithdrawDeposits:input_type -> looprpc.ServerPsbtWithdrawRequest + 9, // 19: looprpc.StaticAddressServer.ServerStaticAddressLoopIn:input_type -> looprpc.ServerStaticAddressLoopInRequest + 12, // 20: looprpc.StaticAddressServer.PushStaticAddressHtlcSigs:input_type -> looprpc.PushStaticAddressHtlcSigsRequest + 15, // 21: looprpc.StaticAddressServer.PushStaticAddressSweeplessSigs:input_type -> looprpc.PushStaticAddressSweeplessSigsRequest + 2, // 22: looprpc.StaticAddressServer.ServerNewAddress:output_type -> looprpc.ServerNewAddressResponse + 5, // 23: looprpc.StaticAddressServer.ServerWithdrawDeposits:output_type -> looprpc.ServerWithdrawResponse + 7, // 24: looprpc.StaticAddressServer.ServerPsbtWithdrawDeposits:output_type -> looprpc.ServerPsbtWithdrawResponse + 10, // 25: looprpc.StaticAddressServer.ServerStaticAddressLoopIn:output_type -> looprpc.ServerStaticAddressLoopInResponse + 14, // 26: looprpc.StaticAddressServer.PushStaticAddressHtlcSigs:output_type -> looprpc.PushStaticAddressHtlcSigsResponse + 17, // 27: looprpc.StaticAddressServer.PushStaticAddressSweeplessSigs:output_type -> looprpc.PushStaticAddressSweeplessSigsResponse + 22, // [22:28] is the sub-list for method output_type + 16, // [16:22] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name } func init() { file_staticaddr_proto_init() } @@ -1360,7 +1596,7 @@ func file_staticaddr_proto_init() { } } file_staticaddr_proto_msgTypes[5].Exporter = func(v any, i int) any { - switch v := v.(*ServerStaticAddressLoopInRequest); i { + switch v := v.(*ServerPsbtWithdrawRequest); i { case 0: return &v.state case 1: @@ -1372,7 +1608,7 @@ func file_staticaddr_proto_init() { } } file_staticaddr_proto_msgTypes[6].Exporter = func(v any, i int) any { - switch v := v.(*ServerStaticAddressLoopInResponse); i { + switch v := v.(*ServerPsbtWithdrawResponse); i { case 0: return &v.state case 1: @@ -1384,7 +1620,7 @@ func file_staticaddr_proto_init() { } } file_staticaddr_proto_msgTypes[7].Exporter = func(v any, i int) any { - switch v := v.(*ServerHtlcSigningInfo); i { + switch v := v.(*ServerPsbtWithdrawSigningInfo); i { case 0: return &v.state case 1: @@ -1396,7 +1632,7 @@ func file_staticaddr_proto_init() { } } file_staticaddr_proto_msgTypes[8].Exporter = func(v any, i int) any { - switch v := v.(*PushStaticAddressHtlcSigsRequest); i { + switch v := v.(*ServerStaticAddressLoopInRequest); i { case 0: return &v.state case 1: @@ -1408,7 +1644,7 @@ func file_staticaddr_proto_init() { } } file_staticaddr_proto_msgTypes[9].Exporter = func(v any, i int) any { - switch v := v.(*ClientHtlcSigningInfo); i { + switch v := v.(*ServerStaticAddressLoopInResponse); i { case 0: return &v.state case 1: @@ -1420,7 +1656,7 @@ func file_staticaddr_proto_init() { } } file_staticaddr_proto_msgTypes[10].Exporter = func(v any, i int) any { - switch v := v.(*PushStaticAddressHtlcSigsResponse); i { + switch v := v.(*ServerHtlcSigningInfo); i { case 0: return &v.state case 1: @@ -1432,7 +1668,7 @@ func file_staticaddr_proto_init() { } } file_staticaddr_proto_msgTypes[11].Exporter = func(v any, i int) any { - switch v := v.(*PushStaticAddressSweeplessSigsRequest); i { + switch v := v.(*PushStaticAddressHtlcSigsRequest); i { case 0: return &v.state case 1: @@ -1444,7 +1680,7 @@ func file_staticaddr_proto_init() { } } file_staticaddr_proto_msgTypes[12].Exporter = func(v any, i int) any { - switch v := v.(*ClientSweeplessSigningInfo); i { + switch v := v.(*ClientHtlcSigningInfo); i { case 0: return &v.state case 1: @@ -1456,6 +1692,42 @@ func file_staticaddr_proto_init() { } } file_staticaddr_proto_msgTypes[13].Exporter = func(v any, i int) any { + switch v := v.(*PushStaticAddressHtlcSigsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_staticaddr_proto_msgTypes[14].Exporter = func(v any, i int) any { + switch v := v.(*PushStaticAddressSweeplessSigsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_staticaddr_proto_msgTypes[15].Exporter = func(v any, i int) any { + switch v := v.(*ClientSweeplessSigningInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_staticaddr_proto_msgTypes[16].Exporter = func(v any, i int) any { switch v := v.(*PushStaticAddressSweeplessSigsResponse); i { case 0: return &v.state @@ -1474,7 +1746,7 @@ func file_staticaddr_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_staticaddr_proto_rawDesc, NumEnums: 1, - NumMessages: 15, + NumMessages: 20, NumExtensions: 0, NumServices: 1, }, diff --git a/swapserverrpc/staticaddr.proto b/swapserverrpc/staticaddr.proto index 684d36e92..98d8eb589 100644 --- a/swapserverrpc/staticaddr.proto +++ b/swapserverrpc/staticaddr.proto @@ -29,6 +29,14 @@ service StaticAddressServer { rpc ServerWithdrawDeposits (ServerWithdrawRequest) returns (ServerWithdrawResponse); + // ServerPsbtWithdrawDeposits allows to cooperatively sweep deposits that + // haven't timed out yet to the client's wallet. In contrast to + // ServerWithdrawDeposits which provides the paramters to form the + // withdrawal transaction, this service method will provide a psbt which + // is signed by the server. + rpc ServerPsbtWithdrawDeposits (ServerPsbtWithdrawRequest) + returns (ServerPsbtWithdrawResponse); + // ServerStaticAddressLoopIn initiates a static address loop-in swap. The // server will respond with htlc details that the client can use to // construct and sign the htlc tx. @@ -92,13 +100,41 @@ message ServerWithdrawRequest { } message ServerWithdrawResponse { - // The sweep sigs that the server generated for the htlc. + // The sweep sigs that the server generated for the withdrawal tx. repeated bytes musig2_sweep_sigs = 1; - // The nonces that the server used to generate the sweepless sweep sigs. + // The nonces that the server used to generate the withdrawal sigs. repeated bytes server_nonces = 2; } +message ServerPsbtWithdrawRequest { + // The withdrawal psbt. + bytes withdrawal_psbt = 1; + + // The map of deposit txid:idx to the nonce used by the client. + map deposit_to_nonces = 2; + + // The prevout information of the psbt. + repeated PrevoutInfo prevout_info = 3; +} + +message ServerPsbtWithdrawResponse { + // The txid of the psbt that the client wants to push the sigs for. + bytes txid = 1; + + // A map of deposits in format txid:idx to the nonces. + map signing_info = 2; +} + +message ServerPsbtWithdrawSigningInfo { + // The nonces that the client used to generate the partial withdrawal tx + // sigs. + bytes nonce = 1; + + // The musig2 htlc sigs that the client generated for the withdrawal tx. + bytes sig = 2; +} + message ServerStaticAddressLoopInRequest { // The client's public key for the htlc output. bytes htlc_client_pub_key = 1; diff --git a/swapserverrpc/staticaddr_grpc.pb.go b/swapserverrpc/staticaddr_grpc.pb.go index e64cc9e05..e01528558 100644 --- a/swapserverrpc/staticaddr_grpc.pb.go +++ b/swapserverrpc/staticaddr_grpc.pb.go @@ -26,6 +26,12 @@ type StaticAddressServerClient interface { // haven't timed out yet to the client's wallet. The server will generate // the partial sigs for the client's selected deposits. ServerWithdrawDeposits(ctx context.Context, in *ServerWithdrawRequest, opts ...grpc.CallOption) (*ServerWithdrawResponse, error) + // ServerPsbtWithdrawDeposits allows to cooperatively sweep deposits that + // haven't timed out yet to the client's wallet. In contrast to + // ServerWithdrawDeposits which provides the paramters to form the + // withdrawal transaction, this service method will provide a psbt which + // is signed by the server. + ServerPsbtWithdrawDeposits(ctx context.Context, in *ServerPsbtWithdrawRequest, opts ...grpc.CallOption) (*ServerPsbtWithdrawResponse, error) // ServerStaticAddressLoopIn initiates a static address loop-in swap. The // server will respond with htlc details that the client can use to // construct and sign the htlc tx. @@ -63,6 +69,15 @@ func (c *staticAddressServerClient) ServerWithdrawDeposits(ctx context.Context, return out, nil } +func (c *staticAddressServerClient) ServerPsbtWithdrawDeposits(ctx context.Context, in *ServerPsbtWithdrawRequest, opts ...grpc.CallOption) (*ServerPsbtWithdrawResponse, error) { + out := new(ServerPsbtWithdrawResponse) + err := c.cc.Invoke(ctx, "/looprpc.StaticAddressServer/ServerPsbtWithdrawDeposits", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *staticAddressServerClient) ServerStaticAddressLoopIn(ctx context.Context, in *ServerStaticAddressLoopInRequest, opts ...grpc.CallOption) (*ServerStaticAddressLoopInResponse, error) { out := new(ServerStaticAddressLoopInResponse) err := c.cc.Invoke(ctx, "/looprpc.StaticAddressServer/ServerStaticAddressLoopIn", in, out, opts...) @@ -102,6 +117,12 @@ type StaticAddressServerServer interface { // haven't timed out yet to the client's wallet. The server will generate // the partial sigs for the client's selected deposits. ServerWithdrawDeposits(context.Context, *ServerWithdrawRequest) (*ServerWithdrawResponse, error) + // ServerPsbtWithdrawDeposits allows to cooperatively sweep deposits that + // haven't timed out yet to the client's wallet. In contrast to + // ServerWithdrawDeposits which provides the paramters to form the + // withdrawal transaction, this service method will provide a psbt which + // is signed by the server. + ServerPsbtWithdrawDeposits(context.Context, *ServerPsbtWithdrawRequest) (*ServerPsbtWithdrawResponse, error) // ServerStaticAddressLoopIn initiates a static address loop-in swap. The // server will respond with htlc details that the client can use to // construct and sign the htlc tx. @@ -124,6 +145,9 @@ func (UnimplementedStaticAddressServerServer) ServerNewAddress(context.Context, func (UnimplementedStaticAddressServerServer) ServerWithdrawDeposits(context.Context, *ServerWithdrawRequest) (*ServerWithdrawResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ServerWithdrawDeposits not implemented") } +func (UnimplementedStaticAddressServerServer) ServerPsbtWithdrawDeposits(context.Context, *ServerPsbtWithdrawRequest) (*ServerPsbtWithdrawResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ServerPsbtWithdrawDeposits not implemented") +} func (UnimplementedStaticAddressServerServer) ServerStaticAddressLoopIn(context.Context, *ServerStaticAddressLoopInRequest) (*ServerStaticAddressLoopInResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ServerStaticAddressLoopIn not implemented") } @@ -182,6 +206,24 @@ func _StaticAddressServer_ServerWithdrawDeposits_Handler(srv interface{}, ctx co return interceptor(ctx, in, info, handler) } +func _StaticAddressServer_ServerPsbtWithdrawDeposits_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ServerPsbtWithdrawRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StaticAddressServerServer).ServerPsbtWithdrawDeposits(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.StaticAddressServer/ServerPsbtWithdrawDeposits", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StaticAddressServerServer).ServerPsbtWithdrawDeposits(ctx, req.(*ServerPsbtWithdrawRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _StaticAddressServer_ServerStaticAddressLoopIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ServerStaticAddressLoopInRequest) if err := dec(in); err != nil { @@ -251,6 +293,10 @@ var StaticAddressServer_ServiceDesc = grpc.ServiceDesc{ MethodName: "ServerWithdrawDeposits", Handler: _StaticAddressServer_ServerWithdrawDeposits_Handler, }, + { + MethodName: "ServerPsbtWithdrawDeposits", + Handler: _StaticAddressServer_ServerPsbtWithdrawDeposits_Handler, + }, { MethodName: "ServerStaticAddressLoopIn", Handler: _StaticAddressServer_ServerStaticAddressLoopIn_Handler,