From 7d72f7114175378fe03d3e0af85c3489354e9f39 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Tue, 30 Jul 2024 12:16:48 +0200 Subject: [PATCH 01/14] looprpc: static address loop-ins rpcs for loop client and server are added to facilitate static address swap requests. --- cmd/loop/staticaddr.go | 3 - loopd/swapclient_server.go | 20 +- looprpc/client.pb.go | 1111 +++++++++++++------- looprpc/client.proto | 192 +++- looprpc/client.swagger.json | 95 +- looprpc/client_grpc.pb.go | 40 + looprpc/swapclient.pb.json.go | 25 + swapserverrpc/server.pb.go | 1694 ++++++++++++++++++++++++++----- swapserverrpc/server.proto | 174 ++++ swapserverrpc/server_grpc.pb.go | 160 +++ 10 files changed, 2859 insertions(+), 655 deletions(-) diff --git a/cmd/loop/staticaddr.go b/cmd/loop/staticaddr.go index 4de4e7c28..dcd02834c 100644 --- a/cmd/loop/staticaddr.go +++ b/cmd/loop/staticaddr.go @@ -238,9 +238,6 @@ func summary(ctx *cli.Context) error { case "expired": filterState = looprpc.DepositState_EXPIRED - case "failed": - filterState = looprpc.DepositState_FAILED_STATE - default: filterState = looprpc.DepositState_UNKNOWN_STATE } diff --git a/loopd/swapclient_server.go b/loopd/swapclient_server.go index af05ec1e8..ab3252b9c 100644 --- a/loopd/swapclient_server.go +++ b/loopd/swapclient_server.go @@ -1558,13 +1558,13 @@ func (s *swapClientServer) depositSummary(ctx context.Context, } return &looprpc.StaticAddressSummaryResponse{ - StaticAddress: address.String(), - TotalNumDeposits: uint32(totalNumDeposits), - ValueUnconfirmed: valueUnconfirmed, - ValueDeposited: valueDeposited, - ValueExpired: valueExpired, - ValueWithdrawn: valueWithdrawn, - FilteredDeposits: clientDeposits, + StaticAddress: address.String(), + TotalNumDeposits: uint32(totalNumDeposits), + ValueUnconfirmedSatoshis: valueUnconfirmed, + ValueDepositedSatoshis: valueDeposited, + ValueExpiredSatoshis: valueExpired, + ValueWithdrawnSatoshis: valueWithdrawn, + FilteredDeposits: clientDeposits, }, nil } @@ -1613,9 +1613,6 @@ func toClientState(state fsm.StateType) looprpc.DepositState { case deposit.Expired: return looprpc.DepositState_EXPIRED - case deposit.Failed: - return looprpc.DepositState_FAILED_STATE - default: return looprpc.DepositState_UNKNOWN_STATE } @@ -1641,9 +1638,6 @@ func toServerState(state looprpc.DepositState) fsm.StateType { case looprpc.DepositState_EXPIRED: return deposit.Expired - case looprpc.DepositState_FAILED_STATE: - return deposit.Failed - default: return fsm.EmptyState } diff --git a/looprpc/client.pb.go b/looprpc/client.pb.go index e73dafee6..1506d54d0 100644 --- a/looprpc/client.pb.go +++ b/looprpc/client.pb.go @@ -453,40 +453,57 @@ const ( DepositState_WITHDRAWING DepositState = 2 // WITHDRAWN indicates that the deposit has been withdrawn. DepositState_WITHDRAWN DepositState = 3 + // LOOPING_IN indicates that the deposit is currently being used in a static + // address loop-in swap. + DepositState_LOOPING_IN DepositState = 4 + // LOOPED_IN indicates that the deposit was used in a static address loop-in + // swap. + DepositState_LOOPED_IN DepositState = 5 + // SWEEP_HTLC_TIMEOUT indicates that the deposit is part of an active loop-in + // of which the respective htlc was published by the server and the timeout + // path has opened up for the client to sweep. + DepositState_SWEEP_HTLC_TIMEOUT DepositState = 6 + // HTLC_TIMEOUT_SWEPT indicates that the timeout path of the htlc has been + // swept by the client. + DepositState_HTLC_TIMEOUT_SWEPT DepositState = 7 // PUBLISH_EXPIRED indicates that the deposit has expired and the sweep // transaction has been published. - DepositState_PUBLISH_EXPIRED DepositState = 4 + DepositState_PUBLISH_EXPIRED DepositState = 8 // WAIT_FOR_EXPIRY_SWEEP indicates that the deposit has expired and the sweep // transaction has not yet been sufficiently confirmed. - DepositState_WAIT_FOR_EXPIRY_SWEEP DepositState = 5 + DepositState_WAIT_FOR_EXPIRY_SWEEP DepositState = 9 // EXPIRED indicates that the deposit has expired and the sweep transaction // has been sufficiently confirmed. - DepositState_EXPIRED DepositState = 6 - // FAILED_STATE indicates that the deposit has failed. - DepositState_FAILED_STATE DepositState = 7 + DepositState_EXPIRED DepositState = 10 ) // Enum value maps for DepositState. var ( DepositState_name = map[int32]string{ - 0: "UNKNOWN_STATE", - 1: "DEPOSITED", - 2: "WITHDRAWING", - 3: "WITHDRAWN", - 4: "PUBLISH_EXPIRED", - 5: "WAIT_FOR_EXPIRY_SWEEP", - 6: "EXPIRED", - 7: "FAILED_STATE", + 0: "UNKNOWN_STATE", + 1: "DEPOSITED", + 2: "WITHDRAWING", + 3: "WITHDRAWN", + 4: "LOOPING_IN", + 5: "LOOPED_IN", + 6: "SWEEP_HTLC_TIMEOUT", + 7: "HTLC_TIMEOUT_SWEPT", + 8: "PUBLISH_EXPIRED", + 9: "WAIT_FOR_EXPIRY_SWEEP", + 10: "EXPIRED", } DepositState_value = map[string]int32{ "UNKNOWN_STATE": 0, "DEPOSITED": 1, "WITHDRAWING": 2, "WITHDRAWN": 3, - "PUBLISH_EXPIRED": 4, - "WAIT_FOR_EXPIRY_SWEEP": 5, - "EXPIRED": 6, - "FAILED_STATE": 7, + "LOOPING_IN": 4, + "LOOPED_IN": 5, + "SWEEP_HTLC_TIMEOUT": 6, + "HTLC_TIMEOUT_SWEPT": 7, + "PUBLISH_EXPIRED": 8, + "WAIT_FOR_EXPIRY_SWEEP": 9, + "EXPIRED": 10, } ) @@ -4385,6 +4402,11 @@ type WithdrawDepositsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + // The transaction hash of the withdrawal transaction. + WithdrawalTxHash string `protobuf:"bytes,1,opt,name=withdrawal_tx_hash,json=withdrawalTxHash,proto3" json:"withdrawal_tx_hash,omitempty"` + // The pkscript of the withdrawal transaction. + PkScript string `protobuf:"bytes,2,opt,name=pk_script,json=pkScript,proto3" json:"pk_script,omitempty"` } func (x *WithdrawDepositsResponse) Reset() { @@ -4419,6 +4441,20 @@ func (*WithdrawDepositsResponse) Descriptor() ([]byte, []int) { return file_client_proto_rawDescGZIP(), []int{51} } +func (x *WithdrawDepositsResponse) GetWithdrawalTxHash() string { + if x != nil { + return x.WithdrawalTxHash + } + return "" +} + +func (x *WithdrawDepositsResponse) GetPkScript() string { + if x != nil { + return x.PkScript + } + return "" +} + type OutPoint struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4552,15 +4588,19 @@ type StaticAddressSummaryResponse struct { // The total number of deposits. TotalNumDeposits uint32 `protobuf:"varint,2,opt,name=total_num_deposits,json=totalNumDeposits,proto3" json:"total_num_deposits,omitempty"` // The total value of unconfirmed deposits. - ValueUnconfirmed int64 `protobuf:"varint,3,opt,name=value_unconfirmed,json=valueUnconfirmed,proto3" json:"value_unconfirmed,omitempty"` + ValueUnconfirmedSatoshis int64 `protobuf:"varint,3,opt,name=value_unconfirmed_satoshis,json=valueUnconfirmedSatoshis,proto3" json:"value_unconfirmed_satoshis,omitempty"` // The total value of confirmed deposits. - ValueDeposited int64 `protobuf:"varint,4,opt,name=value_deposited,json=valueDeposited,proto3" json:"value_deposited,omitempty"` + ValueDepositedSatoshis int64 `protobuf:"varint,4,opt,name=value_deposited_satoshis,json=valueDepositedSatoshis,proto3" json:"value_deposited_satoshis,omitempty"` // The total value of all expired deposits. - ValueExpired int64 `protobuf:"varint,5,opt,name=value_expired,json=valueExpired,proto3" json:"value_expired,omitempty"` + ValueExpiredSatoshis int64 `protobuf:"varint,5,opt,name=value_expired_satoshis,json=valueExpiredSatoshis,proto3" json:"value_expired_satoshis,omitempty"` // The total value of all deposits that have been withdrawn. - ValueWithdrawn int64 `protobuf:"varint,6,opt,name=value_withdrawn,json=valueWithdrawn,proto3" json:"value_withdrawn,omitempty"` + ValueWithdrawnSatoshis int64 `protobuf:"varint,6,opt,name=value_withdrawn_satoshis,json=valueWithdrawnSatoshis,proto3" json:"value_withdrawn_satoshis,omitempty"` + // The total value of all loop-ins that have been finalized. + ValueLoopedInSatoshis int64 `protobuf:"varint,7,opt,name=value_looped_in_satoshis,json=valueLoopedInSatoshis,proto3" json:"value_looped_in_satoshis,omitempty"` + // The total value of all htlc timeout sweeps that the client swept. + ValueHtlcTimeoutSweepsSatoshis int64 `protobuf:"varint,8,opt,name=value_htlc_timeout_sweeps_satoshis,json=valueHtlcTimeoutSweepsSatoshis,proto3" json:"value_htlc_timeout_sweeps_satoshis,omitempty"` // A list of all deposits that match the filtered state. - FilteredDeposits []*Deposit `protobuf:"bytes,7,rep,name=filtered_deposits,json=filteredDeposits,proto3" json:"filtered_deposits,omitempty"` + FilteredDeposits []*Deposit `protobuf:"bytes,9,rep,name=filtered_deposits,json=filteredDeposits,proto3" json:"filtered_deposits,omitempty"` } func (x *StaticAddressSummaryResponse) Reset() { @@ -4609,30 +4649,44 @@ func (x *StaticAddressSummaryResponse) GetTotalNumDeposits() uint32 { return 0 } -func (x *StaticAddressSummaryResponse) GetValueUnconfirmed() int64 { +func (x *StaticAddressSummaryResponse) GetValueUnconfirmedSatoshis() int64 { + if x != nil { + return x.ValueUnconfirmedSatoshis + } + return 0 +} + +func (x *StaticAddressSummaryResponse) GetValueDepositedSatoshis() int64 { + if x != nil { + return x.ValueDepositedSatoshis + } + return 0 +} + +func (x *StaticAddressSummaryResponse) GetValueExpiredSatoshis() int64 { if x != nil { - return x.ValueUnconfirmed + return x.ValueExpiredSatoshis } return 0 } -func (x *StaticAddressSummaryResponse) GetValueDeposited() int64 { +func (x *StaticAddressSummaryResponse) GetValueWithdrawnSatoshis() int64 { if x != nil { - return x.ValueDeposited + return x.ValueWithdrawnSatoshis } return 0 } -func (x *StaticAddressSummaryResponse) GetValueExpired() int64 { +func (x *StaticAddressSummaryResponse) GetValueLoopedInSatoshis() int64 { if x != nil { - return x.ValueExpired + return x.ValueLoopedInSatoshis } return 0 } -func (x *StaticAddressSummaryResponse) GetValueWithdrawn() int64 { +func (x *StaticAddressSummaryResponse) GetValueHtlcTimeoutSweepsSatoshis() int64 { if x != nil { - return x.ValueWithdrawn + return x.ValueHtlcTimeoutSweepsSatoshis } return 0 } @@ -4728,6 +4782,277 @@ func (x *Deposit) GetConfirmationHeight() int64 { return 0 } +type StaticAddressLoopInRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The outpoints of the deposits to loop-in. + Outpoints []string `protobuf:"bytes,1,rep,name=outpoints,proto3" json:"outpoints,omitempty"` + // Maximum satoshis we are willing to pay the server for the swap. This value + // is not disclosed in the swap initiation call, but if the server asks for a + // higher fee, we abort the swap. Typically this value is taken from the + // response of the GetQuote call. + MaxSwapFeeSatoshis int64 `protobuf:"varint,2,opt,name=max_swap_fee_satoshis,json=maxSwapFeeSatoshis,proto3" json:"max_swap_fee_satoshis,omitempty"` + // Optionally the client can specify the last hop pubkey when requesting a + // loop-in quote. This is useful to get better off-chain routing fee from the + // server. + LastHop []byte `protobuf:"bytes,3,opt,name=last_hop,json=lastHop,proto3" json:"last_hop,omitempty"` + // An optional label for this swap. This field is limited to 500 characters and + // may not be one of the reserved values in loop/labels Reserved list. + Label string `protobuf:"bytes,4,opt,name=label,proto3" json:"label,omitempty"` + // An optional identification string that will be appended to the user agent + // string sent to the server to give information about the usage of loop. This + // initiator part is meant for user interfaces to add their name to give the + // full picture of the binary used (loopd, LiT) and the method used for + // triggering the swap (loop CLI, autolooper, LiT UI, other 3rd party UI). + Initiator string `protobuf:"bytes,5,opt,name=initiator,proto3" json:"initiator,omitempty"` + // Optional route hints to reach the destination through private channels. + RouteHints []*swapserverrpc.RouteHint `protobuf:"bytes,6,rep,name=route_hints,json=routeHints,proto3" json:"route_hints,omitempty"` + // Private indicates whether the destination node should be considered private. + // In which case, loop will generate hop hints to assist with probing and + // payment. + Private bool `protobuf:"varint,7,opt,name=private,proto3" json:"private,omitempty"` + // The swap payment timeout allows the user to specify an upper limit for the + // amount of time the server is allowed to take to fulfill the off-chain swap + // payment. If the timeout is reached the swap will be aborted on the server + // side and the client can retry the swap with different parameters. + PaymentTimeoutSeconds uint32 `protobuf:"varint,8,opt,name=payment_timeout_seconds,json=paymentTimeoutSeconds,proto3" json:"payment_timeout_seconds,omitempty"` +} + +func (x *StaticAddressLoopInRequest) Reset() { + *x = StaticAddressLoopInRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[56] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StaticAddressLoopInRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StaticAddressLoopInRequest) ProtoMessage() {} + +func (x *StaticAddressLoopInRequest) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[56] + 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 StaticAddressLoopInRequest.ProtoReflect.Descriptor instead. +func (*StaticAddressLoopInRequest) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{56} +} + +func (x *StaticAddressLoopInRequest) GetOutpoints() []string { + if x != nil { + return x.Outpoints + } + return nil +} + +func (x *StaticAddressLoopInRequest) GetMaxSwapFeeSatoshis() int64 { + if x != nil { + return x.MaxSwapFeeSatoshis + } + return 0 +} + +func (x *StaticAddressLoopInRequest) GetLastHop() []byte { + if x != nil { + return x.LastHop + } + return nil +} + +func (x *StaticAddressLoopInRequest) GetLabel() string { + if x != nil { + return x.Label + } + return "" +} + +func (x *StaticAddressLoopInRequest) GetInitiator() string { + if x != nil { + return x.Initiator + } + return "" +} + +func (x *StaticAddressLoopInRequest) GetRouteHints() []*swapserverrpc.RouteHint { + if x != nil { + return x.RouteHints + } + return nil +} + +func (x *StaticAddressLoopInRequest) GetPrivate() bool { + if x != nil { + return x.Private + } + return false +} + +func (x *StaticAddressLoopInRequest) GetPaymentTimeoutSeconds() uint32 { + if x != nil { + return x.PaymentTimeoutSeconds + } + return 0 +} + +type StaticAddressLoopInResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The swap hash that identifies this swap. + SwapHash []byte `protobuf:"bytes,1,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"` + // The state the swap is in. + State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` + // The amount of the swap. + Amount uint64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` + // The htlc cltv expiry height of the swap. + HtlcCltv int32 `protobuf:"varint,4,opt,name=htlc_cltv,json=htlcCltv,proto3" json:"htlc_cltv,omitempty"` + // The quoted swap fee in satoshis. + QuotedSwapFeeSatoshis int64 `protobuf:"varint,5,opt,name=quoted_swap_fee_satoshis,json=quotedSwapFeeSatoshis,proto3" json:"quoted_swap_fee_satoshis,omitempty"` + // The maximum total swap fee the client is willing to pay for the swap. + MaxSwapFeeSatoshis int64 `protobuf:"varint,6,opt,name=max_swap_fee_satoshis,json=maxSwapFeeSatoshis,proto3" json:"max_swap_fee_satoshis,omitempty"` + // The block height at which the swap was initiated. + InitiationHeight uint32 `protobuf:"varint,7,opt,name=initiation_height,json=initiationHeight,proto3" json:"initiation_height,omitempty"` + // The static address protocol version. + ProtocolVersion string `protobuf:"bytes,8,opt,name=protocol_version,json=protocolVersion,proto3" json:"protocol_version,omitempty"` + // An optional label for this swap. + Label string `protobuf:"bytes,9,opt,name=label,proto3" json:"label,omitempty"` + // An optional identification string that will be appended to the user agent + // string sent to the server to give information about the usage of loop. This + // initiator part is meant for user interfaces to add their name to give the + // full picture of the binary used (loopd, LiT) and the method used for + // triggering the swap (loop CLI, autolooper, LiT UI, other 3rd party UI). + Initiator string `protobuf:"bytes,10,opt,name=initiator,proto3" json:"initiator,omitempty"` + // The swap payment timeout allows the user to specify an upper limit for the + // amount of time the server is allowed to take to fulfill the off-chain swap + // payment. If the timeout is reached the swap will be aborted on the server + // side and the client can retry the swap with different parameters. + PaymentTimeoutSeconds uint32 `protobuf:"varint,11,opt,name=payment_timeout_seconds,json=paymentTimeoutSeconds,proto3" json:"payment_timeout_seconds,omitempty"` +} + +func (x *StaticAddressLoopInResponse) Reset() { + *x = StaticAddressLoopInResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_client_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StaticAddressLoopInResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StaticAddressLoopInResponse) ProtoMessage() {} + +func (x *StaticAddressLoopInResponse) ProtoReflect() protoreflect.Message { + mi := &file_client_proto_msgTypes[57] + 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 StaticAddressLoopInResponse.ProtoReflect.Descriptor instead. +func (*StaticAddressLoopInResponse) Descriptor() ([]byte, []int) { + return file_client_proto_rawDescGZIP(), []int{57} +} + +func (x *StaticAddressLoopInResponse) GetSwapHash() []byte { + if x != nil { + return x.SwapHash + } + return nil +} + +func (x *StaticAddressLoopInResponse) GetState() string { + if x != nil { + return x.State + } + return "" +} + +func (x *StaticAddressLoopInResponse) GetAmount() uint64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *StaticAddressLoopInResponse) GetHtlcCltv() int32 { + if x != nil { + return x.HtlcCltv + } + return 0 +} + +func (x *StaticAddressLoopInResponse) GetQuotedSwapFeeSatoshis() int64 { + if x != nil { + return x.QuotedSwapFeeSatoshis + } + return 0 +} + +func (x *StaticAddressLoopInResponse) GetMaxSwapFeeSatoshis() int64 { + if x != nil { + return x.MaxSwapFeeSatoshis + } + return 0 +} + +func (x *StaticAddressLoopInResponse) GetInitiationHeight() uint32 { + if x != nil { + return x.InitiationHeight + } + return 0 +} + +func (x *StaticAddressLoopInResponse) GetProtocolVersion() string { + if x != nil { + return x.ProtocolVersion + } + return "" +} + +func (x *StaticAddressLoopInResponse) GetLabel() string { + if x != nil { + return x.Label + } + return "" +} + +func (x *StaticAddressLoopInResponse) GetInitiator() string { + if x != nil { + return x.Initiator + } + return "" +} + +func (x *StaticAddressLoopInResponse) GetPaymentTimeoutSeconds() uint32 { + if x != nil { + return x.PaymentTimeoutSeconds + } + return 0 +} + var File_client_proto protoreflect.FileDescriptor var file_client_proto_rawDesc = []byte{ @@ -5252,257 +5577,332 @@ var file_client_proto_rawDesc = []byte{ 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x1a, 0x0a, 0x18, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x65, 0x0a, 0x18, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x67, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, - 0x0a, 0x0a, 0x74, 0x78, 0x69, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x09, 0x74, 0x78, 0x69, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x19, 0x0a, - 0x08, 0x74, 0x78, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x74, 0x78, 0x69, 0x64, 0x53, 0x74, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x70, - 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, - 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x75, 0x0a, 0x1b, 0x53, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x69, - 0x6c, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x73, 0x22, 0xd6, 0x02, 0x0a, 0x1c, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x75, 0x6d, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x5f, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x10, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x64, - 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x64, 0x12, 0x23, - 0x0a, 0x0d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x45, 0x78, 0x70, 0x69, - 0x72, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x77, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x6e, 0x12, 0x3d, 0x0a, 0x11, - 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x10, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x65, 0x64, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x22, 0xa9, 0x01, 0x0a, 0x07, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, - 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2a, 0x3b, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x44, 0x44, 0x52, 0x45, 0x53, - 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, - 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, - 0x45, 0x59, 0x10, 0x01, 0x2a, 0x25, 0x0a, 0x08, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x00, 0x12, 0x0b, - 0x0a, 0x07, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x01, 0x2a, 0x73, 0x0a, 0x09, 0x53, - 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x49, 0x54, - 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x50, 0x52, 0x45, 0x49, 0x4d, - 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x56, 0x45, 0x41, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x12, - 0x0a, 0x0e, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, - 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x03, 0x12, - 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x49, - 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x05, - 0x2a, 0xeb, 0x02, 0x0a, 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, - 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, - 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4f, 0x46, - 0x46, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x41, 0x49, 0x4c, - 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, - 0x55, 0x54, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, - 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x54, 0x49, 0x4d, - 0x45, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, - 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, - 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x04, 0x12, 0x1c, 0x0a, - 0x18, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, - 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x46, - 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, - 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x06, - 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, - 0x4f, 0x4e, 0x5f, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, 0x45, 0x44, 0x10, 0x07, 0x12, 0x31, - 0x0a, 0x2d, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, - 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x4f, - 0x4e, 0x46, 0x49, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, - 0x08, 0x12, 0x2b, 0x0a, 0x27, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, - 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x48, 0x54, - 0x4c, 0x43, 0x5f, 0x41, 0x4d, 0x54, 0x5f, 0x53, 0x57, 0x45, 0x50, 0x54, 0x10, 0x09, 0x2a, 0x2f, - 0x0a, 0x11, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, - 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x10, 0x01, 0x2a, - 0xa6, 0x03, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, - 0x0a, 0x13, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, - 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x41, 0x55, 0x54, 0x4f, 0x5f, - 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x4e, 0x4f, - 0x54, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x41, - 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, - 0x5f, 0x46, 0x45, 0x45, 0x53, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x41, 0x55, 0x54, 0x4f, 0x5f, - 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x45, 0x4c, - 0x41, 0x50, 0x53, 0x45, 0x44, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x55, 0x54, 0x4f, 0x5f, - 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, - 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, - 0x4e, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x05, 0x12, 0x19, 0x0a, 0x15, - 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x49, 0x4e, 0x45, - 0x52, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x55, 0x54, 0x4f, 0x5f, - 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x45, 0x50, 0x41, 0x59, 0x10, 0x07, 0x12, - 0x1f, 0x0a, 0x1b, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x46, - 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x4f, 0x46, 0x46, 0x10, 0x08, - 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, - 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x09, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x55, - 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, - 0x4e, 0x10, 0x0a, 0x12, 0x1c, 0x0a, 0x18, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, - 0x4f, 0x4e, 0x5f, 0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49, 0x54, 0x59, 0x5f, 0x4f, 0x4b, 0x10, - 0x0b, 0x12, 0x23, 0x0a, 0x1f, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, - 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, - 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, - 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, - 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0d, 0x2a, 0x9f, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x55, 0x4e, 0x4b, - 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, - 0x44, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x57, - 0x49, 0x54, 0x48, 0x44, 0x52, 0x41, 0x57, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, - 0x57, 0x49, 0x54, 0x48, 0x44, 0x52, 0x41, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x50, - 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x04, - 0x12, 0x19, 0x0a, 0x15, 0x57, 0x41, 0x49, 0x54, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x45, 0x58, 0x50, - 0x49, 0x52, 0x59, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, 0x45, - 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x41, 0x49, 0x4c, - 0x45, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x07, 0x32, 0xa7, 0x0f, 0x0a, 0x0a, 0x53, - 0x77, 0x61, 0x70, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x07, 0x4c, 0x6f, 0x6f, - 0x70, 0x4f, 0x75, 0x74, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, - 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, - 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x12, 0x16, - 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, - 0x07, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, + 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, + 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, + 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6b, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x67, 0x0a, + 0x08, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x78, 0x69, + 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, + 0x78, 0x69, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x78, 0x69, 0x64, + 0x5f, 0x73, 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x78, 0x69, 0x64, + 0x53, 0x74, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x75, 0x0a, 0x1b, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6f, + 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, + 0x1c, 0x0a, 0x09, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0x9f, 0x04, + 0x0a, 0x1c, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, + 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6e, + 0x75, 0x6d, 0x5f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x73, 0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x75, 0x6e, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x18, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x55, 0x6e, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, + 0x73, 0x12, 0x38, 0x0a, 0x18, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x64, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x16, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x65, 0x64, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x61, 0x74, + 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, + 0x73, 0x12, 0x38, 0x0a, 0x18, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x6e, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x16, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x6e, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6c, 0x6f, 0x6f, 0x70, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x73, + 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x65, 0x64, 0x49, 0x6e, 0x53, 0x61, 0x74, 0x6f, + 0x73, 0x68, 0x69, 0x73, 0x12, 0x4a, 0x0a, 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x68, 0x74, + 0x6c, 0x63, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x77, 0x65, 0x65, 0x70, + 0x73, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x1e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x54, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x73, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, + 0x12, 0x3d, 0x0a, 0x11, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6f, + 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x10, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x22, + 0xa9, 0x01, 0x0a, 0x07, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xc3, 0x02, 0x0a, 0x1a, + 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, 0x1c, 0x0a, 0x09, 0x6f, 0x75, + 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6f, + 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, + 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, + 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, + 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1c, 0x0a, 0x09, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x0b, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, + 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, + 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 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, 0x22, 0xb5, 0x03, 0x0a, 0x1b, 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, 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, 0x14, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, + 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x63, 0x6c, 0x74, 0x76, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x08, 0x68, 0x74, 0x6c, 0x63, 0x43, 0x6c, 0x74, 0x76, 0x12, 0x37, 0x0a, 0x18, 0x71, 0x75, 0x6f, + 0x74, 0x65, 0x64, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, + 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x71, 0x75, 0x6f, + 0x74, 0x65, 0x64, 0x53, 0x77, 0x61, 0x70, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, + 0x69, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x66, + 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x12, 0x6d, 0x61, 0x78, 0x53, 0x77, 0x61, 0x70, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, + 0x6f, 0x73, 0x68, 0x69, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x10, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, + 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, + 0x72, 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, 0x0b, 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, 0x2a, 0x3b, 0x0a, 0x0b, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x44, 0x44, 0x52, + 0x45, 0x53, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, + 0x42, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x2a, 0x25, 0x0a, 0x08, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x00, + 0x12, 0x0b, 0x0a, 0x07, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x01, 0x2a, 0x73, 0x0a, + 0x09, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, + 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x50, 0x52, 0x45, + 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x56, 0x45, 0x41, 0x4c, 0x45, 0x44, 0x10, 0x01, + 0x12, 0x12, 0x0a, 0x0e, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, + 0x45, 0x44, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, + 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x13, 0x0a, + 0x0f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, + 0x10, 0x05, 0x2a, 0xeb, 0x02, 0x0a, 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, + 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, + 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x4f, 0x46, 0x46, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x41, + 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, + 0x45, 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, + 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x54, + 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x46, 0x41, 0x49, 0x4c, + 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, + 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x04, 0x12, + 0x1c, 0x0a, 0x18, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, + 0x4e, 0x5f, 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x10, 0x05, 0x12, 0x23, 0x0a, + 0x1f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, + 0x10, 0x06, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, + 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, 0x45, 0x44, 0x10, 0x07, + 0x12, 0x31, 0x0a, 0x2d, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, + 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, 0x45, 0x44, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, + 0x45, 0x10, 0x08, 0x12, 0x2b, 0x0a, 0x27, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, + 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, + 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x4d, 0x54, 0x5f, 0x53, 0x57, 0x45, 0x50, 0x54, 0x10, 0x09, + 0x2a, 0x2f, 0x0a, 0x11, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x10, + 0x01, 0x2a, 0xa6, 0x03, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x12, 0x17, 0x0a, 0x13, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x41, 0x55, 0x54, + 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, + 0x4e, 0x4f, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1a, 0x0a, + 0x16, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x45, + 0x45, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x53, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x41, 0x55, 0x54, + 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, + 0x45, 0x4c, 0x41, 0x50, 0x53, 0x45, 0x44, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x55, 0x54, + 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, + 0x48, 0x54, 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, + 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x05, 0x12, 0x19, + 0x0a, 0x15, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x49, + 0x4e, 0x45, 0x52, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x55, 0x54, + 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x45, 0x50, 0x41, 0x59, 0x10, + 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x4f, 0x46, 0x46, + 0x10, 0x08, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, + 0x4e, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x09, 0x12, 0x17, 0x0a, 0x13, + 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, + 0x5f, 0x49, 0x4e, 0x10, 0x0a, 0x12, 0x1c, 0x0a, 0x18, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, + 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49, 0x54, 0x59, 0x5f, 0x4f, + 0x4b, 0x10, 0x0b, 0x12, 0x23, 0x0a, 0x1f, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, + 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x55, 0x54, 0x4f, + 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x55, + 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0d, 0x2a, 0xdc, 0x01, 0x0a, 0x0c, 0x44, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0d, + 0x0a, 0x09, 0x44, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0f, 0x0a, + 0x0b, 0x57, 0x49, 0x54, 0x48, 0x44, 0x52, 0x41, 0x57, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0d, + 0x0a, 0x09, 0x57, 0x49, 0x54, 0x48, 0x44, 0x52, 0x41, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x0e, 0x0a, + 0x0a, 0x4c, 0x4f, 0x4f, 0x50, 0x49, 0x4e, 0x47, 0x5f, 0x49, 0x4e, 0x10, 0x04, 0x12, 0x0d, 0x0a, + 0x09, 0x4c, 0x4f, 0x4f, 0x50, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, + 0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, + 0x55, 0x54, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x54, 0x49, 0x4d, + 0x45, 0x4f, 0x55, 0x54, 0x5f, 0x53, 0x57, 0x45, 0x50, 0x54, 0x10, 0x07, 0x12, 0x13, 0x0a, 0x0f, + 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, + 0x08, 0x12, 0x19, 0x0a, 0x15, 0x57, 0x41, 0x49, 0x54, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x45, 0x58, + 0x50, 0x49, 0x52, 0x59, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, 0x10, 0x09, 0x12, 0x0b, 0x0a, 0x07, + 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x0a, 0x32, 0x89, 0x10, 0x0a, 0x0a, 0x53, 0x77, + 0x61, 0x70, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x07, 0x4c, 0x6f, 0x6f, 0x70, + 0x4f, 0x75, 0x74, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, + 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6c, + 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x12, 0x16, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07, + 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x53, + 0x77, 0x61, 0x70, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, + 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x53, + 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, - 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, - 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x08, - 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, - 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x48, 0x0a, 0x0b, 0x41, 0x62, 0x61, 0x6e, 0x64, - 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, - 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, - 0x73, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x75, - 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, - 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x70, - 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, - 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, - 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x51, 0x75, - 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x50, - 0x72, 0x6f, 0x62, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, - 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6f, - 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x34, 0x30, 0x32, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, - 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x73, 0x61, 0x74, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0e, 0x46, 0x65, 0x74, 0x63, 0x68, - 0x4c, 0x34, 0x30, 0x32, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4c, 0x34, 0x30, 0x32, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4c, 0x34, 0x30, 0x32, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x47, 0x65, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, - 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, - 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, - 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, - 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, - 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x12, 0x5d, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, - 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x4b, 0x0a, 0x0c, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, - 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, - 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, - 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, - 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x10, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, - 0x4f, 0x75, 0x74, 0x12, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x74, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0f, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, - 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x74, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x74, 0x4f, 0x75, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x48, 0x0a, 0x0b, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, + 0x6e, 0x53, 0x77, 0x61, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, + 0x6e, 0x64, 0x6f, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, + 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, + 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x70, 0x49, + 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f, + 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x51, 0x75, 0x6f, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x50, 0x72, + 0x6f, 0x62, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, + 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x34, 0x30, 0x32, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x73, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6f, + 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x73, 0x61, 0x74, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4c, + 0x34, 0x30, 0x32, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4c, 0x34, 0x30, 0x32, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4c, 0x34, 0x30, 0x32, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x47, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, 0x69, + 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, + 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, + 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, + 0x5d, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, + 0x0a, 0x0c, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x1c, + 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, + 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, + 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, + 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, + 0x75, 0x74, 0x12, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, + 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x4f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0f, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x1f, + 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x74, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x54, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, + 0x4f, 0x75, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x4e, 0x65, 0x77, 0x53, - 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, 0x2e, 0x6c, - 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, - 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, 0x4e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x60, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x44, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, - 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, - 0x65, 0x6e, 0x74, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, - 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x17, - 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x24, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, - 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, - 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x4e, 0x65, 0x77, 0x53, 0x74, + 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, + 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 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, 0x4e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x69, + 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x60, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x44, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6c, + 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, + 0x6e, 0x74, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x57, 0x0a, 0x10, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x17, 0x47, + 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, + 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x24, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6c, + 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x12, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, + 0x70, 0x72, 0x70, 0x63, 0x2e, 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, + 0x24, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 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, 0x42, 0x27, 0x5a, 0x25, 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, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -5518,7 +5918,7 @@ func file_client_proto_rawDescGZIP() []byte { } var file_client_proto_enumTypes = make([]protoimpl.EnumInfo, 8) -var file_client_proto_msgTypes = make([]protoimpl.MessageInfo, 56) +var file_client_proto_msgTypes = make([]protoimpl.MessageInfo, 58) var file_client_proto_goTypes = []any{ (AddressType)(0), // 0: looprpc.AddressType (SwapType)(0), // 1: looprpc.SwapType @@ -5584,19 +5984,21 @@ var file_client_proto_goTypes = []any{ (*StaticAddressSummaryRequest)(nil), // 61: looprpc.StaticAddressSummaryRequest (*StaticAddressSummaryResponse)(nil), // 62: looprpc.StaticAddressSummaryResponse (*Deposit)(nil), // 63: looprpc.Deposit - (*swapserverrpc.RouteHint)(nil), // 64: looprpc.RouteHint + (*StaticAddressLoopInRequest)(nil), // 64: looprpc.StaticAddressLoopInRequest + (*StaticAddressLoopInResponse)(nil), // 65: looprpc.StaticAddressLoopInResponse + (*swapserverrpc.RouteHint)(nil), // 66: looprpc.RouteHint } var file_client_proto_depIdxs = []int32{ 0, // 0: looprpc.LoopOutRequest.account_addr_type:type_name -> looprpc.AddressType - 64, // 1: looprpc.LoopInRequest.route_hints:type_name -> looprpc.RouteHint + 66, // 1: looprpc.LoopInRequest.route_hints:type_name -> looprpc.RouteHint 1, // 2: looprpc.SwapStatus.type:type_name -> looprpc.SwapType 2, // 3: looprpc.SwapStatus.state:type_name -> looprpc.SwapState 3, // 4: looprpc.SwapStatus.failure_reason:type_name -> looprpc.FailureReason 14, // 5: looprpc.ListSwapsRequest.list_swap_filter:type_name -> looprpc.ListSwapsFilter 7, // 6: looprpc.ListSwapsFilter.swap_type:type_name -> looprpc.ListSwapsFilter.SwapTypeFilter 12, // 7: looprpc.ListSwapsResponse.swaps:type_name -> looprpc.SwapStatus - 64, // 8: looprpc.QuoteRequest.loop_in_route_hints:type_name -> looprpc.RouteHint - 64, // 9: looprpc.ProbeRequest.route_hints:type_name -> looprpc.RouteHint + 66, // 8: looprpc.QuoteRequest.loop_in_route_hints:type_name -> looprpc.RouteHint + 66, // 9: looprpc.ProbeRequest.route_hints:type_name -> looprpc.RouteHint 29, // 10: looprpc.TokensResponse.tokens:type_name -> looprpc.L402Token 30, // 11: looprpc.GetInfoResponse.loop_out_stats:type_name -> looprpc.LoopStats 30, // 12: looprpc.GetInfoResponse.loop_in_stats:type_name -> looprpc.LoopStats @@ -5616,63 +6018,66 @@ var file_client_proto_depIdxs = []int32{ 6, // 26: looprpc.StaticAddressSummaryRequest.state_filter:type_name -> looprpc.DepositState 63, // 27: looprpc.StaticAddressSummaryResponse.filtered_deposits:type_name -> looprpc.Deposit 6, // 28: looprpc.Deposit.state:type_name -> looprpc.DepositState - 8, // 29: looprpc.SwapClient.LoopOut:input_type -> looprpc.LoopOutRequest - 9, // 30: looprpc.SwapClient.LoopIn:input_type -> looprpc.LoopInRequest - 11, // 31: looprpc.SwapClient.Monitor:input_type -> looprpc.MonitorRequest - 13, // 32: looprpc.SwapClient.ListSwaps:input_type -> looprpc.ListSwapsRequest - 16, // 33: looprpc.SwapClient.SwapInfo:input_type -> looprpc.SwapInfoRequest - 41, // 34: looprpc.SwapClient.AbandonSwap:input_type -> looprpc.AbandonSwapRequest - 17, // 35: looprpc.SwapClient.LoopOutTerms:input_type -> looprpc.TermsRequest - 20, // 36: looprpc.SwapClient.LoopOutQuote:input_type -> looprpc.QuoteRequest - 17, // 37: looprpc.SwapClient.GetLoopInTerms:input_type -> looprpc.TermsRequest - 20, // 38: looprpc.SwapClient.GetLoopInQuote:input_type -> looprpc.QuoteRequest - 23, // 39: looprpc.SwapClient.Probe:input_type -> looprpc.ProbeRequest - 25, // 40: looprpc.SwapClient.GetL402Tokens:input_type -> looprpc.TokensRequest - 25, // 41: looprpc.SwapClient.GetLsatTokens:input_type -> looprpc.TokensRequest - 27, // 42: looprpc.SwapClient.FetchL402Token:input_type -> looprpc.FetchL402TokenRequest - 31, // 43: looprpc.SwapClient.GetInfo:input_type -> looprpc.GetInfoRequest - 33, // 44: looprpc.SwapClient.GetLiquidityParams:input_type -> looprpc.GetLiquidityParamsRequest - 36, // 45: looprpc.SwapClient.SetLiquidityParams:input_type -> looprpc.SetLiquidityParamsRequest - 38, // 46: looprpc.SwapClient.SuggestSwaps:input_type -> looprpc.SuggestSwapsRequest - 43, // 47: looprpc.SwapClient.ListReservations:input_type -> looprpc.ListReservationsRequest - 46, // 48: looprpc.SwapClient.InstantOut:input_type -> looprpc.InstantOutRequest - 48, // 49: looprpc.SwapClient.InstantOutQuote:input_type -> looprpc.InstantOutQuoteRequest - 50, // 50: looprpc.SwapClient.ListInstantOuts:input_type -> looprpc.ListInstantOutsRequest - 53, // 51: looprpc.SwapClient.NewStaticAddress:input_type -> looprpc.NewStaticAddressRequest - 55, // 52: looprpc.SwapClient.ListUnspentDeposits:input_type -> looprpc.ListUnspentDepositsRequest - 58, // 53: looprpc.SwapClient.WithdrawDeposits:input_type -> looprpc.WithdrawDepositsRequest - 61, // 54: looprpc.SwapClient.GetStaticAddressSummary:input_type -> looprpc.StaticAddressSummaryRequest - 10, // 55: looprpc.SwapClient.LoopOut:output_type -> looprpc.SwapResponse - 10, // 56: looprpc.SwapClient.LoopIn:output_type -> looprpc.SwapResponse - 12, // 57: looprpc.SwapClient.Monitor:output_type -> looprpc.SwapStatus - 15, // 58: looprpc.SwapClient.ListSwaps:output_type -> looprpc.ListSwapsResponse - 12, // 59: looprpc.SwapClient.SwapInfo:output_type -> looprpc.SwapStatus - 42, // 60: looprpc.SwapClient.AbandonSwap:output_type -> looprpc.AbandonSwapResponse - 19, // 61: looprpc.SwapClient.LoopOutTerms:output_type -> looprpc.OutTermsResponse - 22, // 62: looprpc.SwapClient.LoopOutQuote:output_type -> looprpc.OutQuoteResponse - 18, // 63: looprpc.SwapClient.GetLoopInTerms:output_type -> looprpc.InTermsResponse - 21, // 64: looprpc.SwapClient.GetLoopInQuote:output_type -> looprpc.InQuoteResponse - 24, // 65: looprpc.SwapClient.Probe:output_type -> looprpc.ProbeResponse - 26, // 66: looprpc.SwapClient.GetL402Tokens:output_type -> looprpc.TokensResponse - 26, // 67: looprpc.SwapClient.GetLsatTokens:output_type -> looprpc.TokensResponse - 28, // 68: looprpc.SwapClient.FetchL402Token:output_type -> looprpc.FetchL402TokenResponse - 32, // 69: looprpc.SwapClient.GetInfo:output_type -> looprpc.GetInfoResponse - 34, // 70: looprpc.SwapClient.GetLiquidityParams:output_type -> looprpc.LiquidityParameters - 37, // 71: looprpc.SwapClient.SetLiquidityParams:output_type -> looprpc.SetLiquidityParamsResponse - 40, // 72: looprpc.SwapClient.SuggestSwaps:output_type -> looprpc.SuggestSwapsResponse - 44, // 73: looprpc.SwapClient.ListReservations:output_type -> looprpc.ListReservationsResponse - 47, // 74: looprpc.SwapClient.InstantOut:output_type -> looprpc.InstantOutResponse - 49, // 75: looprpc.SwapClient.InstantOutQuote:output_type -> looprpc.InstantOutQuoteResponse - 51, // 76: looprpc.SwapClient.ListInstantOuts:output_type -> looprpc.ListInstantOutsResponse - 54, // 77: looprpc.SwapClient.NewStaticAddress:output_type -> looprpc.NewStaticAddressResponse - 56, // 78: looprpc.SwapClient.ListUnspentDeposits:output_type -> looprpc.ListUnspentDepositsResponse - 59, // 79: looprpc.SwapClient.WithdrawDeposits:output_type -> looprpc.WithdrawDepositsResponse - 62, // 80: looprpc.SwapClient.GetStaticAddressSummary:output_type -> looprpc.StaticAddressSummaryResponse - 55, // [55:81] is the sub-list for method output_type - 29, // [29:55] is the sub-list for method input_type - 29, // [29:29] is the sub-list for extension type_name - 29, // [29:29] is the sub-list for extension extendee - 0, // [0:29] is the sub-list for field type_name + 66, // 29: looprpc.StaticAddressLoopInRequest.route_hints:type_name -> looprpc.RouteHint + 8, // 30: looprpc.SwapClient.LoopOut:input_type -> looprpc.LoopOutRequest + 9, // 31: looprpc.SwapClient.LoopIn:input_type -> looprpc.LoopInRequest + 11, // 32: looprpc.SwapClient.Monitor:input_type -> looprpc.MonitorRequest + 13, // 33: looprpc.SwapClient.ListSwaps:input_type -> looprpc.ListSwapsRequest + 16, // 34: looprpc.SwapClient.SwapInfo:input_type -> looprpc.SwapInfoRequest + 41, // 35: looprpc.SwapClient.AbandonSwap:input_type -> looprpc.AbandonSwapRequest + 17, // 36: looprpc.SwapClient.LoopOutTerms:input_type -> looprpc.TermsRequest + 20, // 37: looprpc.SwapClient.LoopOutQuote:input_type -> looprpc.QuoteRequest + 17, // 38: looprpc.SwapClient.GetLoopInTerms:input_type -> looprpc.TermsRequest + 20, // 39: looprpc.SwapClient.GetLoopInQuote:input_type -> looprpc.QuoteRequest + 23, // 40: looprpc.SwapClient.Probe:input_type -> looprpc.ProbeRequest + 25, // 41: looprpc.SwapClient.GetL402Tokens:input_type -> looprpc.TokensRequest + 25, // 42: looprpc.SwapClient.GetLsatTokens:input_type -> looprpc.TokensRequest + 27, // 43: looprpc.SwapClient.FetchL402Token:input_type -> looprpc.FetchL402TokenRequest + 31, // 44: looprpc.SwapClient.GetInfo:input_type -> looprpc.GetInfoRequest + 33, // 45: looprpc.SwapClient.GetLiquidityParams:input_type -> looprpc.GetLiquidityParamsRequest + 36, // 46: looprpc.SwapClient.SetLiquidityParams:input_type -> looprpc.SetLiquidityParamsRequest + 38, // 47: looprpc.SwapClient.SuggestSwaps:input_type -> looprpc.SuggestSwapsRequest + 43, // 48: looprpc.SwapClient.ListReservations:input_type -> looprpc.ListReservationsRequest + 46, // 49: looprpc.SwapClient.InstantOut:input_type -> looprpc.InstantOutRequest + 48, // 50: looprpc.SwapClient.InstantOutQuote:input_type -> looprpc.InstantOutQuoteRequest + 50, // 51: looprpc.SwapClient.ListInstantOuts:input_type -> looprpc.ListInstantOutsRequest + 53, // 52: looprpc.SwapClient.NewStaticAddress:input_type -> looprpc.NewStaticAddressRequest + 55, // 53: looprpc.SwapClient.ListUnspentDeposits:input_type -> looprpc.ListUnspentDepositsRequest + 58, // 54: looprpc.SwapClient.WithdrawDeposits:input_type -> looprpc.WithdrawDepositsRequest + 61, // 55: looprpc.SwapClient.GetStaticAddressSummary:input_type -> looprpc.StaticAddressSummaryRequest + 64, // 56: looprpc.SwapClient.StaticAddressLoopIn:input_type -> looprpc.StaticAddressLoopInRequest + 10, // 57: looprpc.SwapClient.LoopOut:output_type -> looprpc.SwapResponse + 10, // 58: looprpc.SwapClient.LoopIn:output_type -> looprpc.SwapResponse + 12, // 59: looprpc.SwapClient.Monitor:output_type -> looprpc.SwapStatus + 15, // 60: looprpc.SwapClient.ListSwaps:output_type -> looprpc.ListSwapsResponse + 12, // 61: looprpc.SwapClient.SwapInfo:output_type -> looprpc.SwapStatus + 42, // 62: looprpc.SwapClient.AbandonSwap:output_type -> looprpc.AbandonSwapResponse + 19, // 63: looprpc.SwapClient.LoopOutTerms:output_type -> looprpc.OutTermsResponse + 22, // 64: looprpc.SwapClient.LoopOutQuote:output_type -> looprpc.OutQuoteResponse + 18, // 65: looprpc.SwapClient.GetLoopInTerms:output_type -> looprpc.InTermsResponse + 21, // 66: looprpc.SwapClient.GetLoopInQuote:output_type -> looprpc.InQuoteResponse + 24, // 67: looprpc.SwapClient.Probe:output_type -> looprpc.ProbeResponse + 26, // 68: looprpc.SwapClient.GetL402Tokens:output_type -> looprpc.TokensResponse + 26, // 69: looprpc.SwapClient.GetLsatTokens:output_type -> looprpc.TokensResponse + 28, // 70: looprpc.SwapClient.FetchL402Token:output_type -> looprpc.FetchL402TokenResponse + 32, // 71: looprpc.SwapClient.GetInfo:output_type -> looprpc.GetInfoResponse + 34, // 72: looprpc.SwapClient.GetLiquidityParams:output_type -> looprpc.LiquidityParameters + 37, // 73: looprpc.SwapClient.SetLiquidityParams:output_type -> looprpc.SetLiquidityParamsResponse + 40, // 74: looprpc.SwapClient.SuggestSwaps:output_type -> looprpc.SuggestSwapsResponse + 44, // 75: looprpc.SwapClient.ListReservations:output_type -> looprpc.ListReservationsResponse + 47, // 76: looprpc.SwapClient.InstantOut:output_type -> looprpc.InstantOutResponse + 49, // 77: looprpc.SwapClient.InstantOutQuote:output_type -> looprpc.InstantOutQuoteResponse + 51, // 78: looprpc.SwapClient.ListInstantOuts:output_type -> looprpc.ListInstantOutsResponse + 54, // 79: looprpc.SwapClient.NewStaticAddress:output_type -> looprpc.NewStaticAddressResponse + 56, // 80: looprpc.SwapClient.ListUnspentDeposits:output_type -> looprpc.ListUnspentDepositsResponse + 59, // 81: looprpc.SwapClient.WithdrawDeposits:output_type -> looprpc.WithdrawDepositsResponse + 62, // 82: looprpc.SwapClient.GetStaticAddressSummary:output_type -> looprpc.StaticAddressSummaryResponse + 65, // 83: looprpc.SwapClient.StaticAddressLoopIn:output_type -> looprpc.StaticAddressLoopInResponse + 57, // [57:84] is the sub-list for method output_type + 30, // [30:57] is the sub-list for method input_type + 30, // [30:30] is the sub-list for extension type_name + 30, // [30:30] is the sub-list for extension extendee + 0, // [0:30] is the sub-list for field type_name } func init() { file_client_proto_init() } @@ -6353,6 +6758,30 @@ func file_client_proto_init() { return nil } } + file_client_proto_msgTypes[56].Exporter = func(v any, i int) any { + switch v := v.(*StaticAddressLoopInRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_client_proto_msgTypes[57].Exporter = func(v any, i int) any { + switch v := v.(*StaticAddressLoopInResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -6360,7 +6789,7 @@ func file_client_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_client_proto_rawDesc, NumEnums: 8, - NumMessages: 56, + NumMessages: 58, NumExtensions: 0, NumServices: 1, }, diff --git a/looprpc/client.proto b/looprpc/client.proto index 3ed4561de..8235f77f3 100644 --- a/looprpc/client.proto +++ b/looprpc/client.proto @@ -174,6 +174,12 @@ service SwapClient { */ rpc GetStaticAddressSummary (StaticAddressSummaryRequest) returns (StaticAddressSummaryResponse); + + /* loop:`static` + StaticAddressLoopIn initiates a static address loop-in swap. + */ + rpc StaticAddressLoopIn (StaticAddressLoopInRequest) + returns (StaticAddressLoopInResponse); } message LoopOutRequest { @@ -1565,6 +1571,15 @@ message WithdrawDepositsRequest { } message WithdrawDepositsResponse { + /* + The transaction hash of the withdrawal transaction. + */ + string withdrawal_tx_hash = 1; + + /* + The pkscript of the withdrawal transaction. + */ + string pk_script = 2; } message OutPoint { @@ -1610,27 +1625,37 @@ message StaticAddressSummaryResponse { /* The total value of unconfirmed deposits. */ - int64 value_unconfirmed = 3; + int64 value_unconfirmed_satoshis = 3; /* The total value of confirmed deposits. */ - int64 value_deposited = 4; + int64 value_deposited_satoshis = 4; /* The total value of all expired deposits. */ - int64 value_expired = 5; + int64 value_expired_satoshis = 5; /* The total value of all deposits that have been withdrawn. */ - int64 value_withdrawn = 6; + int64 value_withdrawn_satoshis = 6; + + /* + The total value of all loop-ins that have been finalized. + */ + int64 value_looped_in_satoshis = 7; + + /* + The total value of all htlc timeout sweeps that the client swept. + */ + int64 value_htlc_timeout_sweeps_satoshis = 8; /* A list of all deposits that match the filtered state. */ - repeated Deposit filtered_deposits = 7; + repeated Deposit filtered_deposits = 9; } enum DepositState { @@ -1657,28 +1682,48 @@ enum DepositState { */ WITHDRAWN = 3; + /* + LOOPING_IN indicates that the deposit is currently being used in a static + address loop-in swap. + */ + LOOPING_IN = 4; + + /* + LOOPED_IN indicates that the deposit was used in a static address loop-in + swap. + */ + LOOPED_IN = 5; + + /* + SWEEP_HTLC_TIMEOUT indicates that the deposit is part of an active loop-in + of which the respective htlc was published by the server and the timeout + path has opened up for the client to sweep. + */ + SWEEP_HTLC_TIMEOUT = 6; + + /* + HTLC_TIMEOUT_SWEPT indicates that the timeout path of the htlc has been + swept by the client. + */ + HTLC_TIMEOUT_SWEPT = 7; + /* PUBLISH_EXPIRED indicates that the deposit has expired and the sweep transaction has been published. */ - PUBLISH_EXPIRED = 4; + PUBLISH_EXPIRED = 8; /* WAIT_FOR_EXPIRY_SWEEP indicates that the deposit has expired and the sweep transaction has not yet been sufficiently confirmed. */ - WAIT_FOR_EXPIRY_SWEEP = 5; + WAIT_FOR_EXPIRY_SWEEP = 9; /* EXPIRED indicates that the deposit has expired and the sweep transaction has been sufficiently confirmed. */ - EXPIRED = 6; - - /* - FAILED_STATE indicates that the deposit has failed. - */ - FAILED_STATE = 7; + EXPIRED = 10; } message Deposit { @@ -1707,3 +1752,124 @@ message Deposit { */ int64 confirmation_height = 5; } + +message StaticAddressLoopInRequest { + /* + The outpoints of the deposits to loop-in. + */ + repeated string outpoints = 1; + + /* + Maximum satoshis we are willing to pay the server for the swap. This value + is not disclosed in the swap initiation call, but if the server asks for a + higher fee, we abort the swap. Typically this value is taken from the + response of the GetQuote call. + */ + int64 max_swap_fee_satoshis = 2; + + /* + Optionally the client can specify the last hop pubkey when requesting a + loop-in quote. This is useful to get better off-chain routing fee from the + server. + */ + bytes last_hop = 3; + + /* + An optional label for this swap. This field is limited to 500 characters and + may not be one of the reserved values in loop/labels Reserved list. + */ + string label = 4; + + /* + An optional identification string that will be appended to the user agent + string sent to the server to give information about the usage of loop. This + initiator part is meant for user interfaces to add their name to give the + full picture of the binary used (loopd, LiT) and the method used for + triggering the swap (loop CLI, autolooper, LiT UI, other 3rd party UI). + */ + string initiator = 5; + + /* + Optional route hints to reach the destination through private channels. + */ + repeated looprpc.RouteHint route_hints = 6; + + /* + Private indicates whether the destination node should be considered private. + In which case, loop will generate hop hints to assist with probing and + payment. + */ + bool private = 7; + + /* + The swap payment timeout allows the user to specify an upper limit for the + amount of time the server is allowed to take to fulfill the off-chain swap + payment. If the timeout is reached the swap will be aborted on the server + side and the client can retry the swap with different parameters. + */ + uint32 payment_timeout_seconds = 8; +} + +message StaticAddressLoopInResponse { + /* + The swap hash that identifies this swap. + */ + bytes swap_hash = 1; + + /* + The state the swap is in. + */ + string state = 2; + + /* + The amount of the swap. + */ + uint64 amount = 3; + + /* + The htlc cltv expiry height of the swap. + */ + int32 htlc_cltv = 4; + + /* + The quoted swap fee in satoshis. + */ + int64 quoted_swap_fee_satoshis = 5; + + /* + The maximum total swap fee the client is willing to pay for the swap. + */ + int64 max_swap_fee_satoshis = 6; + + /* + The block height at which the swap was initiated. + */ + uint32 initiation_height = 7; + + /* + The static address protocol version. + */ + string protocol_version = 8; + + /* + An optional label for this swap. + */ + string label = 9; + + /* + An optional identification string that will be appended to the user agent + string sent to the server to give information about the usage of loop. This + initiator part is meant for user interfaces to add their name to give the + full picture of the binary used (loopd, LiT) and the method used for + triggering the swap (loop CLI, autolooper, LiT UI, other 3rd party UI). + */ + string initiator = 10; + + /* + The swap payment timeout allows the user to specify an upper limit for the + amount of time the server is allowed to take to fulfill the off-chain swap + payment. If the timeout is reached the swap will be aborted on the server + side and the client can retry the swap with different parameters. + */ + uint32 payment_timeout_seconds = 11; +} diff --git a/looprpc/client.swagger.json b/looprpc/client.swagger.json index bca77163a..f9e56145c 100644 --- a/looprpc/client.swagger.json +++ b/looprpc/client.swagger.json @@ -698,13 +698,16 @@ "DEPOSITED", "WITHDRAWING", "WITHDRAWN", + "LOOPING_IN", + "LOOPED_IN", + "SWEEP_HTLC_TIMEOUT", + "HTLC_TIMEOUT_SWEPT", "PUBLISH_EXPIRED", "WAIT_FOR_EXPIRY_SWEEP", - "EXPIRED", - "FAILED_STATE" + "EXPIRED" ], "default": "UNKNOWN_STATE", - "description": " - UNKNOWN_STATE: UNKNOWN_STATE is the default state of a deposit.\n - DEPOSITED: DEPOSITED indicates that the deposit has been sufficiently confirmed on\nchain.\n - WITHDRAWING: WITHDRAWING indicates that the deposit is currently being withdrawn. It\nflips to WITHDRAWN once the withdrawal transaction has been sufficiently\nconfirmed.\n - WITHDRAWN: WITHDRAWN indicates that the deposit has been withdrawn.\n - PUBLISH_EXPIRED: PUBLISH_EXPIRED indicates that the deposit has expired and the sweep\ntransaction has been published.\n - WAIT_FOR_EXPIRY_SWEEP: WAIT_FOR_EXPIRY_SWEEP indicates that the deposit has expired and the sweep\ntransaction has not yet been sufficiently confirmed.\n - EXPIRED: EXPIRED indicates that the deposit has expired and the sweep transaction\nhas been sufficiently confirmed.\n - FAILED_STATE: FAILED_STATE indicates that the deposit has failed." + "description": " - UNKNOWN_STATE: UNKNOWN_STATE is the default state of a deposit.\n - DEPOSITED: DEPOSITED indicates that the deposit has been sufficiently confirmed on\nchain.\n - WITHDRAWING: WITHDRAWING indicates that the deposit is currently being withdrawn. It\nflips to WITHDRAWN once the withdrawal transaction has been sufficiently\nconfirmed.\n - WITHDRAWN: WITHDRAWN indicates that the deposit has been withdrawn.\n - LOOPING_IN: LOOPING_IN indicates that the deposit is currently being used in a static\naddress loop-in swap.\n - LOOPED_IN: LOOPED_IN indicates that the deposit was used in a static address loop-in\nswap.\n - SWEEP_HTLC_TIMEOUT: SWEEP_HTLC_TIMEOUT indicates that the deposit is part of an active loop-in\nof which the respective htlc was published by the server and the timeout\npath has opened up for the client to sweep.\n - HTLC_TIMEOUT_SWEPT: HTLC_TIMEOUT_SWEPT indicates that the timeout path of the htlc has been\nswept by the client.\n - PUBLISH_EXPIRED: PUBLISH_EXPIRED indicates that the deposit has expired and the sweep\ntransaction has been published.\n - WAIT_FOR_EXPIRY_SWEEP: WAIT_FOR_EXPIRY_SWEEP indicates that the deposit has expired and the sweep\ntransaction has not yet been sufficiently confirmed.\n - EXPIRED: EXPIRED indicates that the deposit has expired and the sweep transaction\nhas been sufficiently confirmed." }, "looprpcDisqualified": { "type": "object", @@ -1506,6 +1509,62 @@ "looprpcSetLiquidityParamsResponse": { "type": "object" }, + "looprpcStaticAddressLoopInResponse": { + "type": "object", + "properties": { + "swap_hash": { + "type": "string", + "format": "byte", + "description": "The swap hash that identifies this swap." + }, + "state": { + "type": "string", + "description": "The state the swap is in." + }, + "amount": { + "type": "string", + "format": "uint64", + "description": "The amount of the swap." + }, + "htlc_cltv": { + "type": "integer", + "format": "int32", + "description": "The htlc cltv expiry height of the swap." + }, + "quoted_swap_fee_satoshis": { + "type": "string", + "format": "int64", + "description": "The quoted swap fee in satoshis." + }, + "max_swap_fee_satoshis": { + "type": "string", + "format": "int64", + "description": "The maximum total swap fee the client is willing to pay for the swap." + }, + "initiation_height": { + "type": "integer", + "format": "int64", + "description": "The block height at which the swap was initiated." + }, + "protocol_version": { + "type": "string", + "description": "The static address protocol version." + }, + "label": { + "type": "string", + "description": "An optional label for this swap." + }, + "initiator": { + "type": "string", + "description": "An optional identification string that will be appended to the user agent\nstring sent to the server to give information about the usage of loop. This\ninitiator part is meant for user interfaces to add their name to give the\nfull picture of the binary used (loopd, LiT) and the method used for\ntriggering the swap (loop CLI, autolooper, LiT UI, other 3rd party UI)." + }, + "payment_timeout_seconds": { + "type": "integer", + "format": "int64", + "description": "The swap payment timeout allows the user to specify an upper limit for the\namount of time the server is allowed to take to fulfill the off-chain swap\npayment. If the timeout is reached the swap will be aborted on the server\nside and the client can retry the swap with different parameters." + } + } + }, "looprpcStaticAddressSummaryResponse": { "type": "object", "properties": { @@ -1518,26 +1577,36 @@ "format": "int64", "description": "The total number of deposits." }, - "value_unconfirmed": { + "value_unconfirmed_satoshis": { "type": "string", "format": "int64", "description": "The total value of unconfirmed deposits." }, - "value_deposited": { + "value_deposited_satoshis": { "type": "string", "format": "int64", "description": "The total value of confirmed deposits." }, - "value_expired": { + "value_expired_satoshis": { "type": "string", "format": "int64", "description": "The total value of all expired deposits." }, - "value_withdrawn": { + "value_withdrawn_satoshis": { "type": "string", "format": "int64", "description": "The total value of all deposits that have been withdrawn." }, + "value_looped_in_satoshis": { + "type": "string", + "format": "int64", + "description": "The total value of all loop-ins that have been finalized." + }, + "value_htlc_timeout_sweeps_satoshis": { + "type": "string", + "format": "int64", + "description": "The total value of all htlc timeout sweeps that the client swept." + }, "filtered_deposits": { "type": "array", "items": { @@ -1746,7 +1815,17 @@ } }, "looprpcWithdrawDepositsResponse": { - "type": "object" + "type": "object", + "properties": { + "withdrawal_tx_hash": { + "type": "string", + "description": "The transaction hash of the withdrawal transaction." + }, + "pk_script": { + "type": "string", + "description": "The pkscript of the withdrawal transaction." + } + } }, "protobufAny": { "type": "object", diff --git a/looprpc/client_grpc.pb.go b/looprpc/client_grpc.pb.go index 9be5fe9d7..f482738ee 100644 --- a/looprpc/client_grpc.pb.go +++ b/looprpc/client_grpc.pb.go @@ -119,6 +119,9 @@ type SwapClientClient interface { // GetStaticAddressSummary returns a summary of static address related // statistics. GetStaticAddressSummary(ctx context.Context, in *StaticAddressSummaryRequest, opts ...grpc.CallOption) (*StaticAddressSummaryResponse, error) + // loop:`static` + // StaticAddressLoopIn initiates a static address loop-in swap. + StaticAddressLoopIn(ctx context.Context, in *StaticAddressLoopInRequest, opts ...grpc.CallOption) (*StaticAddressLoopInResponse, error) } type swapClientClient struct { @@ -386,6 +389,15 @@ func (c *swapClientClient) GetStaticAddressSummary(ctx context.Context, in *Stat return out, nil } +func (c *swapClientClient) StaticAddressLoopIn(ctx context.Context, in *StaticAddressLoopInRequest, opts ...grpc.CallOption) (*StaticAddressLoopInResponse, error) { + out := new(StaticAddressLoopInResponse) + err := c.cc.Invoke(ctx, "/looprpc.SwapClient/StaticAddressLoopIn", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // SwapClientServer is the server API for SwapClient service. // All implementations must embed UnimplementedSwapClientServer // for forward compatibility @@ -491,6 +503,9 @@ type SwapClientServer interface { // GetStaticAddressSummary returns a summary of static address related // statistics. GetStaticAddressSummary(context.Context, *StaticAddressSummaryRequest) (*StaticAddressSummaryResponse, error) + // loop:`static` + // StaticAddressLoopIn initiates a static address loop-in swap. + StaticAddressLoopIn(context.Context, *StaticAddressLoopInRequest) (*StaticAddressLoopInResponse, error) mustEmbedUnimplementedSwapClientServer() } @@ -576,6 +591,9 @@ func (UnimplementedSwapClientServer) WithdrawDeposits(context.Context, *Withdraw func (UnimplementedSwapClientServer) GetStaticAddressSummary(context.Context, *StaticAddressSummaryRequest) (*StaticAddressSummaryResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetStaticAddressSummary not implemented") } +func (UnimplementedSwapClientServer) StaticAddressLoopIn(context.Context, *StaticAddressLoopInRequest) (*StaticAddressLoopInResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StaticAddressLoopIn not implemented") +} func (UnimplementedSwapClientServer) mustEmbedUnimplementedSwapClientServer() {} // UnsafeSwapClientServer may be embedded to opt out of forward compatibility for this service. @@ -1060,6 +1078,24 @@ func _SwapClient_GetStaticAddressSummary_Handler(srv interface{}, ctx context.Co return interceptor(ctx, in, info, handler) } +func _SwapClient_StaticAddressLoopIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StaticAddressLoopInRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SwapClientServer).StaticAddressLoopIn(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.SwapClient/StaticAddressLoopIn", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SwapClientServer).StaticAddressLoopIn(ctx, req.(*StaticAddressLoopInRequest)) + } + return interceptor(ctx, in, info, handler) +} + // SwapClient_ServiceDesc is the grpc.ServiceDesc for SwapClient service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -1167,6 +1203,10 @@ var SwapClient_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetStaticAddressSummary", Handler: _SwapClient_GetStaticAddressSummary_Handler, }, + { + MethodName: "StaticAddressLoopIn", + Handler: _SwapClient_StaticAddressLoopIn_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/looprpc/swapclient.pb.json.go b/looprpc/swapclient.pb.json.go index da8749d75..41a721233 100644 --- a/looprpc/swapclient.pb.json.go +++ b/looprpc/swapclient.pb.json.go @@ -687,4 +687,29 @@ func RegisterSwapClientJSONCallbacks(registry map[string]func(ctx context.Contex } callback(string(respBytes), nil) } + + registry["looprpc.SwapClient.StaticAddressLoopIn"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &StaticAddressLoopInRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewSwapClientClient(conn) + resp, err := client.StaticAddressLoopIn(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } } diff --git a/swapserverrpc/server.pb.go b/swapserverrpc/server.pb.go index ec9aded89..33a7a2cbe 100644 --- a/swapserverrpc/server.pb.go +++ b/swapserverrpc/server.pb.go @@ -3218,6 +3218,807 @@ func (x *ServerWithdrawResponse) GetServerNonces() [][]byte { return nil } +type ServerStaticAddressLoopInRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The client's public key for the htlc output. + HtlcClientPubKey []byte `protobuf:"bytes,1,opt,name=htlc_client_pub_key,json=htlcClientPubKey,proto3" json:"htlc_client_pub_key,omitempty"` + // The hashed swap invoice preimage of the swap. + SwapHash []byte `protobuf:"bytes,2,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"` + // The deposit outpoints the client wishes to loop in. They implicitly state + // the swap amount. + DepositOutpoints []string `protobuf:"bytes,3,rep,name=deposit_outpoints,json=depositOutpoints,proto3" json:"deposit_outpoints,omitempty"` + // The swap invoice that the client wants the server to pay. + SwapInvoice string `protobuf:"bytes,4,opt,name=swap_invoice,json=swapInvoice,proto3" json:"swap_invoice,omitempty"` + // An optional last hop the client wants to receive the invoice payment + // from. + LastHop []byte `protobuf:"bytes,5,opt,name=last_hop,json=lastHop,proto3" json:"last_hop,omitempty"` + // The protocol version that the client adheres to. + ProtocolVersion StaticAddressProtocolVersion `protobuf:"varint,6,opt,name=protocol_version,json=protocolVersion,proto3,enum=looprpc.StaticAddressProtocolVersion" json:"protocol_version,omitempty"` + // The user agent string that identifies the software running on the user's + // side. This can be changed in the user's client software but it _SHOULD_ + // conform to the following pattern: + // + // Agent-Name/semver-version(/additional-info) + // + // Examples: + // + // loopd/v0.10.0-beta/commit=3b635821 + // litd/v0.2.0-alpha/commit=326d754 + // loopd/v0.10.0-beta/commit=3b635823,initiator=easy-autoloop + UserAgent string `protobuf:"bytes,7,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"` + // The swap payment timeout allows the user to specify an upper limit for + // the amount of time the server is allowed to take to fulfill the off-chain + // swap payment. If the timeout is reached the swap will be aborted on the + // server side and the client can retry the swap with different parameters. + PaymentTimeoutSeconds uint32 `protobuf:"varint,8,opt,name=payment_timeout_seconds,json=paymentTimeoutSeconds,proto3" json:"payment_timeout_seconds,omitempty"` +} + +func (x *ServerStaticAddressLoopInRequest) Reset() { + *x = ServerStaticAddressLoopInRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_server_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerStaticAddressLoopInRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerStaticAddressLoopInRequest) ProtoMessage() {} + +func (x *ServerStaticAddressLoopInRequest) ProtoReflect() protoreflect.Message { + mi := &file_server_proto_msgTypes[41] + 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 ServerStaticAddressLoopInRequest.ProtoReflect.Descriptor instead. +func (*ServerStaticAddressLoopInRequest) Descriptor() ([]byte, []int) { + return file_server_proto_rawDescGZIP(), []int{41} +} + +func (x *ServerStaticAddressLoopInRequest) GetHtlcClientPubKey() []byte { + if x != nil { + return x.HtlcClientPubKey + } + return nil +} + +func (x *ServerStaticAddressLoopInRequest) GetSwapHash() []byte { + if x != nil { + return x.SwapHash + } + return nil +} + +func (x *ServerStaticAddressLoopInRequest) GetDepositOutpoints() []string { + if x != nil { + return x.DepositOutpoints + } + return nil +} + +func (x *ServerStaticAddressLoopInRequest) GetSwapInvoice() string { + if x != nil { + return x.SwapInvoice + } + return "" +} + +func (x *ServerStaticAddressLoopInRequest) GetLastHop() []byte { + if x != nil { + return x.LastHop + } + return nil +} + +func (x *ServerStaticAddressLoopInRequest) GetProtocolVersion() StaticAddressProtocolVersion { + if x != nil { + return x.ProtocolVersion + } + return StaticAddressProtocolVersion_V0 +} + +func (x *ServerStaticAddressLoopInRequest) GetUserAgent() string { + if x != nil { + return x.UserAgent + } + return "" +} + +func (x *ServerStaticAddressLoopInRequest) GetPaymentTimeoutSeconds() uint32 { + if x != nil { + return x.PaymentTimeoutSeconds + } + return 0 +} + +type ServerStaticAddressLoopInResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The server's public key for the htlc output. + HtlcServerPubKey []byte `protobuf:"bytes,1,opt,name=htlc_server_pub_key,json=htlcServerPubKey,proto3" json:"htlc_server_pub_key,omitempty"` + // The cltv expiry height for the htlc. This is the height at which the + // htlc will expire and the client is free to claim the funds back. + HtlcExpiry int32 `protobuf:"varint,2,opt,name=htlc_expiry,json=htlcExpiry,proto3" json:"htlc_expiry,omitempty"` + // The info the server used to generate the standard fee partial htlc tx + // sigs. + StandardHtlcInfo *ServerHtlcSigningInfo `protobuf:"bytes,3,opt,name=standard_htlc_info,json=standardHtlcInfo,proto3" json:"standard_htlc_info,omitempty"` + // The info the server used to generate the high fee partial htlc tx sigs. + HighFeeHtlcInfo *ServerHtlcSigningInfo `protobuf:"bytes,4,opt,name=high_fee_htlc_info,json=highFeeHtlcInfo,proto3" json:"high_fee_htlc_info,omitempty"` + // The info the server used to generate the extreme fee partial htlc tx + // sigs. + ExtremeFeeHtlcInfo *ServerHtlcSigningInfo `protobuf:"bytes,5,opt,name=extreme_fee_htlc_info,json=extremeFeeHtlcInfo,proto3" json:"extreme_fee_htlc_info,omitempty"` +} + +func (x *ServerStaticAddressLoopInResponse) Reset() { + *x = ServerStaticAddressLoopInResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_server_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerStaticAddressLoopInResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerStaticAddressLoopInResponse) ProtoMessage() {} + +func (x *ServerStaticAddressLoopInResponse) ProtoReflect() protoreflect.Message { + mi := &file_server_proto_msgTypes[42] + 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 ServerStaticAddressLoopInResponse.ProtoReflect.Descriptor instead. +func (*ServerStaticAddressLoopInResponse) Descriptor() ([]byte, []int) { + return file_server_proto_rawDescGZIP(), []int{42} +} + +func (x *ServerStaticAddressLoopInResponse) GetHtlcServerPubKey() []byte { + if x != nil { + return x.HtlcServerPubKey + } + return nil +} + +func (x *ServerStaticAddressLoopInResponse) GetHtlcExpiry() int32 { + if x != nil { + return x.HtlcExpiry + } + return 0 +} + +func (x *ServerStaticAddressLoopInResponse) GetStandardHtlcInfo() *ServerHtlcSigningInfo { + if x != nil { + return x.StandardHtlcInfo + } + return nil +} + +func (x *ServerStaticAddressLoopInResponse) GetHighFeeHtlcInfo() *ServerHtlcSigningInfo { + if x != nil { + return x.HighFeeHtlcInfo + } + return nil +} + +func (x *ServerStaticAddressLoopInResponse) GetExtremeFeeHtlcInfo() *ServerHtlcSigningInfo { + if x != nil { + return x.ExtremeFeeHtlcInfo + } + return nil +} + +type ServerHtlcSigningInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The nonces that the server used to generate the partial htlc tx sigs. + Nonces [][]byte `protobuf:"bytes,1,rep,name=nonces,proto3" json:"nonces,omitempty"` + // The fee rate in sat/kw that the server wants to use for the htlc tx. + FeeRate uint64 `protobuf:"varint,2,opt,name=fee_rate,json=feeRate,proto3" json:"fee_rate,omitempty"` +} + +func (x *ServerHtlcSigningInfo) Reset() { + *x = ServerHtlcSigningInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_server_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerHtlcSigningInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerHtlcSigningInfo) ProtoMessage() {} + +func (x *ServerHtlcSigningInfo) ProtoReflect() protoreflect.Message { + mi := &file_server_proto_msgTypes[43] + 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 ServerHtlcSigningInfo.ProtoReflect.Descriptor instead. +func (*ServerHtlcSigningInfo) Descriptor() ([]byte, []int) { + return file_server_proto_rawDescGZIP(), []int{43} +} + +func (x *ServerHtlcSigningInfo) GetNonces() [][]byte { + if x != nil { + return x.Nonces + } + return nil +} + +func (x *ServerHtlcSigningInfo) GetFeeRate() uint64 { + if x != nil { + return x.FeeRate + } + return 0 +} + +type PushStaticAddressHtlcSigsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The swap hash that the client wants to push the htlc sigs for. + SwapHash []byte `protobuf:"bytes,1,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"` + // The nonces that the client used to generate the htlc sigs. + StandardHtlcInfo *ClientHtlcSigningInfo `protobuf:"bytes,2,opt,name=standard_htlc_info,json=standardHtlcInfo,proto3" json:"standard_htlc_info,omitempty"` + // The nonces that the client used to generate the high fee htlc sigs. + HighFeeHtlcInfo *ClientHtlcSigningInfo `protobuf:"bytes,3,opt,name=high_fee_htlc_info,json=highFeeHtlcInfo,proto3" json:"high_fee_htlc_info,omitempty"` + // The nonces that the client used to generate the extreme fee htlc sigs. + ExtremeFeeHtlcInfo *ClientHtlcSigningInfo `protobuf:"bytes,4,opt,name=extreme_fee_htlc_info,json=extremeFeeHtlcInfo,proto3" json:"extreme_fee_htlc_info,omitempty"` +} + +func (x *PushStaticAddressHtlcSigsRequest) Reset() { + *x = PushStaticAddressHtlcSigsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_server_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushStaticAddressHtlcSigsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushStaticAddressHtlcSigsRequest) ProtoMessage() {} + +func (x *PushStaticAddressHtlcSigsRequest) ProtoReflect() protoreflect.Message { + mi := &file_server_proto_msgTypes[44] + 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 PushStaticAddressHtlcSigsRequest.ProtoReflect.Descriptor instead. +func (*PushStaticAddressHtlcSigsRequest) Descriptor() ([]byte, []int) { + return file_server_proto_rawDescGZIP(), []int{44} +} + +func (x *PushStaticAddressHtlcSigsRequest) GetSwapHash() []byte { + if x != nil { + return x.SwapHash + } + return nil +} + +func (x *PushStaticAddressHtlcSigsRequest) GetStandardHtlcInfo() *ClientHtlcSigningInfo { + if x != nil { + return x.StandardHtlcInfo + } + return nil +} + +func (x *PushStaticAddressHtlcSigsRequest) GetHighFeeHtlcInfo() *ClientHtlcSigningInfo { + if x != nil { + return x.HighFeeHtlcInfo + } + return nil +} + +func (x *PushStaticAddressHtlcSigsRequest) GetExtremeFeeHtlcInfo() *ClientHtlcSigningInfo { + if x != nil { + return x.ExtremeFeeHtlcInfo + } + return nil +} + +type ClientHtlcSigningInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The nonces that the client used to generate the partial htlc tx sigs. + Nonces [][]byte `protobuf:"bytes,1,rep,name=nonces,proto3" json:"nonces,omitempty"` + // The musig2 htlc sigs that the client generated for the htlc tx. + Sigs [][]byte `protobuf:"bytes,2,rep,name=sigs,proto3" json:"sigs,omitempty"` +} + +func (x *ClientHtlcSigningInfo) Reset() { + *x = ClientHtlcSigningInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_server_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClientHtlcSigningInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClientHtlcSigningInfo) ProtoMessage() {} + +func (x *ClientHtlcSigningInfo) ProtoReflect() protoreflect.Message { + mi := &file_server_proto_msgTypes[45] + 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 ClientHtlcSigningInfo.ProtoReflect.Descriptor instead. +func (*ClientHtlcSigningInfo) Descriptor() ([]byte, []int) { + return file_server_proto_rawDescGZIP(), []int{45} +} + +func (x *ClientHtlcSigningInfo) GetNonces() [][]byte { + if x != nil { + return x.Nonces + } + return nil +} + +func (x *ClientHtlcSigningInfo) GetSigs() [][]byte { + if x != nil { + return x.Sigs + } + return nil +} + +type PushStaticAddressHtlcSigsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PushStaticAddressHtlcSigsResponse) Reset() { + *x = PushStaticAddressHtlcSigsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_server_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushStaticAddressHtlcSigsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushStaticAddressHtlcSigsResponse) ProtoMessage() {} + +func (x *PushStaticAddressHtlcSigsResponse) ProtoReflect() protoreflect.Message { + mi := &file_server_proto_msgTypes[46] + 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 PushStaticAddressHtlcSigsResponse.ProtoReflect.Descriptor instead. +func (*PushStaticAddressHtlcSigsResponse) Descriptor() ([]byte, []int) { + return file_server_proto_rawDescGZIP(), []int{46} +} + +type FetchSweeplessSweepTxRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The swap hash of the swap that the client wants to fetch the sweepless + // sweep tx for. + SwapHash []byte `protobuf:"bytes,1,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"` +} + +func (x *FetchSweeplessSweepTxRequest) Reset() { + *x = FetchSweeplessSweepTxRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_server_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FetchSweeplessSweepTxRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FetchSweeplessSweepTxRequest) ProtoMessage() {} + +func (x *FetchSweeplessSweepTxRequest) ProtoReflect() protoreflect.Message { + mi := &file_server_proto_msgTypes[47] + 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 FetchSweeplessSweepTxRequest.ProtoReflect.Descriptor instead. +func (*FetchSweeplessSweepTxRequest) Descriptor() ([]byte, []int) { + return file_server_proto_rawDescGZIP(), []int{47} +} + +func (x *FetchSweeplessSweepTxRequest) GetSwapHash() []byte { + if x != nil { + return x.SwapHash + } + return nil +} + +type FetchSweeplessSweepTxResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The address that the server wants to sweep the static address deposits + // to. + SweepAddr string `protobuf:"bytes,1,opt,name=sweep_addr,json=sweepAddr,proto3" json:"sweep_addr,omitempty"` + // The info the server used to generate the standard fee partial sweepless + // tx sigs. + StandardFeeInfo *ServerSweeplessSigningInfo `protobuf:"bytes,2,opt,name=standard_fee_info,json=standardFeeInfo,proto3" json:"standard_fee_info,omitempty"` + // The info the server used to generate the high fee partial sweepless tx + // sigs. + HighFeeInfo *ServerSweeplessSigningInfo `protobuf:"bytes,3,opt,name=high_fee_info,json=highFeeInfo,proto3" json:"high_fee_info,omitempty"` + // The info the server used to generate the extreme fee partial sweepless tx + // sigs. + ExtremeFeeInfo *ServerSweeplessSigningInfo `protobuf:"bytes,4,opt,name=extreme_fee_info,json=extremeFeeInfo,proto3" json:"extreme_fee_info,omitempty"` +} + +func (x *FetchSweeplessSweepTxResponse) Reset() { + *x = FetchSweeplessSweepTxResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_server_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FetchSweeplessSweepTxResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FetchSweeplessSweepTxResponse) ProtoMessage() {} + +func (x *FetchSweeplessSweepTxResponse) ProtoReflect() protoreflect.Message { + mi := &file_server_proto_msgTypes[48] + 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 FetchSweeplessSweepTxResponse.ProtoReflect.Descriptor instead. +func (*FetchSweeplessSweepTxResponse) Descriptor() ([]byte, []int) { + return file_server_proto_rawDescGZIP(), []int{48} +} + +func (x *FetchSweeplessSweepTxResponse) GetSweepAddr() string { + if x != nil { + return x.SweepAddr + } + return "" +} + +func (x *FetchSweeplessSweepTxResponse) GetStandardFeeInfo() *ServerSweeplessSigningInfo { + if x != nil { + return x.StandardFeeInfo + } + return nil +} + +func (x *FetchSweeplessSweepTxResponse) GetHighFeeInfo() *ServerSweeplessSigningInfo { + if x != nil { + return x.HighFeeInfo + } + return nil +} + +func (x *FetchSweeplessSweepTxResponse) GetExtremeFeeInfo() *ServerSweeplessSigningInfo { + if x != nil { + return x.ExtremeFeeInfo + } + return nil +} + +type ServerSweeplessSigningInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The nonces that the server used to generate the partial sweepless tx + // sigs. + Nonces [][]byte `protobuf:"bytes,1,rep,name=nonces,proto3" json:"nonces,omitempty"` + // The fee rate in sat/kw that the server wants to use for the sweepless tx. + FeeRate uint64 `protobuf:"varint,2,opt,name=fee_rate,json=feeRate,proto3" json:"fee_rate,omitempty"` +} + +func (x *ServerSweeplessSigningInfo) Reset() { + *x = ServerSweeplessSigningInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_server_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerSweeplessSigningInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerSweeplessSigningInfo) ProtoMessage() {} + +func (x *ServerSweeplessSigningInfo) ProtoReflect() protoreflect.Message { + mi := &file_server_proto_msgTypes[49] + 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 ServerSweeplessSigningInfo.ProtoReflect.Descriptor instead. +func (*ServerSweeplessSigningInfo) Descriptor() ([]byte, []int) { + return file_server_proto_rawDescGZIP(), []int{49} +} + +func (x *ServerSweeplessSigningInfo) GetNonces() [][]byte { + if x != nil { + return x.Nonces + } + return nil +} + +func (x *ServerSweeplessSigningInfo) GetFeeRate() uint64 { + if x != nil { + return x.FeeRate + } + return 0 +} + +type PushStaticAddressSweeplessSigsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The swap hash of the swap that the client wants to push the sweepless + // sigs for. + SwapHash []byte `protobuf:"bytes,1,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"` + // The info the client used to generate the standard fee partial sweepless + // tx sigs. + StandardSigningInfo *ClientSweeplessSigningInfo `protobuf:"bytes,2,opt,name=standard_signing_info,json=standardSigningInfo,proto3" json:"standard_signing_info,omitempty"` + // The info the client used to generate the high fee partial sweepless tx + // sigs. + HighFeeSigningInfo *ClientSweeplessSigningInfo `protobuf:"bytes,3,opt,name=high_fee_signing_info,json=highFeeSigningInfo,proto3" json:"high_fee_signing_info,omitempty"` + // The info the client used to generate the extreme fee partial sweepless + // tx sigs. + ExtremeFeeSigningInfo *ClientSweeplessSigningInfo `protobuf:"bytes,4,opt,name=extreme_fee_signing_info,json=extremeFeeSigningInfo,proto3" json:"extreme_fee_signing_info,omitempty"` +} + +func (x *PushStaticAddressSweeplessSigsRequest) Reset() { + *x = PushStaticAddressSweeplessSigsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_server_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushStaticAddressSweeplessSigsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushStaticAddressSweeplessSigsRequest) ProtoMessage() {} + +func (x *PushStaticAddressSweeplessSigsRequest) ProtoReflect() protoreflect.Message { + mi := &file_server_proto_msgTypes[50] + 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 PushStaticAddressSweeplessSigsRequest.ProtoReflect.Descriptor instead. +func (*PushStaticAddressSweeplessSigsRequest) Descriptor() ([]byte, []int) { + return file_server_proto_rawDescGZIP(), []int{50} +} + +func (x *PushStaticAddressSweeplessSigsRequest) GetSwapHash() []byte { + if x != nil { + return x.SwapHash + } + return nil +} + +func (x *PushStaticAddressSweeplessSigsRequest) GetStandardSigningInfo() *ClientSweeplessSigningInfo { + if x != nil { + return x.StandardSigningInfo + } + return nil +} + +func (x *PushStaticAddressSweeplessSigsRequest) GetHighFeeSigningInfo() *ClientSweeplessSigningInfo { + if x != nil { + return x.HighFeeSigningInfo + } + return nil +} + +func (x *PushStaticAddressSweeplessSigsRequest) GetExtremeFeeSigningInfo() *ClientSweeplessSigningInfo { + if x != nil { + return x.ExtremeFeeSigningInfo + } + return nil +} + +type ClientSweeplessSigningInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The nonces that the client used to generate the partial sweepless tx + // sigs. + Nonces [][]byte `protobuf:"bytes,1,rep,name=nonces,proto3" json:"nonces,omitempty"` + // The musig2 htlc sigs that the client generated for the sweepless tx. + Sigs [][]byte `protobuf:"bytes,2,rep,name=sigs,proto3" json:"sigs,omitempty"` +} + +func (x *ClientSweeplessSigningInfo) Reset() { + *x = ClientSweeplessSigningInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_server_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClientSweeplessSigningInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClientSweeplessSigningInfo) ProtoMessage() {} + +func (x *ClientSweeplessSigningInfo) ProtoReflect() protoreflect.Message { + mi := &file_server_proto_msgTypes[51] + 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 ClientSweeplessSigningInfo.ProtoReflect.Descriptor instead. +func (*ClientSweeplessSigningInfo) Descriptor() ([]byte, []int) { + return file_server_proto_rawDescGZIP(), []int{51} +} + +func (x *ClientSweeplessSigningInfo) GetNonces() [][]byte { + if x != nil { + return x.Nonces + } + return nil +} + +func (x *ClientSweeplessSigningInfo) GetSigs() [][]byte { + if x != nil { + return x.Sigs + } + return nil +} + +type PushStaticAddressSweeplessSigsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PushStaticAddressSweeplessSigsResponse) Reset() { + *x = PushStaticAddressSweeplessSigsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_server_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PushStaticAddressSweeplessSigsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PushStaticAddressSweeplessSigsResponse) ProtoMessage() {} + +func (x *PushStaticAddressSweeplessSigsResponse) ProtoReflect() protoreflect.Message { + mi := &file_server_proto_msgTypes[52] + 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 PushStaticAddressSweeplessSigsResponse.ProtoReflect.Descriptor instead. +func (*PushStaticAddressSweeplessSigsResponse) Descriptor() ([]byte, []int) { + return file_server_proto_rawDescGZIP(), []int{52} +} + var File_server_proto protoreflect.FileDescriptor var file_server_proto_rawDesc = []byte{ @@ -3598,190 +4399,352 @@ var file_server_proto_rawDesc = []byte{ 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, 0x2a, 0xef, 0x01, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, - 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, - 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x4e, 0x41, 0x54, 0x49, 0x56, 0x45, 0x5f, - 0x53, 0x45, 0x47, 0x57, 0x49, 0x54, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x02, - 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x52, 0x45, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x50, 0x55, 0x53, - 0x48, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, - 0x55, 0x53, 0x45, 0x52, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, - 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x56, - 0x32, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x4c, 0x4f, 0x4f, - 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x06, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, - 0x55, 0x54, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x50, - 0x52, 0x4f, 0x42, 0x45, 0x10, 0x08, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x4f, 0x55, 0x54, 0x49, 0x4e, - 0x47, 0x5f, 0x50, 0x4c, 0x55, 0x47, 0x49, 0x4e, 0x10, 0x09, 0x12, 0x0b, 0x0a, 0x07, 0x48, 0x54, - 0x4c, 0x43, 0x5f, 0x56, 0x33, 0x10, 0x0a, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x55, 0x53, 0x49, 0x47, - 0x32, 0x10, 0x0b, 0x2a, 0x9e, 0x04, 0x0a, 0x0f, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x77, - 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x45, 0x52, 0x56, 0x45, - 0x52, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, - 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x50, 0x55, 0x42, - 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x45, 0x52, 0x56, - 0x45, 0x52, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, - 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x55, 0x4e, - 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, - 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x5f, 0x48, 0x54, 0x4c, 0x43, - 0x10, 0x04, 0x12, 0x25, 0x0a, 0x21, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, - 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x48, 0x54, 0x4c, 0x43, - 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x53, 0x45, 0x52, - 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4f, 0x46, 0x46, 0x5f, 0x43, - 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x06, 0x12, 0x19, - 0x0a, 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, - 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x45, 0x52, - 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, - 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x08, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x45, - 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x48, 0x54, 0x4c, 0x43, - 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x09, 0x12, 0x1c, - 0x0a, 0x18, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, - 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x0a, 0x12, 0x1d, 0x0a, 0x19, - 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, - 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x0b, 0x12, 0x19, 0x0a, 0x15, 0x53, - 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, - 0x52, 0x4d, 0x45, 0x44, 0x10, 0x0c, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, - 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x52, 0x45, 0x50, 0x41, 0x59, 0x5f, 0x43, - 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x0d, 0x12, 0x20, 0x0a, 0x1c, 0x53, 0x45, 0x52, 0x56, 0x45, - 0x52, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, - 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x0e, 0x12, 0x27, 0x0a, 0x23, 0x53, 0x45, 0x52, - 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, - 0x50, 0x4c, 0x45, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x53, - 0x10, 0x0f, 0x12, 0x20, 0x0a, 0x1c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, - 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, 0x49, - 0x4f, 0x4e, 0x10, 0x10, 0x2a, 0x4a, 0x0a, 0x10, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x50, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x4f, 0x55, 0x54, - 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x50, - 0x52, 0x45, 0x50, 0x41, 0x59, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, - 0x0d, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x02, - 0x2a, 0xf1, 0x01, 0x0a, 0x14, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x17, 0x4c, 0x4e, 0x44, - 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, - 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4c, 0x4e, 0x44, 0x5f, 0x46, 0x41, - 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, - 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x4c, 0x4e, 0x44, 0x5f, 0x46, 0x41, - 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, - 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x4c, 0x4e, 0x44, 0x5f, 0x46, - 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, - 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x30, 0x0a, 0x2c, 0x4c, 0x4e, 0x44, 0x5f, 0x46, 0x41, 0x49, - 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, - 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, - 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x2b, 0x0a, 0x27, 0x4c, 0x4e, 0x44, 0x5f, 0x46, - 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, - 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, - 0x43, 0x45, 0x10, 0x05, 0x2a, 0x27, 0x0a, 0x0d, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, - 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, - 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x57, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x01, 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, 0xc9, 0x0b, 0x0a, 0x0a, 0x53, 0x77, 0x61, 0x70, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, - 0x65, 0x72, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x73, 0x22, 0x82, 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, 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, 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, 0x3b, 0x0a, 0x1c, 0x46, 0x65, 0x74, 0x63, 0x68, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, + 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x54, 0x78, 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, 0x22, 0xa7, 0x02, + 0x0a, 0x1d, 0x46, 0x65, 0x74, 0x63, 0x68, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, + 0x53, 0x77, 0x65, 0x65, 0x70, 0x54, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x77, 0x65, 0x65, 0x70, 0x41, 0x64, 0x64, 0x72, 0x12, 0x4f, + 0x0a, 0x11, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, + 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, + 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x46, 0x65, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x47, 0x0a, 0x0d, 0x68, 0x69, 0x67, 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, + 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x68, 0x69, 0x67, + 0x68, 0x46, 0x65, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4d, 0x0a, 0x10, 0x65, 0x78, 0x74, 0x72, + 0x65, 0x6d, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, + 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x65, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, + 0x46, 0x65, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x4f, 0x0a, 0x1a, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 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, 0xd3, 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, + 0x57, 0x0a, 0x15, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x6e, + 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 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, 0x13, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x53, 0x69, 0x67, + 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x56, 0x0a, 0x15, 0x68, 0x69, 0x67, 0x68, + 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, + 0x6f, 0x18, 0x03, 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, 0x12, 0x68, 0x69, + 0x67, 0x68, 0x46, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x5c, 0x0a, 0x18, 0x65, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, + 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 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, 0x15, 0x65, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, + 0x46, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x48, + 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, 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, 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, 0xef, 0x01, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, + 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, + 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x4e, 0x41, 0x54, 0x49, 0x56, 0x45, + 0x5f, 0x53, 0x45, 0x47, 0x57, 0x49, 0x54, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, + 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x52, 0x45, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x50, 0x55, + 0x53, 0x48, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x12, 0x18, 0x0a, + 0x14, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x4c, 0x4f, 0x4f, + 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x48, 0x54, 0x4c, 0x43, 0x5f, + 0x56, 0x32, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x4c, 0x4f, + 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x06, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, + 0x4f, 0x55, 0x54, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, + 0x50, 0x52, 0x4f, 0x42, 0x45, 0x10, 0x08, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x4f, 0x55, 0x54, 0x49, + 0x4e, 0x47, 0x5f, 0x50, 0x4c, 0x55, 0x47, 0x49, 0x4e, 0x10, 0x09, 0x12, 0x0b, 0x0a, 0x07, 0x48, + 0x54, 0x4c, 0x43, 0x5f, 0x56, 0x33, 0x10, 0x0a, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x55, 0x53, 0x49, + 0x47, 0x32, 0x10, 0x0b, 0x2a, 0x9e, 0x04, 0x0a, 0x0f, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, + 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x45, 0x52, 0x56, + 0x45, 0x52, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, + 0x0a, 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x50, 0x55, + 0x42, 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x45, 0x52, + 0x56, 0x45, 0x52, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x19, 0x0a, + 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x45, 0x52, 0x56, + 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x5f, 0x48, 0x54, 0x4c, + 0x43, 0x10, 0x04, 0x12, 0x25, 0x0a, 0x21, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, + 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x48, 0x54, 0x4c, + 0x43, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x53, 0x45, + 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4f, 0x46, 0x46, 0x5f, + 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x06, 0x12, + 0x19, 0x0a, 0x15, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, + 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x45, + 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x53, 0x57, 0x41, 0x50, + 0x5f, 0x44, 0x45, 0x41, 0x44, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x08, 0x12, 0x22, 0x0a, 0x1e, 0x53, + 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x48, 0x54, 0x4c, + 0x43, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x09, 0x12, + 0x1c, 0x0a, 0x18, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, + 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x0a, 0x12, 0x1d, 0x0a, + 0x19, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x45, 0x58, 0x50, 0x45, 0x43, 0x54, + 0x45, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x0b, 0x12, 0x19, 0x0a, 0x15, + 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x43, 0x4f, 0x4e, 0x46, + 0x49, 0x52, 0x4d, 0x45, 0x44, 0x10, 0x0c, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x45, 0x52, 0x56, 0x45, + 0x52, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x52, 0x45, 0x50, 0x41, 0x59, 0x5f, + 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x0d, 0x12, 0x20, 0x0a, 0x1c, 0x53, 0x45, 0x52, 0x56, + 0x45, 0x52, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, + 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x0e, 0x12, 0x27, 0x0a, 0x23, 0x53, 0x45, + 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4d, 0x55, 0x4c, 0x54, + 0x49, 0x50, 0x4c, 0x45, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, + 0x53, 0x10, 0x0f, 0x12, 0x20, 0x0a, 0x1c, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x46, 0x41, + 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, + 0x49, 0x4f, 0x4e, 0x10, 0x10, 0x2a, 0x4a, 0x0a, 0x10, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x4f, 0x55, + 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, + 0x50, 0x52, 0x45, 0x50, 0x41, 0x59, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x01, 0x12, 0x11, + 0x0a, 0x0d, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, + 0x02, 0x2a, 0xf1, 0x01, 0x0a, 0x14, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x17, 0x4c, 0x4e, + 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4c, 0x4e, 0x44, 0x5f, 0x46, + 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, + 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x4c, 0x4e, 0x44, 0x5f, 0x46, + 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, + 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x4c, 0x4e, 0x44, 0x5f, + 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, + 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x30, 0x0a, 0x2c, 0x4c, 0x4e, 0x44, 0x5f, 0x46, 0x41, + 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, + 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, + 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x2b, 0x0a, 0x27, 0x4c, 0x4e, 0x44, 0x5f, + 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, + 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, + 0x4e, 0x43, 0x45, 0x10, 0x05, 0x2a, 0x27, 0x0a, 0x0d, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, + 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, + 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x57, 0x5f, 0x48, 0x49, 0x47, 0x48, 0x10, 0x01, 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, 0xc9, 0x0b, 0x0a, 0x0a, 0x53, 0x77, 0x61, 0x70, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, + 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, + 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, + 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x4f, 0x0a, 0x0e, 0x4e, 0x65, 0x77, 0x4c, 0x6f, 0x6f, + 0x70, 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70, 0x12, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, - 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x4f, 0x0a, 0x0e, 0x4e, 0x65, 0x77, 0x4c, 0x6f, 0x6f, 0x70, - 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70, 0x12, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6c, 0x0a, 0x13, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, - 0x74, 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x29, 0x2e, - 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, - 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, - 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, - 0x75, 0x6f, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, - 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x54, - 0x65, 0x72, 0x6d, 0x73, 0x12, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x54, 0x65, - 0x72, 0x6d, 0x73, 0x12, 0x4c, 0x0a, 0x0d, 0x4e, 0x65, 0x77, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, - 0x53, 0x77, 0x61, 0x70, 0x12, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x54, 0x0a, 0x0b, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, - 0x12, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, - 0x12, 0x65, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x6f, - 0x70, 0x49, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6c, - 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, - 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70, 0x12, 0x21, 0x2e, 0x6c, - 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4c, 0x6f, 0x6f, - 0x70, 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x1b, 0x2e, 0x6c, - 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, - 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x16, 0x52, 0x65, 0x63, 0x6f, 0x6d, - 0x6d, 0x65, 0x6e, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x63, 0x6f, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6c, 0x0a, 0x13, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, + 0x75, 0x74, 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x29, + 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, + 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, + 0x74, 0x50, 0x75, 0x73, 0x68, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, + 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, + 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, + 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x54, + 0x65, 0x72, 0x6d, 0x73, 0x12, 0x4c, 0x0a, 0x0d, 0x4e, 0x65, 0x77, 0x4c, 0x6f, 0x6f, 0x70, 0x49, + 0x6e, 0x53, 0x77, 0x61, 0x70, 0x12, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0b, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, + 0x65, 0x12, 0x21, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, + 0x01, 0x12, 0x65, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4c, 0x6f, + 0x6f, 0x70, 0x49, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6f, + 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70, 0x12, 0x21, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4c, 0x6f, + 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x1b, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x72, + 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, + 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x62, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x16, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, - 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x13, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x12, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, - 0x71, 0x1a, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6f, + 0x69, 0x6e, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x63, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, + 0x67, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x13, 0x52, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x12, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, - 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x0f, 0x4d, 0x75, 0x53, 0x69, 0x67, 0x32, 0x53, 0x69, 0x67, 0x6e, - 0x53, 0x77, 0x65, 0x65, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, - 0x4d, 0x75, 0x53, 0x69, 0x67, 0x32, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x77, 0x65, 0x65, 0x70, 0x52, - 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x75, 0x53, - 0x69, 0x67, 0x32, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x77, 0x65, 0x65, 0x70, 0x52, 0x65, 0x73, 0x12, - 0x3f, 0x0a, 0x07, 0x50, 0x75, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, - 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x73, 0x68, 0x4b, - 0x65, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, - 0x12, 0x42, 0x0a, 0x09, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4c, 0x34, 0x30, 0x32, 0x12, 0x19, 0x2e, - 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4c, 0x34, 0x30, - 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, - 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4c, 0x34, 0x30, 0x32, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, - 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x62, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, - 0x01, 0x32, 0xc9, 0x01, 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, 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, + 0x65, 0x71, 0x1a, 0x1f, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x52, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x0f, 0x4d, 0x75, 0x53, 0x69, 0x67, 0x32, 0x53, 0x69, 0x67, + 0x6e, 0x53, 0x77, 0x65, 0x65, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x4d, 0x75, 0x53, 0x69, 0x67, 0x32, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x77, 0x65, 0x65, 0x70, + 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x75, + 0x53, 0x69, 0x67, 0x32, 0x53, 0x69, 0x67, 0x6e, 0x53, 0x77, 0x65, 0x65, 0x70, 0x52, 0x65, 0x73, + 0x12, 0x3f, 0x0a, 0x07, 0x50, 0x75, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x6c, 0x6f, + 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x73, 0x68, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x73, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x73, 0x12, 0x42, 0x0a, 0x09, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4c, 0x34, 0x30, 0x32, 0x12, 0x19, + 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4c, 0x34, + 0x30, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, + 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4c, 0x34, 0x30, 0x32, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x62, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x26, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x30, 0x01, 0x32, 0x9d, 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, 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, 0x66, 0x0a, 0x15, 0x46, 0x65, 0x74, 0x63, 0x68, 0x53, + 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x54, 0x78, 0x12, + 0x25, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x53, + 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x77, 0x65, 0x65, 0x70, 0x54, 0x78, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, + 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x53, 0x77, 0x65, 0x65, 0x70, 0x6c, 0x65, 0x73, 0x73, 0x53, + 0x77, 0x65, 0x65, 0x70, 0x54, 0x78, 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 ( @@ -3797,64 +4760,76 @@ func file_server_proto_rawDescGZIP() []byte { } var file_server_proto_enumTypes = make([]protoimpl.EnumInfo, 6) -var file_server_proto_msgTypes = make([]protoimpl.MessageInfo, 41) +var file_server_proto_msgTypes = make([]protoimpl.MessageInfo, 53) var file_server_proto_goTypes = []interface{}{ - (ProtocolVersion)(0), // 0: looprpc.ProtocolVersion - (ServerSwapState)(0), // 1: looprpc.ServerSwapState - (RoutePaymentType)(0), // 2: looprpc.RoutePaymentType - (PaymentFailureReason)(0), // 3: looprpc.PaymentFailureReason - (RoutingPlugin)(0), // 4: looprpc.RoutingPlugin - (StaticAddressProtocolVersion)(0), // 5: looprpc.StaticAddressProtocolVersion - (*ServerLoopOutRequest)(nil), // 6: looprpc.ServerLoopOutRequest - (*ServerLoopOutResponse)(nil), // 7: looprpc.ServerLoopOutResponse - (*ServerLoopOutQuoteRequest)(nil), // 8: looprpc.ServerLoopOutQuoteRequest - (*ServerLoopOutQuote)(nil), // 9: looprpc.ServerLoopOutQuote - (*ServerLoopOutTermsRequest)(nil), // 10: looprpc.ServerLoopOutTermsRequest - (*ServerLoopOutTerms)(nil), // 11: looprpc.ServerLoopOutTerms - (*ServerLoopInRequest)(nil), // 12: looprpc.ServerLoopInRequest - (*ServerLoopInResponse)(nil), // 13: looprpc.ServerLoopInResponse - (*ServerLoopInQuoteRequest)(nil), // 14: looprpc.ServerLoopInQuoteRequest - (*ServerLoopInQuoteResponse)(nil), // 15: looprpc.ServerLoopInQuoteResponse - (*ServerLoopInTermsRequest)(nil), // 16: looprpc.ServerLoopInTermsRequest - (*ServerLoopInTerms)(nil), // 17: looprpc.ServerLoopInTerms - (*ServerLoopOutPushPreimageRequest)(nil), // 18: looprpc.ServerLoopOutPushPreimageRequest - (*ServerLoopOutPushPreimageResponse)(nil), // 19: looprpc.ServerLoopOutPushPreimageResponse - (*SubscribeUpdatesRequest)(nil), // 20: looprpc.SubscribeUpdatesRequest - (*SubscribeLoopOutUpdatesResponse)(nil), // 21: looprpc.SubscribeLoopOutUpdatesResponse - (*SubscribeLoopInUpdatesResponse)(nil), // 22: looprpc.SubscribeLoopInUpdatesResponse - (*RouteCancel)(nil), // 23: looprpc.RouteCancel - (*HtlcAttempt)(nil), // 24: looprpc.HtlcAttempt - (*CancelLoopOutSwapRequest)(nil), // 25: looprpc.CancelLoopOutSwapRequest - (*CancelLoopOutSwapResponse)(nil), // 26: looprpc.CancelLoopOutSwapResponse - (*ServerProbeRequest)(nil), // 27: looprpc.ServerProbeRequest - (*ServerProbeResponse)(nil), // 28: looprpc.ServerProbeResponse - (*RecommendRoutingPluginReq)(nil), // 29: looprpc.RecommendRoutingPluginReq - (*RecommendRoutingPluginRes)(nil), // 30: looprpc.RecommendRoutingPluginRes - (*ReportRoutingResultReq)(nil), // 31: looprpc.ReportRoutingResultReq - (*ReportRoutingResultRes)(nil), // 32: looprpc.ReportRoutingResultRes - (*MuSig2SignSweepReq)(nil), // 33: looprpc.MuSig2SignSweepReq - (*PrevoutInfo)(nil), // 34: looprpc.PrevoutInfo - (*MuSig2SignSweepRes)(nil), // 35: looprpc.MuSig2SignSweepRes - (*ServerPushKeyReq)(nil), // 36: looprpc.ServerPushKeyReq - (*ServerPushKeyRes)(nil), // 37: looprpc.ServerPushKeyRes - (*FetchL402Request)(nil), // 38: looprpc.FetchL402Request - (*FetchL402Response)(nil), // 39: looprpc.FetchL402Response - (*SubscribeNotificationsRequest)(nil), // 40: looprpc.SubscribeNotificationsRequest - (*SubscribeNotificationsResponse)(nil), // 41: looprpc.SubscribeNotificationsResponse - (*ServerNewAddressRequest)(nil), // 42: looprpc.ServerNewAddressRequest - (*ServerNewAddressResponse)(nil), // 43: looprpc.ServerNewAddressResponse - (*ServerAddressParameters)(nil), // 44: looprpc.ServerAddressParameters - (*ServerWithdrawRequest)(nil), // 45: looprpc.ServerWithdrawRequest - (*ServerWithdrawResponse)(nil), // 46: looprpc.ServerWithdrawResponse - (*RouteHint)(nil), // 47: looprpc.RouteHint - (*ServerReservationNotification)(nil), // 48: looprpc.ServerReservationNotification + (ProtocolVersion)(0), // 0: looprpc.ProtocolVersion + (ServerSwapState)(0), // 1: looprpc.ServerSwapState + (RoutePaymentType)(0), // 2: looprpc.RoutePaymentType + (PaymentFailureReason)(0), // 3: looprpc.PaymentFailureReason + (RoutingPlugin)(0), // 4: looprpc.RoutingPlugin + (StaticAddressProtocolVersion)(0), // 5: looprpc.StaticAddressProtocolVersion + (*ServerLoopOutRequest)(nil), // 6: looprpc.ServerLoopOutRequest + (*ServerLoopOutResponse)(nil), // 7: looprpc.ServerLoopOutResponse + (*ServerLoopOutQuoteRequest)(nil), // 8: looprpc.ServerLoopOutQuoteRequest + (*ServerLoopOutQuote)(nil), // 9: looprpc.ServerLoopOutQuote + (*ServerLoopOutTermsRequest)(nil), // 10: looprpc.ServerLoopOutTermsRequest + (*ServerLoopOutTerms)(nil), // 11: looprpc.ServerLoopOutTerms + (*ServerLoopInRequest)(nil), // 12: looprpc.ServerLoopInRequest + (*ServerLoopInResponse)(nil), // 13: looprpc.ServerLoopInResponse + (*ServerLoopInQuoteRequest)(nil), // 14: looprpc.ServerLoopInQuoteRequest + (*ServerLoopInQuoteResponse)(nil), // 15: looprpc.ServerLoopInQuoteResponse + (*ServerLoopInTermsRequest)(nil), // 16: looprpc.ServerLoopInTermsRequest + (*ServerLoopInTerms)(nil), // 17: looprpc.ServerLoopInTerms + (*ServerLoopOutPushPreimageRequest)(nil), // 18: looprpc.ServerLoopOutPushPreimageRequest + (*ServerLoopOutPushPreimageResponse)(nil), // 19: looprpc.ServerLoopOutPushPreimageResponse + (*SubscribeUpdatesRequest)(nil), // 20: looprpc.SubscribeUpdatesRequest + (*SubscribeLoopOutUpdatesResponse)(nil), // 21: looprpc.SubscribeLoopOutUpdatesResponse + (*SubscribeLoopInUpdatesResponse)(nil), // 22: looprpc.SubscribeLoopInUpdatesResponse + (*RouteCancel)(nil), // 23: looprpc.RouteCancel + (*HtlcAttempt)(nil), // 24: looprpc.HtlcAttempt + (*CancelLoopOutSwapRequest)(nil), // 25: looprpc.CancelLoopOutSwapRequest + (*CancelLoopOutSwapResponse)(nil), // 26: looprpc.CancelLoopOutSwapResponse + (*ServerProbeRequest)(nil), // 27: looprpc.ServerProbeRequest + (*ServerProbeResponse)(nil), // 28: looprpc.ServerProbeResponse + (*RecommendRoutingPluginReq)(nil), // 29: looprpc.RecommendRoutingPluginReq + (*RecommendRoutingPluginRes)(nil), // 30: looprpc.RecommendRoutingPluginRes + (*ReportRoutingResultReq)(nil), // 31: looprpc.ReportRoutingResultReq + (*ReportRoutingResultRes)(nil), // 32: looprpc.ReportRoutingResultRes + (*MuSig2SignSweepReq)(nil), // 33: looprpc.MuSig2SignSweepReq + (*PrevoutInfo)(nil), // 34: looprpc.PrevoutInfo + (*MuSig2SignSweepRes)(nil), // 35: looprpc.MuSig2SignSweepRes + (*ServerPushKeyReq)(nil), // 36: looprpc.ServerPushKeyReq + (*ServerPushKeyRes)(nil), // 37: looprpc.ServerPushKeyRes + (*FetchL402Request)(nil), // 38: looprpc.FetchL402Request + (*FetchL402Response)(nil), // 39: looprpc.FetchL402Response + (*SubscribeNotificationsRequest)(nil), // 40: looprpc.SubscribeNotificationsRequest + (*SubscribeNotificationsResponse)(nil), // 41: looprpc.SubscribeNotificationsResponse + (*ServerNewAddressRequest)(nil), // 42: looprpc.ServerNewAddressRequest + (*ServerNewAddressResponse)(nil), // 43: looprpc.ServerNewAddressResponse + (*ServerAddressParameters)(nil), // 44: looprpc.ServerAddressParameters + (*ServerWithdrawRequest)(nil), // 45: looprpc.ServerWithdrawRequest + (*ServerWithdrawResponse)(nil), // 46: looprpc.ServerWithdrawResponse + (*ServerStaticAddressLoopInRequest)(nil), // 47: looprpc.ServerStaticAddressLoopInRequest + (*ServerStaticAddressLoopInResponse)(nil), // 48: looprpc.ServerStaticAddressLoopInResponse + (*ServerHtlcSigningInfo)(nil), // 49: looprpc.ServerHtlcSigningInfo + (*PushStaticAddressHtlcSigsRequest)(nil), // 50: looprpc.PushStaticAddressHtlcSigsRequest + (*ClientHtlcSigningInfo)(nil), // 51: looprpc.ClientHtlcSigningInfo + (*PushStaticAddressHtlcSigsResponse)(nil), // 52: looprpc.PushStaticAddressHtlcSigsResponse + (*FetchSweeplessSweepTxRequest)(nil), // 53: looprpc.FetchSweeplessSweepTxRequest + (*FetchSweeplessSweepTxResponse)(nil), // 54: looprpc.FetchSweeplessSweepTxResponse + (*ServerSweeplessSigningInfo)(nil), // 55: looprpc.ServerSweeplessSigningInfo + (*PushStaticAddressSweeplessSigsRequest)(nil), // 56: looprpc.PushStaticAddressSweeplessSigsRequest + (*ClientSweeplessSigningInfo)(nil), // 57: looprpc.ClientSweeplessSigningInfo + (*PushStaticAddressSweeplessSigsResponse)(nil), // 58: looprpc.PushStaticAddressSweeplessSigsResponse + (*RouteHint)(nil), // 59: looprpc.RouteHint + (*ServerReservationNotification)(nil), // 60: looprpc.ServerReservationNotification } var file_server_proto_depIdxs = []int32{ 0, // 0: looprpc.ServerLoopOutRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 1: looprpc.ServerLoopOutQuoteRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 2: looprpc.ServerLoopOutTermsRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 3: looprpc.ServerLoopInRequest.protocol_version:type_name -> looprpc.ProtocolVersion - 47, // 4: looprpc.ServerLoopInQuoteRequest.route_hints:type_name -> looprpc.RouteHint + 59, // 4: looprpc.ServerLoopInQuoteRequest.route_hints:type_name -> looprpc.RouteHint 0, // 5: looprpc.ServerLoopInQuoteRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 6: looprpc.ServerLoopInTermsRequest.protocol_version:type_name -> looprpc.ProtocolVersion 0, // 7: looprpc.ServerLoopOutPushPreimageRequest.protocol_version:type_name -> looprpc.ProtocolVersion @@ -3867,7 +4842,7 @@ var file_server_proto_depIdxs = []int32{ 0, // 14: looprpc.CancelLoopOutSwapRequest.protocol_version:type_name -> looprpc.ProtocolVersion 23, // 15: looprpc.CancelLoopOutSwapRequest.route_cancel:type_name -> looprpc.RouteCancel 0, // 16: looprpc.ServerProbeRequest.protocol_version:type_name -> looprpc.ProtocolVersion - 47, // 17: looprpc.ServerProbeRequest.route_hints:type_name -> looprpc.RouteHint + 59, // 17: looprpc.ServerProbeRequest.route_hints:type_name -> looprpc.RouteHint 0, // 18: looprpc.RecommendRoutingPluginReq.protocol_version:type_name -> looprpc.ProtocolVersion 4, // 19: looprpc.RecommendRoutingPluginRes.plugin:type_name -> looprpc.RoutingPlugin 0, // 20: looprpc.ReportRoutingResultReq.protocol_version:type_name -> looprpc.ProtocolVersion @@ -3875,53 +4850,74 @@ var file_server_proto_depIdxs = []int32{ 0, // 22: looprpc.MuSig2SignSweepReq.protocol_version:type_name -> looprpc.ProtocolVersion 34, // 23: looprpc.MuSig2SignSweepReq.prevout_info:type_name -> looprpc.PrevoutInfo 0, // 24: looprpc.ServerPushKeyReq.protocol_version:type_name -> looprpc.ProtocolVersion - 48, // 25: looprpc.SubscribeNotificationsResponse.reservation_notification:type_name -> looprpc.ServerReservationNotification + 60, // 25: looprpc.SubscribeNotificationsResponse.reservation_notification:type_name -> looprpc.ServerReservationNotification 5, // 26: looprpc.ServerNewAddressRequest.protocol_version:type_name -> looprpc.StaticAddressProtocolVersion 44, // 27: looprpc.ServerNewAddressResponse.params:type_name -> looprpc.ServerAddressParameters 34, // 28: looprpc.ServerWithdrawRequest.outpoints:type_name -> looprpc.PrevoutInfo - 10, // 29: looprpc.SwapServer.LoopOutTerms:input_type -> looprpc.ServerLoopOutTermsRequest - 6, // 30: looprpc.SwapServer.NewLoopOutSwap:input_type -> looprpc.ServerLoopOutRequest - 18, // 31: looprpc.SwapServer.LoopOutPushPreimage:input_type -> looprpc.ServerLoopOutPushPreimageRequest - 8, // 32: looprpc.SwapServer.LoopOutQuote:input_type -> looprpc.ServerLoopOutQuoteRequest - 16, // 33: looprpc.SwapServer.LoopInTerms:input_type -> looprpc.ServerLoopInTermsRequest - 12, // 34: looprpc.SwapServer.NewLoopInSwap:input_type -> looprpc.ServerLoopInRequest - 14, // 35: looprpc.SwapServer.LoopInQuote:input_type -> looprpc.ServerLoopInQuoteRequest - 20, // 36: looprpc.SwapServer.SubscribeLoopOutUpdates:input_type -> looprpc.SubscribeUpdatesRequest - 20, // 37: looprpc.SwapServer.SubscribeLoopInUpdates:input_type -> looprpc.SubscribeUpdatesRequest - 25, // 38: looprpc.SwapServer.CancelLoopOutSwap:input_type -> looprpc.CancelLoopOutSwapRequest - 27, // 39: looprpc.SwapServer.Probe:input_type -> looprpc.ServerProbeRequest - 29, // 40: looprpc.SwapServer.RecommendRoutingPlugin:input_type -> looprpc.RecommendRoutingPluginReq - 31, // 41: looprpc.SwapServer.ReportRoutingResult:input_type -> looprpc.ReportRoutingResultReq - 33, // 42: looprpc.SwapServer.MuSig2SignSweep:input_type -> looprpc.MuSig2SignSweepReq - 36, // 43: looprpc.SwapServer.PushKey:input_type -> looprpc.ServerPushKeyReq - 38, // 44: looprpc.SwapServer.FetchL402:input_type -> looprpc.FetchL402Request - 40, // 45: looprpc.SwapServer.SubscribeNotifications:input_type -> looprpc.SubscribeNotificationsRequest - 42, // 46: looprpc.StaticAddressServer.ServerNewAddress:input_type -> looprpc.ServerNewAddressRequest - 45, // 47: looprpc.StaticAddressServer.ServerWithdrawDeposits:input_type -> looprpc.ServerWithdrawRequest - 11, // 48: looprpc.SwapServer.LoopOutTerms:output_type -> looprpc.ServerLoopOutTerms - 7, // 49: looprpc.SwapServer.NewLoopOutSwap:output_type -> looprpc.ServerLoopOutResponse - 19, // 50: looprpc.SwapServer.LoopOutPushPreimage:output_type -> looprpc.ServerLoopOutPushPreimageResponse - 9, // 51: looprpc.SwapServer.LoopOutQuote:output_type -> looprpc.ServerLoopOutQuote - 17, // 52: looprpc.SwapServer.LoopInTerms:output_type -> looprpc.ServerLoopInTerms - 13, // 53: looprpc.SwapServer.NewLoopInSwap:output_type -> looprpc.ServerLoopInResponse - 15, // 54: looprpc.SwapServer.LoopInQuote:output_type -> looprpc.ServerLoopInQuoteResponse - 21, // 55: looprpc.SwapServer.SubscribeLoopOutUpdates:output_type -> looprpc.SubscribeLoopOutUpdatesResponse - 22, // 56: looprpc.SwapServer.SubscribeLoopInUpdates:output_type -> looprpc.SubscribeLoopInUpdatesResponse - 26, // 57: looprpc.SwapServer.CancelLoopOutSwap:output_type -> looprpc.CancelLoopOutSwapResponse - 28, // 58: looprpc.SwapServer.Probe:output_type -> looprpc.ServerProbeResponse - 30, // 59: looprpc.SwapServer.RecommendRoutingPlugin:output_type -> looprpc.RecommendRoutingPluginRes - 32, // 60: looprpc.SwapServer.ReportRoutingResult:output_type -> looprpc.ReportRoutingResultRes - 35, // 61: looprpc.SwapServer.MuSig2SignSweep:output_type -> looprpc.MuSig2SignSweepRes - 37, // 62: looprpc.SwapServer.PushKey:output_type -> looprpc.ServerPushKeyRes - 39, // 63: looprpc.SwapServer.FetchL402:output_type -> looprpc.FetchL402Response - 41, // 64: looprpc.SwapServer.SubscribeNotifications:output_type -> looprpc.SubscribeNotificationsResponse - 43, // 65: looprpc.StaticAddressServer.ServerNewAddress:output_type -> looprpc.ServerNewAddressResponse - 46, // 66: looprpc.StaticAddressServer.ServerWithdrawDeposits:output_type -> looprpc.ServerWithdrawResponse - 48, // [48:67] is the sub-list for method output_type - 29, // [29:48] is the sub-list for method input_type - 29, // [29:29] is the sub-list for extension type_name - 29, // [29:29] is the sub-list for extension extendee - 0, // [0:29] is the sub-list for field type_name + 5, // 29: looprpc.ServerStaticAddressLoopInRequest.protocol_version:type_name -> looprpc.StaticAddressProtocolVersion + 49, // 30: looprpc.ServerStaticAddressLoopInResponse.standard_htlc_info:type_name -> looprpc.ServerHtlcSigningInfo + 49, // 31: looprpc.ServerStaticAddressLoopInResponse.high_fee_htlc_info:type_name -> looprpc.ServerHtlcSigningInfo + 49, // 32: looprpc.ServerStaticAddressLoopInResponse.extreme_fee_htlc_info:type_name -> looprpc.ServerHtlcSigningInfo + 51, // 33: looprpc.PushStaticAddressHtlcSigsRequest.standard_htlc_info:type_name -> looprpc.ClientHtlcSigningInfo + 51, // 34: looprpc.PushStaticAddressHtlcSigsRequest.high_fee_htlc_info:type_name -> looprpc.ClientHtlcSigningInfo + 51, // 35: looprpc.PushStaticAddressHtlcSigsRequest.extreme_fee_htlc_info:type_name -> looprpc.ClientHtlcSigningInfo + 55, // 36: looprpc.FetchSweeplessSweepTxResponse.standard_fee_info:type_name -> looprpc.ServerSweeplessSigningInfo + 55, // 37: looprpc.FetchSweeplessSweepTxResponse.high_fee_info:type_name -> looprpc.ServerSweeplessSigningInfo + 55, // 38: looprpc.FetchSweeplessSweepTxResponse.extreme_fee_info:type_name -> looprpc.ServerSweeplessSigningInfo + 57, // 39: looprpc.PushStaticAddressSweeplessSigsRequest.standard_signing_info:type_name -> looprpc.ClientSweeplessSigningInfo + 57, // 40: looprpc.PushStaticAddressSweeplessSigsRequest.high_fee_signing_info:type_name -> looprpc.ClientSweeplessSigningInfo + 57, // 41: looprpc.PushStaticAddressSweeplessSigsRequest.extreme_fee_signing_info:type_name -> looprpc.ClientSweeplessSigningInfo + 10, // 42: looprpc.SwapServer.LoopOutTerms:input_type -> looprpc.ServerLoopOutTermsRequest + 6, // 43: looprpc.SwapServer.NewLoopOutSwap:input_type -> looprpc.ServerLoopOutRequest + 18, // 44: looprpc.SwapServer.LoopOutPushPreimage:input_type -> looprpc.ServerLoopOutPushPreimageRequest + 8, // 45: looprpc.SwapServer.LoopOutQuote:input_type -> looprpc.ServerLoopOutQuoteRequest + 16, // 46: looprpc.SwapServer.LoopInTerms:input_type -> looprpc.ServerLoopInTermsRequest + 12, // 47: looprpc.SwapServer.NewLoopInSwap:input_type -> looprpc.ServerLoopInRequest + 14, // 48: looprpc.SwapServer.LoopInQuote:input_type -> looprpc.ServerLoopInQuoteRequest + 20, // 49: looprpc.SwapServer.SubscribeLoopOutUpdates:input_type -> looprpc.SubscribeUpdatesRequest + 20, // 50: looprpc.SwapServer.SubscribeLoopInUpdates:input_type -> looprpc.SubscribeUpdatesRequest + 25, // 51: looprpc.SwapServer.CancelLoopOutSwap:input_type -> looprpc.CancelLoopOutSwapRequest + 27, // 52: looprpc.SwapServer.Probe:input_type -> looprpc.ServerProbeRequest + 29, // 53: looprpc.SwapServer.RecommendRoutingPlugin:input_type -> looprpc.RecommendRoutingPluginReq + 31, // 54: looprpc.SwapServer.ReportRoutingResult:input_type -> looprpc.ReportRoutingResultReq + 33, // 55: looprpc.SwapServer.MuSig2SignSweep:input_type -> looprpc.MuSig2SignSweepReq + 36, // 56: looprpc.SwapServer.PushKey:input_type -> looprpc.ServerPushKeyReq + 38, // 57: looprpc.SwapServer.FetchL402:input_type -> looprpc.FetchL402Request + 40, // 58: looprpc.SwapServer.SubscribeNotifications:input_type -> looprpc.SubscribeNotificationsRequest + 42, // 59: looprpc.StaticAddressServer.ServerNewAddress:input_type -> looprpc.ServerNewAddressRequest + 45, // 60: looprpc.StaticAddressServer.ServerWithdrawDeposits:input_type -> looprpc.ServerWithdrawRequest + 47, // 61: looprpc.StaticAddressServer.ServerStaticAddressLoopIn:input_type -> looprpc.ServerStaticAddressLoopInRequest + 50, // 62: looprpc.StaticAddressServer.PushStaticAddressHtlcSigs:input_type -> looprpc.PushStaticAddressHtlcSigsRequest + 53, // 63: looprpc.StaticAddressServer.FetchSweeplessSweepTx:input_type -> looprpc.FetchSweeplessSweepTxRequest + 56, // 64: looprpc.StaticAddressServer.PushStaticAddressSweeplessSigs:input_type -> looprpc.PushStaticAddressSweeplessSigsRequest + 11, // 65: looprpc.SwapServer.LoopOutTerms:output_type -> looprpc.ServerLoopOutTerms + 7, // 66: looprpc.SwapServer.NewLoopOutSwap:output_type -> looprpc.ServerLoopOutResponse + 19, // 67: looprpc.SwapServer.LoopOutPushPreimage:output_type -> looprpc.ServerLoopOutPushPreimageResponse + 9, // 68: looprpc.SwapServer.LoopOutQuote:output_type -> looprpc.ServerLoopOutQuote + 17, // 69: looprpc.SwapServer.LoopInTerms:output_type -> looprpc.ServerLoopInTerms + 13, // 70: looprpc.SwapServer.NewLoopInSwap:output_type -> looprpc.ServerLoopInResponse + 15, // 71: looprpc.SwapServer.LoopInQuote:output_type -> looprpc.ServerLoopInQuoteResponse + 21, // 72: looprpc.SwapServer.SubscribeLoopOutUpdates:output_type -> looprpc.SubscribeLoopOutUpdatesResponse + 22, // 73: looprpc.SwapServer.SubscribeLoopInUpdates:output_type -> looprpc.SubscribeLoopInUpdatesResponse + 26, // 74: looprpc.SwapServer.CancelLoopOutSwap:output_type -> looprpc.CancelLoopOutSwapResponse + 28, // 75: looprpc.SwapServer.Probe:output_type -> looprpc.ServerProbeResponse + 30, // 76: looprpc.SwapServer.RecommendRoutingPlugin:output_type -> looprpc.RecommendRoutingPluginRes + 32, // 77: looprpc.SwapServer.ReportRoutingResult:output_type -> looprpc.ReportRoutingResultRes + 35, // 78: looprpc.SwapServer.MuSig2SignSweep:output_type -> looprpc.MuSig2SignSweepRes + 37, // 79: looprpc.SwapServer.PushKey:output_type -> looprpc.ServerPushKeyRes + 39, // 80: looprpc.SwapServer.FetchL402:output_type -> looprpc.FetchL402Response + 41, // 81: looprpc.SwapServer.SubscribeNotifications:output_type -> looprpc.SubscribeNotificationsResponse + 43, // 82: looprpc.StaticAddressServer.ServerNewAddress:output_type -> looprpc.ServerNewAddressResponse + 46, // 83: looprpc.StaticAddressServer.ServerWithdrawDeposits:output_type -> looprpc.ServerWithdrawResponse + 48, // 84: looprpc.StaticAddressServer.ServerStaticAddressLoopIn:output_type -> looprpc.ServerStaticAddressLoopInResponse + 52, // 85: looprpc.StaticAddressServer.PushStaticAddressHtlcSigs:output_type -> looprpc.PushStaticAddressHtlcSigsResponse + 54, // 86: looprpc.StaticAddressServer.FetchSweeplessSweepTx:output_type -> looprpc.FetchSweeplessSweepTxResponse + 58, // 87: looprpc.StaticAddressServer.PushStaticAddressSweeplessSigs:output_type -> looprpc.PushStaticAddressSweeplessSigsResponse + 65, // [65:88] is the sub-list for method output_type + 42, // [42:65] is the sub-list for method input_type + 42, // [42:42] is the sub-list for extension type_name + 42, // [42:42] is the sub-list for extension extendee + 0, // [0:42] is the sub-list for field type_name } func init() { file_server_proto_init() } @@ -4424,6 +5420,150 @@ func file_server_proto_init() { return nil } } + file_server_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerStaticAddressLoopInRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_server_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerStaticAddressLoopInResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_server_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerHtlcSigningInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_server_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushStaticAddressHtlcSigsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_server_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientHtlcSigningInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_server_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushStaticAddressHtlcSigsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_server_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FetchSweeplessSweepTxRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_server_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FetchSweeplessSweepTxResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_server_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerSweeplessSigningInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_server_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushStaticAddressSweeplessSigsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_server_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientSweeplessSigningInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_server_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PushStaticAddressSweeplessSigsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_server_proto_msgTypes[19].OneofWrappers = []interface{}{ (*CancelLoopOutSwapRequest_RouteCancel)(nil), @@ -4437,7 +5577,7 @@ func file_server_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_server_proto_rawDesc, NumEnums: 6, - NumMessages: 41, + NumMessages: 53, NumExtensions: 0, NumServices: 2, }, diff --git a/swapserverrpc/server.proto b/swapserverrpc/server.proto index 3f32370cb..a1c3d8827 100644 --- a/swapserverrpc/server.proto +++ b/swapserverrpc/server.proto @@ -689,6 +689,26 @@ service StaticAddressServer { // the partial sigs for the client's selected deposits. rpc ServerWithdrawDeposits (ServerWithdrawRequest) returns (ServerWithdrawResponse); + + // 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. + rpc ServerStaticAddressLoopIn (ServerStaticAddressLoopInRequest) + returns (ServerStaticAddressLoopInResponse); + + // PushStaticAddressHtlcSigs pushes the client's htlc tx sigs to the server. + rpc PushStaticAddressHtlcSigs (PushStaticAddressHtlcSigsRequest) + returns (PushStaticAddressHtlcSigsResponse); + + // FetchSweeplessSweepTx fetches the sweepless sweep tx for the client to + // to sign it. + rpc FetchSweeplessSweepTx (FetchSweeplessSweepTxRequest) + returns (FetchSweeplessSweepTxResponse); + + // PushStaticAddressSweeplessSigs pushes the client's sweepless sweep tx + // sigs to the server. + rpc PushStaticAddressSweeplessSigs (PushStaticAddressSweeplessSigsRequest) + returns (PushStaticAddressSweeplessSigsResponse); } message ServerNewAddressRequest { @@ -733,3 +753,157 @@ message ServerWithdrawResponse { // The nonces that the server used to generate the sweepless sweep sigs. repeated bytes server_nonces = 2; } + +message ServerStaticAddressLoopInRequest { + // The client's public key for the htlc output. + bytes htlc_client_pub_key = 1; + + // The hashed swap invoice preimage of the swap. + bytes swap_hash = 2; + + // The deposit outpoints the client wishes to loop in. They implicitly state + // the swap amount. + repeated string deposit_outpoints = 3; + + // The swap invoice that the client wants the server to pay. + string swap_invoice = 4; + + // An optional last hop the client wants to receive the invoice payment + // from. + bytes last_hop = 5; + + // The protocol version that the client adheres to. + StaticAddressProtocolVersion protocol_version = 6; + + // The user agent string that identifies the software running on the user's + // side. This can be changed in the user's client software but it _SHOULD_ + // conform to the following pattern: + // Agent-Name/semver-version(/additional-info) + // Examples: + // loopd/v0.10.0-beta/commit=3b635821 + // litd/v0.2.0-alpha/commit=326d754 + // loopd/v0.10.0-beta/commit=3b635823,initiator=easy-autoloop + string user_agent = 7; + + // The swap payment timeout allows the user to specify an upper limit for + // the amount of time the server is allowed to take to fulfill the off-chain + // swap payment. If the timeout is reached the swap will be aborted on the + // server side and the client can retry the swap with different parameters. + uint32 payment_timeout_seconds = 8; +} + +message ServerStaticAddressLoopInResponse { + // The server's public key for the htlc output. + bytes htlc_server_pub_key = 1; + + // The cltv expiry height for the htlc. This is the height at which the + // htlc will expire and the client is free to claim the funds back. + int32 htlc_expiry = 2; + + // The info the server used to generate the standard fee partial htlc tx + // sigs. + ServerHtlcSigningInfo standard_htlc_info = 3; + + // The info the server used to generate the high fee partial htlc tx sigs. + ServerHtlcSigningInfo high_fee_htlc_info = 4; + + // The info the server used to generate the extreme fee partial htlc tx + // sigs. + ServerHtlcSigningInfo extreme_fee_htlc_info = 5; +} + +message ServerHtlcSigningInfo { + // The nonces that the server used to generate the partial htlc tx sigs. + repeated bytes nonces = 1; + + // The fee rate in sat/kw that the server wants to use for the htlc tx. + uint64 fee_rate = 2; +} + +message PushStaticAddressHtlcSigsRequest { + // The swap hash that the client wants to push the htlc sigs for. + bytes swap_hash = 1; + + // The nonces that the client used to generate the htlc sigs. + ClientHtlcSigningInfo standard_htlc_info = 2; + + // The nonces that the client used to generate the high fee htlc sigs. + ClientHtlcSigningInfo high_fee_htlc_info = 3; + + // The nonces that the client used to generate the extreme fee htlc sigs. + ClientHtlcSigningInfo extreme_fee_htlc_info = 4; +} + +message ClientHtlcSigningInfo { + // The nonces that the client used to generate the partial htlc tx sigs. + repeated bytes nonces = 1; + + // The musig2 htlc sigs that the client generated for the htlc tx. + repeated bytes sigs = 2; +} + +message PushStaticAddressHtlcSigsResponse { +} + +message FetchSweeplessSweepTxRequest { + // The swap hash of the swap that the client wants to fetch the sweepless + // sweep tx for. + bytes swap_hash = 1; +} + +message FetchSweeplessSweepTxResponse { + // The address that the server wants to sweep the static address deposits + // to. + string sweep_addr = 1; + + // The info the server used to generate the standard fee partial sweepless + // tx sigs. + ServerSweeplessSigningInfo standard_fee_info = 2; + + // The info the server used to generate the high fee partial sweepless tx + // sigs. + ServerSweeplessSigningInfo high_fee_info = 3; + + // The info the server used to generate the extreme fee partial sweepless tx + // sigs. + ServerSweeplessSigningInfo extreme_fee_info = 4; +} + +message ServerSweeplessSigningInfo { + // The nonces that the server used to generate the partial sweepless tx + // sigs. + repeated bytes nonces = 1; + + // The fee rate in sat/kw that the server wants to use for the sweepless tx. + uint64 fee_rate = 2; +} + +message PushStaticAddressSweeplessSigsRequest { + // The swap hash of the swap that the client wants to push the sweepless + // sigs for. + bytes swap_hash = 1; + + // The info the client used to generate the standard fee partial sweepless + // tx sigs. + ClientSweeplessSigningInfo standard_signing_info = 2; + + // The info the client used to generate the high fee partial sweepless tx + // sigs. + ClientSweeplessSigningInfo high_fee_signing_info = 3; + + // The info the client used to generate the extreme fee partial sweepless + // tx sigs. + ClientSweeplessSigningInfo extreme_fee_signing_info = 4; +} + +message ClientSweeplessSigningInfo { + // The nonces that the client used to generate the partial sweepless tx + // sigs. + repeated bytes nonces = 1; + + // The musig2 htlc sigs that the client generated for the sweepless tx. + repeated bytes sigs = 2; +} + +message PushStaticAddressSweeplessSigsResponse { +} diff --git a/swapserverrpc/server_grpc.pb.go b/swapserverrpc/server_grpc.pb.go index 3287898ae..6fc165c73 100644 --- a/swapserverrpc/server_grpc.pb.go +++ b/swapserverrpc/server_grpc.pb.go @@ -774,6 +774,18 @@ 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) + // 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. + ServerStaticAddressLoopIn(ctx context.Context, in *ServerStaticAddressLoopInRequest, opts ...grpc.CallOption) (*ServerStaticAddressLoopInResponse, error) + // PushStaticAddressHtlcSigs pushes the client's htlc tx sigs to the server. + PushStaticAddressHtlcSigs(ctx context.Context, in *PushStaticAddressHtlcSigsRequest, opts ...grpc.CallOption) (*PushStaticAddressHtlcSigsResponse, error) + // FetchSweeplessSweepTx fetches the sweepless sweep tx for the client to + // to sign it. + FetchSweeplessSweepTx(ctx context.Context, in *FetchSweeplessSweepTxRequest, opts ...grpc.CallOption) (*FetchSweeplessSweepTxResponse, error) + // PushStaticAddressSweeplessSigs pushes the client's sweepless sweep tx + // sigs to the server. + PushStaticAddressSweeplessSigs(ctx context.Context, in *PushStaticAddressSweeplessSigsRequest, opts ...grpc.CallOption) (*PushStaticAddressSweeplessSigsResponse, error) } type staticAddressServerClient struct { @@ -802,6 +814,42 @@ func (c *staticAddressServerClient) ServerWithdrawDeposits(ctx context.Context, 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...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *staticAddressServerClient) PushStaticAddressHtlcSigs(ctx context.Context, in *PushStaticAddressHtlcSigsRequest, opts ...grpc.CallOption) (*PushStaticAddressHtlcSigsResponse, error) { + out := new(PushStaticAddressHtlcSigsResponse) + err := c.cc.Invoke(ctx, "/looprpc.StaticAddressServer/PushStaticAddressHtlcSigs", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *staticAddressServerClient) FetchSweeplessSweepTx(ctx context.Context, in *FetchSweeplessSweepTxRequest, opts ...grpc.CallOption) (*FetchSweeplessSweepTxResponse, error) { + out := new(FetchSweeplessSweepTxResponse) + err := c.cc.Invoke(ctx, "/looprpc.StaticAddressServer/FetchSweeplessSweepTx", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *staticAddressServerClient) PushStaticAddressSweeplessSigs(ctx context.Context, in *PushStaticAddressSweeplessSigsRequest, opts ...grpc.CallOption) (*PushStaticAddressSweeplessSigsResponse, error) { + out := new(PushStaticAddressSweeplessSigsResponse) + err := c.cc.Invoke(ctx, "/looprpc.StaticAddressServer/PushStaticAddressSweeplessSigs", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // StaticAddressServerServer is the server API for StaticAddressServer service. // All implementations must embed UnimplementedStaticAddressServerServer // for forward compatibility @@ -814,6 +862,18 @@ 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) + // 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. + ServerStaticAddressLoopIn(context.Context, *ServerStaticAddressLoopInRequest) (*ServerStaticAddressLoopInResponse, error) + // PushStaticAddressHtlcSigs pushes the client's htlc tx sigs to the server. + PushStaticAddressHtlcSigs(context.Context, *PushStaticAddressHtlcSigsRequest) (*PushStaticAddressHtlcSigsResponse, error) + // FetchSweeplessSweepTx fetches the sweepless sweep tx for the client to + // to sign it. + FetchSweeplessSweepTx(context.Context, *FetchSweeplessSweepTxRequest) (*FetchSweeplessSweepTxResponse, error) + // PushStaticAddressSweeplessSigs pushes the client's sweepless sweep tx + // sigs to the server. + PushStaticAddressSweeplessSigs(context.Context, *PushStaticAddressSweeplessSigsRequest) (*PushStaticAddressSweeplessSigsResponse, error) mustEmbedUnimplementedStaticAddressServerServer() } @@ -827,6 +887,18 @@ 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) ServerStaticAddressLoopIn(context.Context, *ServerStaticAddressLoopInRequest) (*ServerStaticAddressLoopInResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ServerStaticAddressLoopIn not implemented") +} +func (UnimplementedStaticAddressServerServer) PushStaticAddressHtlcSigs(context.Context, *PushStaticAddressHtlcSigsRequest) (*PushStaticAddressHtlcSigsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PushStaticAddressHtlcSigs not implemented") +} +func (UnimplementedStaticAddressServerServer) FetchSweeplessSweepTx(context.Context, *FetchSweeplessSweepTxRequest) (*FetchSweeplessSweepTxResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FetchSweeplessSweepTx not implemented") +} +func (UnimplementedStaticAddressServerServer) PushStaticAddressSweeplessSigs(context.Context, *PushStaticAddressSweeplessSigsRequest) (*PushStaticAddressSweeplessSigsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PushStaticAddressSweeplessSigs not implemented") +} func (UnimplementedStaticAddressServerServer) mustEmbedUnimplementedStaticAddressServerServer() {} // UnsafeStaticAddressServerServer may be embedded to opt out of forward compatibility for this service. @@ -876,6 +948,78 @@ func _StaticAddressServer_ServerWithdrawDeposits_Handler(srv interface{}, ctx co 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 { + return nil, err + } + if interceptor == nil { + return srv.(StaticAddressServerServer).ServerStaticAddressLoopIn(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.StaticAddressServer/ServerStaticAddressLoopIn", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StaticAddressServerServer).ServerStaticAddressLoopIn(ctx, req.(*ServerStaticAddressLoopInRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StaticAddressServer_PushStaticAddressHtlcSigs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PushStaticAddressHtlcSigsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StaticAddressServerServer).PushStaticAddressHtlcSigs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.StaticAddressServer/PushStaticAddressHtlcSigs", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StaticAddressServerServer).PushStaticAddressHtlcSigs(ctx, req.(*PushStaticAddressHtlcSigsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StaticAddressServer_FetchSweeplessSweepTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FetchSweeplessSweepTxRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StaticAddressServerServer).FetchSweeplessSweepTx(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.StaticAddressServer/FetchSweeplessSweepTx", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StaticAddressServerServer).FetchSweeplessSweepTx(ctx, req.(*FetchSweeplessSweepTxRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StaticAddressServer_PushStaticAddressSweeplessSigs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PushStaticAddressSweeplessSigsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StaticAddressServerServer).PushStaticAddressSweeplessSigs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/looprpc.StaticAddressServer/PushStaticAddressSweeplessSigs", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StaticAddressServerServer).PushStaticAddressSweeplessSigs(ctx, req.(*PushStaticAddressSweeplessSigsRequest)) + } + return interceptor(ctx, in, info, handler) +} + // StaticAddressServer_ServiceDesc is the grpc.ServiceDesc for StaticAddressServer service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -891,6 +1035,22 @@ var StaticAddressServer_ServiceDesc = grpc.ServiceDesc{ MethodName: "ServerWithdrawDeposits", Handler: _StaticAddressServer_ServerWithdrawDeposits_Handler, }, + { + MethodName: "ServerStaticAddressLoopIn", + Handler: _StaticAddressServer_ServerStaticAddressLoopIn_Handler, + }, + { + MethodName: "PushStaticAddressHtlcSigs", + Handler: _StaticAddressServer_PushStaticAddressHtlcSigs_Handler, + }, + { + MethodName: "FetchSweeplessSweepTx", + Handler: _StaticAddressServer_FetchSweeplessSweepTx_Handler, + }, + { + MethodName: "PushStaticAddressSweeplessSigs", + Handler: _StaticAddressServer_PushStaticAddressSweeplessSigs_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "server.proto", From 3c0c6f8901dd1451cbd0f6c8473153058f36aed5 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Tue, 30 Jul 2024 12:22:04 +0200 Subject: [PATCH 02/14] sqlc: loop-in tables and queries We add CRU procedures for static address swaps. --- .../000011_static_address_loop_in.down.sql | 2 + .../000011_static_address_loop_in.up.sql | 59 +++ loopdb/sqlc/models.go | 20 + loopdb/sqlc/querier.go | 8 + loopdb/sqlc/queries/static_address_loopin.sql | 95 +++++ loopdb/sqlc/static_address_loopin.sql.go | 349 ++++++++++++++++++ 6 files changed, 533 insertions(+) create mode 100644 loopdb/sqlc/migrations/000011_static_address_loop_in.down.sql create mode 100644 loopdb/sqlc/migrations/000011_static_address_loop_in.up.sql create mode 100644 loopdb/sqlc/queries/static_address_loopin.sql create mode 100644 loopdb/sqlc/static_address_loopin.sql.go diff --git a/loopdb/sqlc/migrations/000011_static_address_loop_in.down.sql b/loopdb/sqlc/migrations/000011_static_address_loop_in.down.sql new file mode 100644 index 000000000..fae889ca3 --- /dev/null +++ b/loopdb/sqlc/migrations/000011_static_address_loop_in.down.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS static_address_swaps; +DROP TABLE IF EXISTS static_address_swap_updates; diff --git a/loopdb/sqlc/migrations/000011_static_address_loop_in.up.sql b/loopdb/sqlc/migrations/000011_static_address_loop_in.up.sql new file mode 100644 index 000000000..dfff6d2d1 --- /dev/null +++ b/loopdb/sqlc/migrations/000011_static_address_loop_in.up.sql @@ -0,0 +1,59 @@ +-- static_address_swaps stores the static address loop-in specific data. +CREATE TABLE IF NOT EXISTS static_address_swaps ( + -- id is the auto incrementing primary key. + id INTEGER PRIMARY KEY, + + -- swap_hash of the swap is unique and is used to identify the swap. + swap_hash BLOB NOT NULL UNIQUE, + + -- swap_invoice is the invoice that needs to be paid by the server to + -- complete the loop-in swap. + swap_invoice TEXT NOT NULL, + + -- last_hop is an optional parameter that specifies the last hop to be + -- used for a loop in swap. + last_hop BLOB, + + -- payment_timeout_seconds is the time in seconds that the server has to + -- pay the invoice. + payment_timeout_seconds INTEGER NOT NULL, + + -- quoted_swap_fee_satoshis is the swap fee in sats that the server returned + -- in the swap quote. + quoted_swap_fee_satoshis BIGINT NOT NULL, + + -- deposit_outpoints is a concatenated list of outpoints that are used for + -- this swap. The list has the format txid1:idx;txid2:idx;... + deposit_outpoints TEXT NOT NULL, + + -- htlc_tx_fee_rate_sat_kw is the fee rate in sat/kw that is used for the + -- htlc transaction. + htlc_tx_fee_rate_sat_kw BIGINT NOT NULL, + + -- htlc_timeout_sweep_tx_hash contains the htlc timeout sweep tx id. + htlc_timeout_sweep_tx_id TEXT, + + -- htlc_timeout_sweep_address contains the address the htlc timeout sweep + -- transaction sends funds to. + htlc_timeout_sweep_address TEXT NOT NULL +); + +-- static_address_swap_updates contains all the updates to a loop-in swap. +CREATE TABLE IF NOT EXISTS static_address_swap_updates ( + -- id is the auto incrementing primary key. + id INTEGER PRIMARY KEY, + + -- deposit_id is the unique identifier for the deposit. + swap_hash BLOB NOT NULL REFERENCES static_address_swaps(swap_hash), + + -- update_state is the state of the loop-in at the time of the update. + -- Example states are InitHtlc, SignHtlcTx and others defined in + -- staticaddr/loopin/fsm.go. + update_state TEXT NOT NULL, + + -- update_timestamp is the timestamp of the update. + update_timestamp TIMESTAMP NOT NULL +); + +CREATE INDEX IF NOT EXISTS static_address_swap_hash_idx ON static_address_swap_updates(swap_hash); +CREATE INDEX IF NOT EXISTS static_address_update_state_idx ON static_address_swap_updates(update_state); diff --git a/loopdb/sqlc/models.go b/loopdb/sqlc/models.go index b36933489..a2929dccd 100644 --- a/loopdb/sqlc/models.go +++ b/loopdb/sqlc/models.go @@ -124,6 +124,26 @@ type StaticAddress struct { ProtocolVersion int32 } +type StaticAddressSwap struct { + ID int32 + SwapHash []byte + SwapInvoice string + LastHop []byte + PaymentTimeoutSeconds int32 + QuotedSwapFeeSatoshis int64 + DepositOutpoints string + HtlcTxFeeRateSatKw int64 + HtlcTimeoutSweepTxID sql.NullString + HtlcTimeoutSweepAddress string +} + +type StaticAddressSwapUpdate struct { + ID int32 + SwapHash []byte + UpdateState string + UpdateTimestamp time.Time +} + type Swap struct { ID int32 SwapHash []byte diff --git a/loopdb/sqlc/querier.go b/loopdb/sqlc/querier.go index a942562c2..648eb3b50 100644 --- a/loopdb/sqlc/querier.go +++ b/loopdb/sqlc/querier.go @@ -6,6 +6,7 @@ package sqlc import ( "context" + "database/sql" ) type Querier interface { @@ -26,6 +27,7 @@ type Querier interface { GetLastUpdateID(ctx context.Context, swapHash []byte) (int32, error) GetLatestDepositUpdate(ctx context.Context, depositID []byte) (DepositUpdate, error) GetLoopInSwap(ctx context.Context, swapHash []byte) (GetLoopInSwapRow, error) + GetLoopInSwapUpdates(ctx context.Context, swapHash []byte) ([]StaticAddressSwapUpdate, error) GetLoopInSwaps(ctx context.Context) ([]GetLoopInSwapsRow, error) GetLoopOutSwap(ctx context.Context, swapHash []byte) (GetLoopOutSwapRow, error) GetLoopOutSwaps(ctx context.Context) ([]GetLoopOutSwapsRow, error) @@ -35,6 +37,8 @@ type Querier interface { GetReservationUpdates(ctx context.Context, reservationID []byte) ([]ReservationUpdate, error) GetReservations(ctx context.Context) ([]Reservation, error) GetStaticAddress(ctx context.Context, pkscript []byte) (StaticAddress, error) + GetStaticAddressLoopInSwap(ctx context.Context, swapHash []byte) (GetStaticAddressLoopInSwapRow, error) + GetStaticAddressLoopInSwapsByStates(ctx context.Context, dollar_1 sql.NullString) ([]GetStaticAddressLoopInSwapsByStatesRow, error) GetSwapUpdates(ctx context.Context, swapHash []byte) ([]SwapUpdate, error) GetSweepStatus(ctx context.Context, swapHash []byte) (bool, error) GetUnconfirmedBatches(ctx context.Context) ([]SweepBatch, error) @@ -47,13 +51,17 @@ type Querier interface { InsertLoopOut(ctx context.Context, arg InsertLoopOutParams) error InsertMigration(ctx context.Context, arg InsertMigrationParams) error InsertReservationUpdate(ctx context.Context, arg InsertReservationUpdateParams) error + InsertStaticAddressLoopIn(ctx context.Context, arg InsertStaticAddressLoopInParams) error + InsertStaticAddressMetaUpdate(ctx context.Context, arg InsertStaticAddressMetaUpdateParams) error InsertSwap(ctx context.Context, arg InsertSwapParams) error InsertSwapUpdate(ctx context.Context, arg InsertSwapUpdateParams) error + IsStored(ctx context.Context, swapHash []byte) (bool, error) OverrideSwapCosts(ctx context.Context, arg OverrideSwapCostsParams) error UpdateBatch(ctx context.Context, arg UpdateBatchParams) error UpdateDeposit(ctx context.Context, arg UpdateDepositParams) error UpdateInstantOut(ctx context.Context, arg UpdateInstantOutParams) error UpdateReservation(ctx context.Context, arg UpdateReservationParams) error + UpdateStaticAddressLoopIn(ctx context.Context, arg UpdateStaticAddressLoopInParams) error UpsertLiquidityParams(ctx context.Context, params []byte) error UpsertSweep(ctx context.Context, arg UpsertSweepParams) error } diff --git a/loopdb/sqlc/queries/static_address_loopin.sql b/loopdb/sqlc/queries/static_address_loopin.sql new file mode 100644 index 000000000..ea4344b75 --- /dev/null +++ b/loopdb/sqlc/queries/static_address_loopin.sql @@ -0,0 +1,95 @@ +-- name: InsertStaticAddressLoopIn :exec +INSERT INTO static_address_swaps ( + swap_hash, + swap_invoice, + last_hop, + payment_timeout_seconds, + quoted_swap_fee_satoshis, + deposit_outpoints, + htlc_tx_fee_rate_sat_kw, + htlc_timeout_sweep_tx_id, + htlc_timeout_sweep_address +) VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7, + $8, + $9 +); + +-- name: UpdateStaticAddressLoopIn :exec +UPDATE static_address_swaps +SET + htlc_tx_fee_rate_sat_kw = $2, + htlc_timeout_sweep_tx_id = $3 +WHERE + swap_hash = $1; + +-- name: InsertStaticAddressMetaUpdate :exec +INSERT INTO static_address_swap_updates ( + swap_hash, + update_state, + update_timestamp +) VALUES ( + $1, + $2, + $3 + ); + +-- name: GetStaticAddressLoopInSwap :one +SELECT + swaps.*, + static_address_swaps.*, + htlc_keys.* +FROM + swaps + JOIN + static_address_swaps ON swaps.swap_hash = static_address_swaps.swap_hash + JOIN + htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash +WHERE + swaps.swap_hash = $1; + +-- name: GetStaticAddressLoopInSwapsByStates :many +SELECT + swaps.*, + static_address_swaps.*, + htlc_keys.* +FROM + swaps + JOIN + static_address_swaps ON swaps.swap_hash = static_address_swaps.swap_hash + JOIN + htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash + JOIN + static_address_swap_updates u ON swaps.swap_hash = u.swap_hash + -- This subquery ensures that we are checking only the latest update for + -- each swap_hash. + AND u.update_timestamp = ( + SELECT MAX(update_timestamp) + FROM static_address_swap_updates + WHERE swap_hash = u.swap_hash + ) +WHERE + (',' || $1 || ',') LIKE ('%,' || u.update_state || ',%') +ORDER BY + swaps.id; + +-- name: GetLoopInSwapUpdates :many +SELECT + static_address_swap_updates.* +FROM + static_address_swap_updates +WHERE + swap_hash = $1; + +-- name: IsStored :one +SELECT EXISTS ( + SELECT 1 + FROM static_address_swaps + WHERE swap_hash = $1 +); diff --git a/loopdb/sqlc/static_address_loopin.sql.go b/loopdb/sqlc/static_address_loopin.sql.go new file mode 100644 index 000000000..22ee6438a --- /dev/null +++ b/loopdb/sqlc/static_address_loopin.sql.go @@ -0,0 +1,349 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 +// source: static_address_loopin.sql + +package sqlc + +import ( + "context" + "database/sql" + "time" +) + +const getLoopInSwapUpdates = `-- name: GetLoopInSwapUpdates :many +SELECT + static_address_swap_updates.id, static_address_swap_updates.swap_hash, static_address_swap_updates.update_state, static_address_swap_updates.update_timestamp +FROM + static_address_swap_updates +WHERE + swap_hash = $1 +` + +func (q *Queries) GetLoopInSwapUpdates(ctx context.Context, swapHash []byte) ([]StaticAddressSwapUpdate, error) { + rows, err := q.db.QueryContext(ctx, getLoopInSwapUpdates, swapHash) + if err != nil { + return nil, err + } + defer rows.Close() + var items []StaticAddressSwapUpdate + for rows.Next() { + var i StaticAddressSwapUpdate + if err := rows.Scan( + &i.ID, + &i.SwapHash, + &i.UpdateState, + &i.UpdateTimestamp, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getStaticAddressLoopInSwap = `-- name: GetStaticAddressLoopInSwap :one +SELECT + swaps.id, swaps.swap_hash, swaps.preimage, swaps.initiation_time, swaps.amount_requested, swaps.cltv_expiry, swaps.max_miner_fee, swaps.max_swap_fee, swaps.initiation_height, swaps.protocol_version, swaps.label, + static_address_swaps.id, static_address_swaps.swap_hash, static_address_swaps.swap_invoice, static_address_swaps.last_hop, static_address_swaps.payment_timeout_seconds, static_address_swaps.quoted_swap_fee_satoshis, static_address_swaps.deposit_outpoints, static_address_swaps.htlc_tx_fee_rate_sat_kw, static_address_swaps.htlc_timeout_sweep_tx_id, static_address_swaps.htlc_timeout_sweep_address, + htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index +FROM + swaps + JOIN + static_address_swaps ON swaps.swap_hash = static_address_swaps.swap_hash + JOIN + htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash +WHERE + swaps.swap_hash = $1 +` + +type GetStaticAddressLoopInSwapRow struct { + ID int32 + SwapHash []byte + Preimage []byte + InitiationTime time.Time + AmountRequested int64 + CltvExpiry int32 + MaxMinerFee int64 + MaxSwapFee int64 + InitiationHeight int32 + ProtocolVersion int32 + Label string + ID_2 int32 + SwapHash_2 []byte + SwapInvoice string + LastHop []byte + PaymentTimeoutSeconds int32 + QuotedSwapFeeSatoshis int64 + DepositOutpoints string + HtlcTxFeeRateSatKw int64 + HtlcTimeoutSweepTxID sql.NullString + HtlcTimeoutSweepAddress string + SwapHash_3 []byte + SenderScriptPubkey []byte + ReceiverScriptPubkey []byte + SenderInternalPubkey []byte + ReceiverInternalPubkey []byte + ClientKeyFamily int32 + ClientKeyIndex int32 +} + +func (q *Queries) GetStaticAddressLoopInSwap(ctx context.Context, swapHash []byte) (GetStaticAddressLoopInSwapRow, error) { + row := q.db.QueryRowContext(ctx, getStaticAddressLoopInSwap, swapHash) + var i GetStaticAddressLoopInSwapRow + err := row.Scan( + &i.ID, + &i.SwapHash, + &i.Preimage, + &i.InitiationTime, + &i.AmountRequested, + &i.CltvExpiry, + &i.MaxMinerFee, + &i.MaxSwapFee, + &i.InitiationHeight, + &i.ProtocolVersion, + &i.Label, + &i.ID_2, + &i.SwapHash_2, + &i.SwapInvoice, + &i.LastHop, + &i.PaymentTimeoutSeconds, + &i.QuotedSwapFeeSatoshis, + &i.DepositOutpoints, + &i.HtlcTxFeeRateSatKw, + &i.HtlcTimeoutSweepTxID, + &i.HtlcTimeoutSweepAddress, + &i.SwapHash_3, + &i.SenderScriptPubkey, + &i.ReceiverScriptPubkey, + &i.SenderInternalPubkey, + &i.ReceiverInternalPubkey, + &i.ClientKeyFamily, + &i.ClientKeyIndex, + ) + return i, err +} + +const getStaticAddressLoopInSwapsByStates = `-- name: GetStaticAddressLoopInSwapsByStates :many +SELECT + swaps.id, swaps.swap_hash, swaps.preimage, swaps.initiation_time, swaps.amount_requested, swaps.cltv_expiry, swaps.max_miner_fee, swaps.max_swap_fee, swaps.initiation_height, swaps.protocol_version, swaps.label, + static_address_swaps.id, static_address_swaps.swap_hash, static_address_swaps.swap_invoice, static_address_swaps.last_hop, static_address_swaps.payment_timeout_seconds, static_address_swaps.quoted_swap_fee_satoshis, static_address_swaps.deposit_outpoints, static_address_swaps.htlc_tx_fee_rate_sat_kw, static_address_swaps.htlc_timeout_sweep_tx_id, static_address_swaps.htlc_timeout_sweep_address, + htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index +FROM + swaps + JOIN + static_address_swaps ON swaps.swap_hash = static_address_swaps.swap_hash + JOIN + htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash + JOIN + static_address_swap_updates u ON swaps.swap_hash = u.swap_hash + -- This subquery ensures that we are checking only the latest update for + -- each swap_hash. + AND u.update_timestamp = ( + SELECT MAX(update_timestamp) + FROM static_address_swap_updates + WHERE swap_hash = u.swap_hash + ) +WHERE + (',' || $1 || ',') LIKE ('%,' || u.update_state || ',%') +ORDER BY + swaps.id +` + +type GetStaticAddressLoopInSwapsByStatesRow struct { + ID int32 + SwapHash []byte + Preimage []byte + InitiationTime time.Time + AmountRequested int64 + CltvExpiry int32 + MaxMinerFee int64 + MaxSwapFee int64 + InitiationHeight int32 + ProtocolVersion int32 + Label string + ID_2 int32 + SwapHash_2 []byte + SwapInvoice string + LastHop []byte + PaymentTimeoutSeconds int32 + QuotedSwapFeeSatoshis int64 + DepositOutpoints string + HtlcTxFeeRateSatKw int64 + HtlcTimeoutSweepTxID sql.NullString + HtlcTimeoutSweepAddress string + SwapHash_3 []byte + SenderScriptPubkey []byte + ReceiverScriptPubkey []byte + SenderInternalPubkey []byte + ReceiverInternalPubkey []byte + ClientKeyFamily int32 + ClientKeyIndex int32 +} + +func (q *Queries) GetStaticAddressLoopInSwapsByStates(ctx context.Context, dollar_1 sql.NullString) ([]GetStaticAddressLoopInSwapsByStatesRow, error) { + rows, err := q.db.QueryContext(ctx, getStaticAddressLoopInSwapsByStates, dollar_1) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetStaticAddressLoopInSwapsByStatesRow + for rows.Next() { + var i GetStaticAddressLoopInSwapsByStatesRow + if err := rows.Scan( + &i.ID, + &i.SwapHash, + &i.Preimage, + &i.InitiationTime, + &i.AmountRequested, + &i.CltvExpiry, + &i.MaxMinerFee, + &i.MaxSwapFee, + &i.InitiationHeight, + &i.ProtocolVersion, + &i.Label, + &i.ID_2, + &i.SwapHash_2, + &i.SwapInvoice, + &i.LastHop, + &i.PaymentTimeoutSeconds, + &i.QuotedSwapFeeSatoshis, + &i.DepositOutpoints, + &i.HtlcTxFeeRateSatKw, + &i.HtlcTimeoutSweepTxID, + &i.HtlcTimeoutSweepAddress, + &i.SwapHash_3, + &i.SenderScriptPubkey, + &i.ReceiverScriptPubkey, + &i.SenderInternalPubkey, + &i.ReceiverInternalPubkey, + &i.ClientKeyFamily, + &i.ClientKeyIndex, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const insertStaticAddressLoopIn = `-- name: InsertStaticAddressLoopIn :exec +INSERT INTO static_address_swaps ( + swap_hash, + swap_invoice, + last_hop, + payment_timeout_seconds, + quoted_swap_fee_satoshis, + deposit_outpoints, + htlc_tx_fee_rate_sat_kw, + htlc_timeout_sweep_tx_id, + htlc_timeout_sweep_address +) VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7, + $8, + $9 +) +` + +type InsertStaticAddressLoopInParams struct { + SwapHash []byte + SwapInvoice string + LastHop []byte + PaymentTimeoutSeconds int32 + QuotedSwapFeeSatoshis int64 + DepositOutpoints string + HtlcTxFeeRateSatKw int64 + HtlcTimeoutSweepTxID sql.NullString + HtlcTimeoutSweepAddress string +} + +func (q *Queries) InsertStaticAddressLoopIn(ctx context.Context, arg InsertStaticAddressLoopInParams) error { + _, err := q.db.ExecContext(ctx, insertStaticAddressLoopIn, + arg.SwapHash, + arg.SwapInvoice, + arg.LastHop, + arg.PaymentTimeoutSeconds, + arg.QuotedSwapFeeSatoshis, + arg.DepositOutpoints, + arg.HtlcTxFeeRateSatKw, + arg.HtlcTimeoutSweepTxID, + arg.HtlcTimeoutSweepAddress, + ) + return err +} + +const insertStaticAddressMetaUpdate = `-- name: InsertStaticAddressMetaUpdate :exec +INSERT INTO static_address_swap_updates ( + swap_hash, + update_state, + update_timestamp +) VALUES ( + $1, + $2, + $3 + ) +` + +type InsertStaticAddressMetaUpdateParams struct { + SwapHash []byte + UpdateState string + UpdateTimestamp time.Time +} + +func (q *Queries) InsertStaticAddressMetaUpdate(ctx context.Context, arg InsertStaticAddressMetaUpdateParams) error { + _, err := q.db.ExecContext(ctx, insertStaticAddressMetaUpdate, arg.SwapHash, arg.UpdateState, arg.UpdateTimestamp) + return err +} + +const isStored = `-- name: IsStored :one +SELECT EXISTS ( + SELECT 1 + FROM static_address_swaps + WHERE swap_hash = $1 +) +` + +func (q *Queries) IsStored(ctx context.Context, swapHash []byte) (bool, error) { + row := q.db.QueryRowContext(ctx, isStored, swapHash) + var exists bool + err := row.Scan(&exists) + return exists, err +} + +const updateStaticAddressLoopIn = `-- name: UpdateStaticAddressLoopIn :exec +UPDATE static_address_swaps +SET + htlc_tx_fee_rate_sat_kw = $2, + htlc_timeout_sweep_tx_id = $3 +WHERE + swap_hash = $1 +` + +type UpdateStaticAddressLoopInParams struct { + SwapHash []byte + HtlcTxFeeRateSatKw int64 + HtlcTimeoutSweepTxID sql.NullString +} + +func (q *Queries) UpdateStaticAddressLoopIn(ctx context.Context, arg UpdateStaticAddressLoopInParams) error { + _, err := q.db.ExecContext(ctx, updateStaticAddressLoopIn, arg.SwapHash, arg.HtlcTxFeeRateSatKw, arg.HtlcTimeoutSweepTxID) + return err +} From 21cd9340776addcf2b10576af570c21278406198 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Tue, 30 Jul 2024 12:23:35 +0200 Subject: [PATCH 03/14] log: static address loop-in logging --- staticaddr/log.go | 2 ++ staticaddr/loopin/log.go | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 staticaddr/loopin/log.go diff --git a/staticaddr/log.go b/staticaddr/log.go index fc5eb0108..cfd9e0b9d 100644 --- a/staticaddr/log.go +++ b/staticaddr/log.go @@ -4,6 +4,7 @@ import ( "github.com/btcsuite/btclog" "github.com/lightninglabs/loop/staticaddr/address" "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/loopin" "github.com/lightninglabs/loop/staticaddr/withdraw" "github.com/lightningnetwork/lnd/build" ) @@ -27,4 +28,5 @@ func UseLogger(logger btclog.Logger) { address.UseLogger(log) deposit.UseLogger(log) withdraw.UseLogger(log) + loopin.UseLogger(log) } diff --git a/staticaddr/loopin/log.go b/staticaddr/loopin/log.go new file mode 100644 index 000000000..99816e9cb --- /dev/null +++ b/staticaddr/loopin/log.go @@ -0,0 +1,24 @@ +package loopin + +import ( + "github.com/btcsuite/btclog" + "github.com/lightningnetwork/lnd/build" +) + +// Subsystem defines the sub system name of this package. +const Subsystem = "SADDR" + +// log is a logger that is initialized with no output filters. This means the +// package will not perform any logging by default until the caller requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + UseLogger(build.NewSubLogger(Subsystem, nil)) +} + +// UseLogger uses a specified Logger to output package logging info. This should +// be used in preference to SetLogWriter if the caller is also using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} From dba7796c041010aea4e96156442ee9c94f5fcda9 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Tue, 30 Jul 2024 15:37:37 +0200 Subject: [PATCH 04/14] staticaddr: enable deposits for swaps The static address state machine adds states to reflect the state of a deposit during a loop-in swap. --- loopd/swapclient_server.go | 6 +- staticaddr/deposit/actions.go | 14 +- staticaddr/deposit/deposit.go | 19 +-- staticaddr/deposit/fsm.go | 203 ++++++++++++++++++++++---- staticaddr/deposit/manager.go | 249 ++++++++++++++++++++------------ staticaddr/deposit/sql_store.go | 7 +- 6 files changed, 347 insertions(+), 151 deletions(-) diff --git a/loopd/swapclient_server.go b/loopd/swapclient_server.go index ab3252b9c..e957d8a82 100644 --- a/loopd/swapclient_server.go +++ b/loopd/swapclient_server.go @@ -1466,7 +1466,7 @@ func (s *swapClientServer) GetStaticAddressSummary(ctx context.Context, "outpoints") } - allDeposits, err := s.depositManager.GetAllDeposits() + allDeposits, err := s.depositManager.GetAllDeposits(ctx) if err != nil { return nil, err } @@ -1604,7 +1604,7 @@ func toClientState(state fsm.StateType) looprpc.DepositState { case deposit.Withdrawn: return looprpc.DepositState_WITHDRAWN - case deposit.PublishExpiredDeposit: + case deposit.PublishExpirySweep: return looprpc.DepositState_PUBLISH_EXPIRED case deposit.WaitForExpirySweep: @@ -1630,7 +1630,7 @@ func toServerState(state looprpc.DepositState) fsm.StateType { return deposit.Withdrawn case looprpc.DepositState_PUBLISH_EXPIRED: - return deposit.PublishExpiredDeposit + return deposit.PublishExpirySweep case looprpc.DepositState_WAIT_FOR_EXPIRY_SWEEP: return deposit.WaitForExpirySweep diff --git a/staticaddr/deposit/actions.go b/staticaddr/deposit/actions.go index 835a96076..7f58fa4ae 100644 --- a/staticaddr/deposit/actions.go +++ b/staticaddr/deposit/actions.go @@ -14,7 +14,7 @@ import ( ) const ( - defaultConfTarget = 3 + DefaultConfTarget = 3 ) // PublishDepositExpirySweepAction creates and publishes the timeout transaction @@ -39,11 +39,11 @@ func (f *FSM) PublishDepositExpirySweepAction(ctx context.Context, // Estimate the fee rate of an expiry spend transaction. feeRateEstimator, err := f.cfg.WalletKit.EstimateFeeRate( - ctx, defaultConfTarget, + ctx, DefaultConfTarget, ) if err != nil { return f.HandleError(fmt.Errorf("timeout sweep fee "+ - "estimation failed: %v", err)) + "estimation failed: %w", err)) } weight := script.ExpirySpendWeight() @@ -116,7 +116,7 @@ func (f *FSM) WaitForExpirySweepAction(ctx context.Context, _ fsm.EventContext) fsm.EventType { spendChan, errSpendChan, err := f.cfg.ChainNotifier.RegisterConfirmationsNtfn( //nolint:lll - ctx, nil, f.deposit.TimeOutSweepPkScript, defaultConfTarget, + ctx, nil, f.deposit.TimeOutSweepPkScript, DefaultConfTarget, int32(f.deposit.ConfirmationHeight), ) if err != nil { @@ -124,7 +124,7 @@ func (f *FSM) WaitForExpirySweepAction(ctx context.Context, } select { - case err := <-errSpendChan: + case err = <-errSpendChan: log.Debugf("error while sweeping expired deposit: %v", err) return fsm.OnError @@ -155,9 +155,9 @@ func (f *FSM) SweptExpiredDepositAction(ctx context.Context, return fsm.NoOp } -// WithdrawnDepositAction is the final action after a withdrawal. It signals to +// FinalizeDepositAction is the final action after a withdrawal. It signals to // the manager that the deposit has been swept and the FSM can be removed. -func (f *FSM) WithdrawnDepositAction(ctx context.Context, +func (f *FSM) FinalizeDepositAction(ctx context.Context, _ fsm.EventContext) fsm.EventType { select { diff --git a/staticaddr/deposit/deposit.go b/staticaddr/deposit/deposit.go index 3e23f9a6f..a360e7679 100644 --- a/staticaddr/deposit/deposit.go +++ b/staticaddr/deposit/deposit.go @@ -59,20 +59,13 @@ type Deposit struct { sync.Mutex } -// IsInPendingState returns true if the deposit is pending. -func (d *Deposit) IsInPendingState() bool { - d.Lock() - defer d.Unlock() - - return !d.IsInFinalState() -} - // IsInFinalState returns true if the deposit is final. func (d *Deposit) IsInFinalState() bool { d.Lock() defer d.Unlock() - return d.state == Expired || d.state == Withdrawn || d.state == Failed + return d.state == Expired || d.state == Withdrawn || + d.state == LoopedIn || d.state == HtlcTimeoutSwept } func (d *Deposit) IsExpired(currentHeight, expiry uint32) bool { @@ -96,6 +89,10 @@ func (d *Deposit) SetState(state fsm.StateType) { d.state = state } +func (d *Deposit) SetStateNoLock(state fsm.StateType) { + d.state = state +} + func (d *Deposit) IsInState(state fsm.StateType) bool { d.Lock() defer d.Unlock() @@ -103,6 +100,10 @@ func (d *Deposit) IsInState(state fsm.StateType) bool { return d.state == state } +func (d *Deposit) IsInStateNoLock(state fsm.StateType) bool { + return d.state == state +} + // GetRandomDepositID generates a random deposit ID. func GetRandomDepositID() (ID, error) { var id ID diff --git a/staticaddr/deposit/fsm.go b/staticaddr/deposit/fsm.go index 347ad5dff..e33a5d41d 100644 --- a/staticaddr/deposit/fsm.go +++ b/staticaddr/deposit/fsm.go @@ -23,34 +23,108 @@ const ( var ( ErrProtocolVersionNotSupported = errors.New("protocol version not " + "supported") + + // Withdrawal and loop-in transitions lock their respective deposits + // themselves. We need to make sure that we don't lock the deposit + // twice. For the events below we expect the deposits already locked. + lockedEvents = map[fsm.EventType]struct{}{ + OnLoopInInitiated: {}, + OnSweepingHtlcTimeout: {}, + OnHtlcTimeoutSwept: {}, + OnLoopedIn: {}, + fsm.OnError: {}, + OnWithdrawInitiated: {}, + OnWithdrawn: {}, + } ) // States. var ( + // Deposited signals that funds at a static address have reached the + // confirmation height. Deposited = fsm.StateType("Deposited") + // Withdrawing signals that the withdrawal transaction has been + // broadcast, awaiting sufficient confirmations. Withdrawing = fsm.StateType("Withdrawing") + // Withdrawn signals that the withdrawal transaction has been confirmed. Withdrawn = fsm.StateType("Withdrawn") - PublishExpiredDeposit = fsm.StateType("PublishExpiredDeposit") + // LoopingIn signals that the deposit is locked for a loop in swap. + LoopingIn = fsm.StateType("LoopingIn") + + // LoopedIn signals that the loop in swap has been successfully + // completed. It implies that we signed the sweepless sweep tx for the + // server. + LoopedIn = fsm.StateType("LoopedIn") + + // SweepHtlcTimeout signals that the htlc timeout path is in the + // process of being swept. + SweepHtlcTimeout = fsm.StateType("SweepHtlcTimeout") + + // HtlcTimeoutSwept signals that the htlc timeout path has been swept. + HtlcTimeoutSwept = fsm.StateType("HtlcTimeoutSwept") + + // PublishExpirySweep signals that the deposit has expired, and we are + // in the process of publishing the expiry sweep transaction. + PublishExpirySweep = fsm.StateType("PublishExpirySweep") + // WaitForExpirySweep signals that the expiry sweep transaction has been + // published, and we are waiting for it to be confirmed. WaitForExpirySweep = fsm.StateType("WaitForExpirySweep") + // Expired signals that the deposit has expired and the expiry sweep + // transaction has been confirmed sufficiently. Expired = fsm.StateType("Expired") - - Failed = fsm.StateType("Failed") ) // Events. var ( - OnStart = fsm.EventType("OnStart") + // OnStart is sent to the fsm once the deposit outpoint has been + // sufficiently confirmed. It transitions the fsm into the Deposited + // state from where we can trigger a withdrawal, a loopin or an expiry. + OnStart = fsm.EventType("OnStart") + + // OnWithdrawInitiated is sent to the fsm when a withdrawal has been + // initiated. OnWithdrawInitiated = fsm.EventType("OnWithdrawInitiated") - OnWithdrawn = fsm.EventType("OnWithdrawn") - OnExpiry = fsm.EventType("OnExpiry") - OnExpiryPublished = fsm.EventType("OnExpiryPublished") - OnExpirySwept = fsm.EventType("OnExpirySwept") - OnRecover = fsm.EventType("OnRecover") + + // OnWithdrawn is sent to the fsm when a withdrawal has been confirmed. + OnWithdrawn = fsm.EventType("OnWithdrawn") + + // OnLoopInInitiated is sent to the fsm when a loop in has been + // initiated. + OnLoopInInitiated = fsm.EventType("OnLoopInInitiated") + + // OnSweepingHtlcTimeout is sent to the fsm when the htlc timeout path + // is being swept. This indicates that the server didn't pay the swap + // invoice, but the htlc tx was published, from we which we need to + // sweep the htlc timeout path. + OnSweepingHtlcTimeout = fsm.EventType("OnSweepingHtlcTimeout") + + // OnHtlcTimeoutSwept is sent to the fsm when the htlc timeout path has + // been swept. + OnHtlcTimeoutSwept = fsm.EventType("OnHtlcTimeoutSwept") + + // OnLoopedIn is sent to the fsm when the user intents to use the + // deposit for a loop in swap. + OnLoopedIn = fsm.EventType("OnLoopedIn") + + // OnExpiry is sent to the fsm when the deposit has expired. + OnExpiry = fsm.EventType("OnExpiry") + + // OnExpiryPublished is sent to the fsm when the expiry sweep tx has + // been published. + OnExpiryPublished = fsm.EventType("OnExpiryPublished") + + // OnExpirySwept is sent to the fsm when the expiry sweep tx has been + // confirmed. + OnExpirySwept = fsm.EventType("OnExpirySwept") + + // OnRecover is sent to the fsm when it should recover from client + // restart. + OnRecover = fsm.EventType("OnRecover") ) // FSM is the state machine that handles the instant out. @@ -79,12 +153,12 @@ func NewFSM(ctx context.Context, deposit *Deposit, cfg *ManagerConfig, params, err := cfg.AddressManager.GetStaticAddressParameters(ctx) if err != nil { return nil, fmt.Errorf("unable to get static address "+ - "parameters: %v", err) + "parameters: %w", err) } address, err := cfg.AddressManager.GetStaticAddress(ctx) if err != nil { - return nil, fmt.Errorf("unable to get static address: %v", err) + return nil, fmt.Errorf("unable to get static address: %w", err) } depoFsm := &FSM{ @@ -150,7 +224,7 @@ func (f *FSM) handleBlockNotification(ctx context.Context, err := f.SendEvent(ctx, OnExpiry, nil) if err != nil { log.Debugf("error sending OnExpiry "+ - "event: %v", err) + "event: %w", err) } }() } @@ -168,15 +242,22 @@ func (f *FSM) DepositStatesV0() fsm.States { }, Deposited: fsm.State{ Transitions: fsm.Transitions{ - OnExpiry: PublishExpiredDeposit, + OnExpiry: PublishExpirySweep, OnWithdrawInitiated: Withdrawing, - OnRecover: Deposited, + OnLoopInInitiated: LoopingIn, + // We encounter OnSweepingHtlcTimeout if the + // server published the htlc tx without paying + // us. We then need to monitor for the timeout + // path to open up to sweep it. + OnSweepingHtlcTimeout: SweepHtlcTimeout, + OnRecover: Deposited, + fsm.OnError: Deposited, }, Action: fsm.NoOpAction, }, - PublishExpiredDeposit: fsm.State{ + PublishExpirySweep: fsm.State{ Transitions: fsm.Transitions{ - OnRecover: PublishExpiredDeposit, + OnRecover: PublishExpirySweep, OnExpiryPublished: WaitForExpirySweep, // If the timeout sweep failed we go back to // Deposited, hoping that another timeout sweep @@ -190,7 +271,7 @@ func (f *FSM) DepositStatesV0() fsm.States { Transitions: fsm.Transitions{ OnExpirySwept: Expired, // Upon recovery, we republish the sweep tx. - OnRecover: PublishExpiredDeposit, + OnRecover: PublishExpirySweep, // If the timeout sweep failed we go back to // Deposited, hoping that another timeout sweep // attempt will be successful. Alternatively, @@ -229,18 +310,50 @@ func (f *FSM) DepositStatesV0() fsm.States { }, Action: fsm.NoOpAction, }, - Withdrawn: fsm.State{ + LoopingIn: fsm.State{ + Transitions: fsm.Transitions{ + // This event is triggered when the loop in + // payment has been received. We consider the + // swap to be completed and transition to a + // final state. + OnLoopedIn: LoopedIn, + + // If the deposit expires while the loop in is + // still pending, we publish the expiry sweep. + OnExpiry: PublishExpirySweep, + + OnLoopInInitiated: LoopingIn, + + OnRecover: LoopingIn, + fsm.OnError: Deposited, + }, + Action: fsm.NoOpAction, + }, + LoopedIn: fsm.State{ Transitions: fsm.Transitions{ OnExpiry: Expired, }, - Action: f.WithdrawnDepositAction, + Action: f.FinalizeDepositAction, }, - Failed: fsm.State{ + SweepHtlcTimeout: fsm.State{ Transitions: fsm.Transitions{ - OnExpiry: Failed, + OnHtlcTimeoutSwept: HtlcTimeoutSwept, + OnRecover: SweepHtlcTimeout, }, Action: fsm.NoOpAction, }, + HtlcTimeoutSwept: fsm.State{ + Transitions: fsm.Transitions{ + OnExpiry: HtlcTimeoutSwept, + }, + Action: f.FinalizeDepositAction, + }, + Withdrawn: fsm.State{ + Transitions: fsm.Transitions{ + OnExpiry: Expired, + }, + Action: f.FinalizeDepositAction, + }, } } @@ -258,23 +371,51 @@ func (f *FSM) updateDeposit(ctx context.Context, notification.Event, ) - f.deposit.SetState(notification.NextState) - - // Don't update the deposit if we are in an initial state or if we - // are transitioning from an initial state to a failed state. - d := f.deposit - if d.IsInState(fsm.EmptyState) || d.IsInState(Deposited) || - (notification.PreviousState == Deposited && d.IsInState( - Failed, - )) { + type checkStateFunc func(state fsm.StateType) bool + type setStateFunc func(state fsm.StateType) + checkFunc := checkStateFunc(f.deposit.IsInState) + setFunc := setStateFunc(f.deposit.SetState) + if _, ok := lockedEvents[notification.Event]; ok { + checkFunc = f.deposit.IsInStateNoLock + setFunc = f.deposit.SetStateNoLock + } + setFunc(notification.NextState) + if isUpdateSkipped(notification, checkFunc) { return } err := f.cfg.Store.UpdateDeposit(ctx, f.deposit) if err != nil { - f.Errorf("unable to update deposit: %v", err) + f.Errorf("unable to update deposit: %w", err) + } +} + +// isUpdateSkipped returns true if the deposit should not be updated for the given +// notification. +func isUpdateSkipped(notification fsm.Notification, + checkStateFunc func(stateType fsm.StateType) bool) bool { + + prevState := notification.PreviousState + + // Skip if we are in the empty state because no deposit has been + // persisted yet. + if checkStateFunc(fsm.EmptyState) { + return true } + + // If we transitioned from the empty state to Deposited there's still no + // deposit persisted, so we don't need to update it. + if prevState == fsm.EmptyState && checkStateFunc(Deposited) { + return true + } + + // We don't update in self-loops, e.g. in the case of recovery. + if checkStateFunc(prevState) { + return true + } + + return false } // Infof logs an info message with the deposit outpoint. diff --git a/staticaddr/deposit/manager.go b/staticaddr/deposit/manager.go index 329ca0f05..64b7116fe 100644 --- a/staticaddr/deposit/manager.go +++ b/staticaddr/deposit/manager.go @@ -34,7 +34,7 @@ const ( // DefaultTransitionTimeout is the default timeout for transitions in // the deposit state machine. - DefaultTransitionTimeout = 1 * time.Minute + DefaultTransitionTimeout = 5 * time.Second ) // ManagerConfig holds the configuration for the address manager. @@ -74,9 +74,8 @@ type ManagerConfig struct { type Manager struct { cfg *ManagerConfig - runCtx context.Context - - sync.Mutex + // mu guards access to activeDeposits map. + mu sync.Mutex // initChan signals the daemon that the address manager has completed // its initialization. @@ -88,9 +87,6 @@ type Manager struct { // initiationHeight stores the currently best known block height. initiationHeight uint32 - // currentHeight stores the currently best known block height. - currentHeight uint32 - // deposits contains all the deposits that have ever been made to the // static address. This field is used to store and recover deposits. It // also serves as basis for reconciliation of newly detected deposits by @@ -116,25 +112,21 @@ func NewManager(cfg *ManagerConfig) *Manager { // Run runs the address manager. func (m *Manager) Run(ctx context.Context, currentHeight uint32) error { - m.runCtx = ctx + m.initiationHeight = currentHeight - m.Lock() - m.currentHeight, m.initiationHeight = currentHeight, currentHeight - m.Unlock() - - newBlockChan, newBlockErrChan, err := m.cfg.ChainNotifier.RegisterBlockEpochNtfn(m.runCtx) //nolint:lll + newBlockChan, newBlockErrChan, err := m.cfg.ChainNotifier.RegisterBlockEpochNtfn(ctx) //nolint:lll if err != nil { return err } // Recover previous deposits and static address parameters from the DB. - err = m.recover(m.runCtx) + err = m.recoverDeposits(ctx) if err != nil { return err } // Start the deposit notifier. - m.pollDeposits(m.runCtx) + m.pollDeposits(ctx) // Communicate to the caller that the address manager has completed its // initialization. @@ -143,37 +135,33 @@ func (m *Manager) Run(ctx context.Context, currentHeight uint32) error { for { select { case height := <-newBlockChan: - m.Lock() - m.currentHeight = uint32(height) - m.Unlock() - // Inform all active deposits about a new block arrival. for _, fsm := range m.activeDeposits { select { case fsm.blockNtfnChan <- uint32(height): - case <-m.runCtx.Done(): - return m.runCtx.Err() + case <-ctx.Done(): + return ctx.Err() } } case outpoint := <-m.finalizedDepositChan: // If deposits notify us about their finalization, we // update the manager's internal state and flush the // finalized deposit from memory. - m.finalizeDeposit(outpoint) + delete(m.activeDeposits, outpoint) - case err := <-newBlockErrChan: + case err = <-newBlockErrChan: return err - case <-m.runCtx.Done(): - return m.runCtx.Err() + case <-ctx.Done(): + return ctx.Err() } } } -// recover recovers static address parameters, previous deposits and state -// machines from the database and starts the deposit notifier. -func (m *Manager) recover(ctx context.Context) error { +// recoverDeposits recovers static address parameters, previous deposits and +// state machines from the database and starts the deposit notifier. +func (m *Manager) recoverDeposits(ctx context.Context) error { log.Infof("Recovering static address parameters and deposits...") // Recover deposits. @@ -195,10 +183,7 @@ func (m *Manager) recover(ctx context.Context) error { log.Debugf("Recovering deposit %x", d.ID) // Create a state machine for a given deposit. - fsm, err := NewFSM( - m.runCtx, d, m.cfg, - m.finalizedDepositChan, true, - ) + fsm, err := NewFSM(ctx, d, m.cfg, m.finalizedDepositChan, true) if err != nil { return err } @@ -259,12 +244,12 @@ func (m *Manager) reconcileDeposits(ctx context.Context) error { ctx, MinConfs, MaxConfs, ) if err != nil { - return fmt.Errorf("unable to list new deposits: %v", err) + return fmt.Errorf("unable to list new deposits: %w", err) } newDeposits := m.filterNewDeposits(utxos) if err != nil { - return fmt.Errorf("unable to filter new deposits: %v", err) + return fmt.Errorf("unable to filter new deposits: %w", err) } if len(newDeposits) == 0 { @@ -275,14 +260,14 @@ func (m *Manager) reconcileDeposits(ctx context.Context) error { for _, utxo := range newDeposits { deposit, err := m.createNewDeposit(ctx, utxo) if err != nil { - return fmt.Errorf("unable to retain new deposit: %v", + return fmt.Errorf("unable to retain new deposit: %w", err) } log.Debugf("Received deposit: %v", deposit) - err = m.startDepositFsm(deposit) + err = m.startDepositFsm(ctx, deposit) if err != nil { - return fmt.Errorf("unable to start new deposit FSM: %v", + return fmt.Errorf("unable to start new deposit FSM: %w", err) } } @@ -332,9 +317,9 @@ func (m *Manager) createNewDeposit(ctx context.Context, return nil, err } - m.Lock() + m.mu.Lock() m.deposits[deposit.OutPoint] = deposit - m.Unlock() + m.mu.Unlock() return deposit, nil } @@ -348,7 +333,7 @@ func (m *Manager) getBlockHeight(ctx context.Context, ) if err != nil { return 0, fmt.Errorf("couldn't get confirmation height for "+ - "deposit, %v", err) + "deposit, %w", err) } notifChan, errChan, err := m.cfg.ChainNotifier.RegisterConfirmationsNtfn( //nolint:lll @@ -374,8 +359,8 @@ func (m *Manager) getBlockHeight(ctx context.Context, // filterNewDeposits filters the given utxos for new deposits that we haven't // seen before. func (m *Manager) filterNewDeposits(utxos []*lnwallet.Utxo) []*lnwallet.Utxo { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() var newDeposits []*lnwallet.Utxo for _, utxo := range utxos { @@ -390,115 +375,152 @@ func (m *Manager) filterNewDeposits(utxos []*lnwallet.Utxo) []*lnwallet.Utxo { // startDepositFsm creates a new state machine flow from the latest deposit to // our static address. -func (m *Manager) startDepositFsm(deposit *Deposit) error { +func (m *Manager) startDepositFsm(ctx context.Context, deposit *Deposit) error { // Create a state machine for a given deposit. - fsm, err := NewFSM( - m.runCtx, deposit, m.cfg, m.finalizedDepositChan, false, - ) + fsm, err := NewFSM(ctx, deposit, m.cfg, m.finalizedDepositChan, false) if err != nil { return err } // Send the start event to the state machine. go func() { - err = fsm.SendEvent(m.runCtx, OnStart, nil) + err = fsm.SendEvent(ctx, OnStart, nil) if err != nil { log.Errorf("Error sending OnStart event: %v", err) } }() - err = fsm.DefaultObserver.WaitForState(m.runCtx, time.Minute, Deposited) + err = fsm.DefaultObserver.WaitForState(ctx, time.Minute, Deposited) if err != nil { return err } // Add the FSM to the active FSMs map. - m.Lock() + m.mu.Lock() m.activeDeposits[deposit.OutPoint] = fsm - m.Unlock() + m.mu.Unlock() return nil } -func (m *Manager) finalizeDeposit(outpoint wire.OutPoint) { - m.Lock() - delete(m.activeDeposits, outpoint) - delete(m.deposits, outpoint) - m.Unlock() -} - -// GetActiveDepositsInState returns all active deposits. +// GetActiveDepositsInState returns all active deposits. This function is called +// on a client restart before the manager is fully initialized, hence we don't +// have to lock the deposits. func (m *Manager) GetActiveDepositsInState(stateFilter fsm.StateType) ( []*Deposit, error) { - m.Lock() - defer m.Unlock() + m.mu.Lock() + defer m.mu.Unlock() var deposits []*Deposit for _, fsm := range m.activeDeposits { - if fsm.deposit.GetState() != stateFilter { + deposits = append(deposits, fsm.deposit) + } + + lockDeposits(deposits) + defer unlockDeposits(deposits) + + filteredDeposits := make([]*Deposit, 0, len(deposits)) + for _, d := range deposits { + if !d.IsInStateNoLock(stateFilter) { continue } - deposits = append(deposits, fsm.deposit) + + filteredDeposits = append(filteredDeposits, d) } - sort.Slice(deposits, func(i, j int) bool { - return deposits[i].ConfirmationHeight < - deposits[j].ConfirmationHeight + sort.Slice(filteredDeposits, func(i, j int) bool { + return filteredDeposits[i].ConfirmationHeight < + filteredDeposits[j].ConfirmationHeight }) - return deposits, nil -} - -// GetAllDeposits returns all active deposits. -func (m *Manager) GetAllDeposits() ([]*Deposit, error) { - return m.cfg.Store.AllDeposits(m.runCtx) + return filteredDeposits, nil } // AllOutpointsActiveDeposits checks if all deposits referenced by the outpoints -// are active and in the specified state. +// are in our in-mem active deposits map and in the specified state. If +// fsm.EmptyState is set as targetState all deposits are returned regardless of +// their state. Each existent deposit is locked during the check. func (m *Manager) AllOutpointsActiveDeposits(outpoints []wire.OutPoint, - stateFilter fsm.StateType) ([]*Deposit, bool) { + targetState fsm.StateType) ([]*Deposit, bool) { + + m.mu.Lock() + defer m.mu.Unlock() - m.Lock() - defer m.Unlock() + _, deposits := m.toActiveDeposits(&outpoints) + if deposits == nil { + return nil, false + } + + // If the targetState is empty we return all active deposits regardless + // of state. + if targetState == fsm.EmptyState { + return deposits, true + } - deposits := make([]*Deposit, 0, len(outpoints)) - for _, o := range outpoints { - if _, ok := m.activeDeposits[o]; !ok { + lockDeposits(deposits) + defer unlockDeposits(deposits) + for _, d := range deposits { + if !d.IsInStateNoLock(targetState) { return nil, false } + } + + return deposits, true +} - deposit := m.deposits[o] - if deposit.GetState() != stateFilter { +// AllStringOutpointsActiveDeposits converts outpoint strings of format txid:idx +// to wire outpoints and checks if all deposits referenced by the outpoints are +// active and in the specified state. If fsm.EmptyState is referenced as +// stateFilter all deposits are returned regardless of their state. +func (m *Manager) AllStringOutpointsActiveDeposits(outpoints []string, + stateFilter fsm.StateType) ([]*Deposit, bool) { + + outPoints := make([]wire.OutPoint, len(outpoints)) + for i, o := range outpoints { + op, err := wire.NewOutPointFromString(o) + if err != nil { return nil, false } - deposits = append(deposits, m.deposits[o]) + outPoints[i] = *op } - return deposits, true + return m.AllOutpointsActiveDeposits(outPoints, stateFilter) } // TransitionDeposits allows a caller to transition a set of deposits to a new // state. -func (m *Manager) TransitionDeposits(deposits []*Deposit, event fsm.EventType, - expectedFinalState fsm.StateType) error { +// Caveat: The action triggered by the state transitions should not compute +// heavy things or call external endpoints that can block for a long time. +// Deposits will be released if a transition takes longer than +// DefaultTransitionTimeout which is set to 5 seconds. +func (m *Manager) TransitionDeposits(ctx context.Context, deposits []*Deposit, + event fsm.EventType, expectedFinalState fsm.StateType) error { + + outpoints := make([]wire.OutPoint, len(deposits)) + for i, d := range deposits { + outpoints[i] = d.OutPoint + } - for _, d := range deposits { - m.Lock() - sm, ok := m.activeDeposits[d.OutPoint] - m.Unlock() - if !ok { - return fmt.Errorf("deposit not found") - } + m.mu.Lock() + defer m.mu.Unlock() - err := sm.SendEvent(m.runCtx, event, nil) + stateMachines, _ := m.toActiveDeposits(&outpoints) + if stateMachines == nil { + return fmt.Errorf("deposits not found in active deposits") + } + + lockDeposits(deposits) + defer unlockDeposits(deposits) + for _, sm := range stateMachines { + err := sm.SendEvent(ctx, event, nil) if err != nil { return err } + err = sm.DefaultObserver.WaitForState( - m.runCtx, DefaultTransitionTimeout, expectedFinalState, + ctx, DefaultTransitionTimeout, expectedFinalState, ) if err != nil { return err @@ -508,7 +530,44 @@ func (m *Manager) TransitionDeposits(deposits []*Deposit, event fsm.EventType, return nil } +func lockDeposits(deposits []*Deposit) { + for _, d := range deposits { + d.Lock() + } +} + +func unlockDeposits(deposits []*Deposit) { + for _, d := range deposits { + d.Unlock() + } +} + +// GetAllDeposits returns all active deposits. +func (m *Manager) GetAllDeposits(ctx context.Context) ([]*Deposit, error) { + return m.cfg.Store.AllDeposits(ctx) +} + // UpdateDeposit overrides all fields of the deposit with given ID in the store. -func (m *Manager) UpdateDeposit(d *Deposit) error { - return m.cfg.Store.UpdateDeposit(m.runCtx, d) +func (m *Manager) UpdateDeposit(ctx context.Context, d *Deposit) error { + return m.cfg.Store.UpdateDeposit(ctx, d) +} + +// toActiveDeposits converts a list of outpoints to a list of FSMs and deposits. +// The caller should call mu.Lock() before calling this function. +func (m *Manager) toActiveDeposits(outpoints *[]wire.OutPoint) ([]*FSM, + []*Deposit) { + + fsms := make([]*FSM, 0, len(*outpoints)) + deposits := make([]*Deposit, 0, len(*outpoints)) + for _, o := range *outpoints { + sm, ok := m.activeDeposits[o] + if !ok { + return nil, nil + } + + fsms = append(fsms, sm) + deposits = append(deposits, m.deposits[o]) + } + + return fsms, deposits } diff --git a/staticaddr/deposit/sql_store.go b/staticaddr/deposit/sql_store.go index e9cb301b0..47a346ac4 100644 --- a/staticaddr/deposit/sql_store.go +++ b/staticaddr/deposit/sql_store.go @@ -67,7 +67,7 @@ func (s *SqlStore) UpdateDeposit(ctx context.Context, deposit *Deposit) error { insertUpdateArgs := sqlc.InsertDepositUpdateParams{ DepositID: deposit.ID[:], UpdateTimestamp: s.clock.Now().UTC(), - UpdateState: string(deposit.GetState()), + UpdateState: string(deposit.state), } var ( @@ -214,8 +214,3 @@ func (s *SqlStore) toDeposit(row sqlc.Deposit, WithdrawalSweepAddress: row.WithdrawalSweepAddress.String, }, nil } - -// Close closes the database connection. -func (s *SqlStore) Close() { - s.baseDB.DB.Close() -} From 6840b2b2b94e192ef9361949d6f259458d202ff7 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Wed, 28 Aug 2024 10:21:57 +0200 Subject: [PATCH 05/14] staticaddr: withdrawal changes to be squashed --- loopd/swapclient_server.go | 9 +- .../000010_static_address_deposits.up.sql | 7 +- loopdb/sqlc/models.go | 18 +- .../sqlc/queries/static_address_deposits.sql | 4 +- loopdb/sqlc/static_address_deposits.sql.go | 44 +-- staticaddr/deposit/deposit.go | 6 +- staticaddr/deposit/sql_store.go | 54 +++- staticaddr/withdraw/interface.go | 6 +- staticaddr/withdraw/manager.go | 282 +++++++++++------- 9 files changed, 262 insertions(+), 168 deletions(-) diff --git a/loopd/swapclient_server.go b/loopd/swapclient_server.go index e957d8a82..8982dd8e2 100644 --- a/loopd/swapclient_server.go +++ b/loopd/swapclient_server.go @@ -1444,12 +1444,17 @@ func (s *swapClientServer) WithdrawDeposits(ctx context.Context, } } - err = s.withdrawalManager.WithdrawDeposits(ctx, outpoints) + txhash, pkScript, err := s.withdrawalManager.DeliverWithdrawalRequest( + ctx, outpoints, + ) if err != nil { return nil, err } - return &looprpc.WithdrawDepositsResponse{}, err + return &looprpc.WithdrawDepositsResponse{ + WithdrawalTxHash: txhash, + PkScript: pkScript, + }, err } // GetStaticAddressSummary returns a summary static address related information. diff --git a/loopdb/sqlc/migrations/000010_static_address_deposits.up.sql b/loopdb/sqlc/migrations/000010_static_address_deposits.up.sql index 951450225..2952e7e53 100644 --- a/loopdb/sqlc/migrations/000010_static_address_deposits.up.sql +++ b/loopdb/sqlc/migrations/000010_static_address_deposits.up.sql @@ -26,9 +26,10 @@ CREATE TABLE IF NOT EXISTS deposits ( -- expiry_sweep_txid is the transaction id of the expiry sweep. expiry_sweep_txid BLOB, - -- withdrawal_sweep_address is the address that will be used to sweep the - -- deposit cooperatively with the server before it has expired. - withdrawal_sweep_address TEXT + -- finalized_withdrawal_tx is the coop signed tx that will be used to sweep + -- the deposit back to the clients wallet. It will be republished on block + -- arrival and after daemon restarts. + finalized_withdrawal_tx TEXT ); -- deposit_updates contains all the updates to a deposit. diff --git a/loopdb/sqlc/models.go b/loopdb/sqlc/models.go index a2929dccd..59939ad7a 100644 --- a/loopdb/sqlc/models.go +++ b/loopdb/sqlc/models.go @@ -10,15 +10,15 @@ import ( ) type Deposit struct { - ID int32 - DepositID []byte - TxHash []byte - OutIndex int32 - Amount int64 - ConfirmationHeight int64 - TimeoutSweepPkScript []byte - ExpirySweepTxid []byte - WithdrawalSweepAddress sql.NullString + ID int32 + DepositID []byte + TxHash []byte + OutIndex int32 + Amount int64 + ConfirmationHeight int64 + TimeoutSweepPkScript []byte + ExpirySweepTxid []byte + FinalizedWithdrawalTx sql.NullString } type DepositUpdate struct { diff --git a/loopdb/sqlc/queries/static_address_deposits.sql b/loopdb/sqlc/queries/static_address_deposits.sql index 6e3bb7a73..bc782bbb2 100644 --- a/loopdb/sqlc/queries/static_address_deposits.sql +++ b/loopdb/sqlc/queries/static_address_deposits.sql @@ -7,7 +7,7 @@ INSERT INTO deposits ( confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid, - withdrawal_sweep_address + finalized_withdrawal_tx ) VALUES ( $1, $2, @@ -26,7 +26,7 @@ SET out_index = $3, confirmation_height = $4, expiry_sweep_txid = $5, - withdrawal_sweep_address = $6 + finalized_withdrawal_tx = $6 WHERE deposits.deposit_id = $1; diff --git a/loopdb/sqlc/static_address_deposits.sql.go b/loopdb/sqlc/static_address_deposits.sql.go index 924e4b64f..75645c8e1 100644 --- a/loopdb/sqlc/static_address_deposits.sql.go +++ b/loopdb/sqlc/static_address_deposits.sql.go @@ -13,7 +13,7 @@ import ( const allDeposits = `-- name: AllDeposits :many SELECT - id, deposit_id, tx_hash, out_index, amount, confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid, withdrawal_sweep_address + id, deposit_id, tx_hash, out_index, amount, confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid, finalized_withdrawal_tx FROM deposits ORDER BY @@ -38,7 +38,7 @@ func (q *Queries) AllDeposits(ctx context.Context) ([]Deposit, error) { &i.ConfirmationHeight, &i.TimeoutSweepPkScript, &i.ExpirySweepTxid, - &i.WithdrawalSweepAddress, + &i.FinalizedWithdrawalTx, ); err != nil { return nil, err } @@ -62,7 +62,7 @@ INSERT INTO deposits ( confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid, - withdrawal_sweep_address + finalized_withdrawal_tx ) VALUES ( $1, $2, @@ -76,14 +76,14 @@ INSERT INTO deposits ( ` type CreateDepositParams struct { - DepositID []byte - TxHash []byte - OutIndex int32 - Amount int64 - ConfirmationHeight int64 - TimeoutSweepPkScript []byte - ExpirySweepTxid []byte - WithdrawalSweepAddress sql.NullString + DepositID []byte + TxHash []byte + OutIndex int32 + Amount int64 + ConfirmationHeight int64 + TimeoutSweepPkScript []byte + ExpirySweepTxid []byte + FinalizedWithdrawalTx sql.NullString } func (q *Queries) CreateDeposit(ctx context.Context, arg CreateDepositParams) error { @@ -95,14 +95,14 @@ func (q *Queries) CreateDeposit(ctx context.Context, arg CreateDepositParams) er arg.ConfirmationHeight, arg.TimeoutSweepPkScript, arg.ExpirySweepTxid, - arg.WithdrawalSweepAddress, + arg.FinalizedWithdrawalTx, ) return err } const getDeposit = `-- name: GetDeposit :one SELECT - id, deposit_id, tx_hash, out_index, amount, confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid, withdrawal_sweep_address + id, deposit_id, tx_hash, out_index, amount, confirmation_height, timeout_sweep_pk_script, expiry_sweep_txid, finalized_withdrawal_tx FROM deposits WHERE @@ -121,7 +121,7 @@ func (q *Queries) GetDeposit(ctx context.Context, depositID []byte) (Deposit, er &i.ConfirmationHeight, &i.TimeoutSweepPkScript, &i.ExpirySweepTxid, - &i.WithdrawalSweepAddress, + &i.FinalizedWithdrawalTx, ) return i, err } @@ -180,18 +180,18 @@ SET out_index = $3, confirmation_height = $4, expiry_sweep_txid = $5, - withdrawal_sweep_address = $6 + finalized_withdrawal_tx = $6 WHERE deposits.deposit_id = $1 ` type UpdateDepositParams struct { - DepositID []byte - TxHash []byte - OutIndex int32 - ConfirmationHeight int64 - ExpirySweepTxid []byte - WithdrawalSweepAddress sql.NullString + DepositID []byte + TxHash []byte + OutIndex int32 + ConfirmationHeight int64 + ExpirySweepTxid []byte + FinalizedWithdrawalTx sql.NullString } func (q *Queries) UpdateDeposit(ctx context.Context, arg UpdateDepositParams) error { @@ -201,7 +201,7 @@ func (q *Queries) UpdateDeposit(ctx context.Context, arg UpdateDepositParams) er arg.OutIndex, arg.ConfirmationHeight, arg.ExpirySweepTxid, - arg.WithdrawalSweepAddress, + arg.FinalizedWithdrawalTx, ) return err } diff --git a/staticaddr/deposit/deposit.go b/staticaddr/deposit/deposit.go index a360e7679..6da9e729a 100644 --- a/staticaddr/deposit/deposit.go +++ b/staticaddr/deposit/deposit.go @@ -52,9 +52,9 @@ type Deposit struct { // ExpirySweepTxid is the transaction id of the expiry sweep. ExpirySweepTxid chainhash.Hash - // WithdrawalSweepAddress is the address that is used to - // cooperatively sweep the deposit to before it is expired. - WithdrawalSweepAddress string + // FinalizedWithdrawalTx is the coop signed withdrawal transaction. It + // is republished on new block arrivals and on client restarts. + FinalizedWithdrawalTx *wire.MsgTx sync.Mutex } diff --git a/staticaddr/deposit/sql_store.go b/staticaddr/deposit/sql_store.go index 47a346ac4..6a52ae282 100644 --- a/staticaddr/deposit/sql_store.go +++ b/staticaddr/deposit/sql_store.go @@ -1,8 +1,10 @@ package deposit import ( + "bytes" "context" "database/sql" + "encoding/hex" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -39,10 +41,6 @@ func (s *SqlStore) CreateDeposit(ctx context.Context, deposit *Deposit) error { Amount: int64(deposit.Value), ConfirmationHeight: deposit.ConfirmationHeight, TimeoutSweepPkScript: deposit.TimeOutSweepPkScript, - WithdrawalSweepAddress: sql.NullString{ - String: deposit.WithdrawalSweepAddress, - Valid: deposit.WithdrawalSweepAddress != "", - }, } updateArgs := sqlc.InsertDepositUpdateParams{ @@ -81,18 +79,34 @@ func (s *SqlStore) UpdateDeposit(ctx context.Context, deposit *Deposit) error { } ) + var finalizedWithdrawalTx string + if deposit.FinalizedWithdrawalTx != nil { + var buffer bytes.Buffer + err := deposit.FinalizedWithdrawalTx.Serialize( + &buffer, + ) + if err != nil { + return err + } + + finalizedWithdrawalTx = hex.EncodeToString(buffer.Bytes()) + } + updateArgs := sqlc.UpdateDepositParams{ DepositID: deposit.ID[:], TxHash: txHash, OutIndex: outIndex.Int32, ConfirmationHeight: confirmationHeight.Int64, - ExpirySweepTxid: deposit.ExpirySweepTxid[:], - WithdrawalSweepAddress: sql.NullString{ - String: deposit.WithdrawalSweepAddress, - Valid: deposit.WithdrawalSweepAddress != "", + FinalizedWithdrawalTx: sql.NullString{ + String: finalizedWithdrawalTx, + Valid: finalizedWithdrawalTx != "", }, } + if deposit.ExpirySweepTxid != (chainhash.Hash{}) { + updateArgs.ExpirySweepTxid = deposit.ExpirySweepTxid[:] + } + return s.baseDB.ExecTx(ctx, &loopdb.SqliteTxOptions{}, func(q *sqlc.Queries) error { err := q.UpdateDeposit(ctx, updateArgs) @@ -200,6 +214,20 @@ func (s *SqlStore) toDeposit(row sqlc.Deposit, expirySweepTxid = *hash } + var finalizedWithdrawalTx *wire.MsgTx + if row.FinalizedWithdrawalTx.Valid { + finalizedWithdrawalTx = &wire.MsgTx{} + tx, err := hex.DecodeString(row.FinalizedWithdrawalTx.String) + if err != nil { + return nil, err + } + + err = finalizedWithdrawalTx.Deserialize(bytes.NewReader(tx)) + if err != nil { + return nil, err + } + } + return &Deposit{ ID: id, state: fsm.StateType(lastUpdate.UpdateState), @@ -207,10 +235,10 @@ func (s *SqlStore) toDeposit(row sqlc.Deposit, Hash: *txHash, Index: uint32(row.OutIndex), }, - Value: btcutil.Amount(row.Amount), - ConfirmationHeight: row.ConfirmationHeight, - TimeOutSweepPkScript: row.TimeoutSweepPkScript, - ExpirySweepTxid: expirySweepTxid, - WithdrawalSweepAddress: row.WithdrawalSweepAddress.String, + Value: btcutil.Amount(row.Amount), + ConfirmationHeight: row.ConfirmationHeight, + TimeOutSweepPkScript: row.TimeoutSweepPkScript, + ExpirySweepTxid: expirySweepTxid, + FinalizedWithdrawalTx: finalizedWithdrawalTx, }, nil } diff --git a/staticaddr/withdraw/interface.go b/staticaddr/withdraw/interface.go index 48f110cea..ed089b36a 100644 --- a/staticaddr/withdraw/interface.go +++ b/staticaddr/withdraw/interface.go @@ -37,8 +37,8 @@ type DepositManager interface { AllOutpointsActiveDeposits(outpoints []wire.OutPoint, stateFilter fsm.StateType) ([]*deposit.Deposit, bool) - TransitionDeposits(deposits []*deposit.Deposit, event fsm.EventType, - expectedFinalState fsm.StateType) error + TransitionDeposits(ctx context.Context, deposits []*deposit.Deposit, + event fsm.EventType, expectedFinalState fsm.StateType) error - UpdateDeposit(d *deposit.Deposit) error + UpdateDeposit(ctx context.Context, d *deposit.Deposit) error } diff --git a/staticaddr/withdraw/manager.go b/staticaddr/withdraw/manager.go index a3b61e959..26f5de501 100644 --- a/staticaddr/withdraw/manager.go +++ b/staticaddr/withdraw/manager.go @@ -6,7 +6,6 @@ import ( "fmt" "reflect" "strings" - "sync" "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/btcutil" @@ -69,27 +68,41 @@ type ManagerConfig struct { Signer lndclient.SignerClient } -// Manager manages the address state machines. -type Manager struct { - cfg *ManagerConfig +// newWithdrawalRequest is used to send withdrawal request to the manager main +// loop. +type newWithdrawalRequest struct { + outpoints []wire.OutPoint + respChan chan *newWithdrawalResponse +} - runCtx context.Context +// newWithdrawalResponse is used to return withdrawal info and error to the +// server. +type newWithdrawalResponse struct { + txHash string + withdrawalPkScript string + err error +} - sync.Mutex +// Manager manages the withdrawal state machines. +type Manager struct { + cfg *ManagerConfig - // initChan signals the daemon that the address manager has completed + // initChan signals the daemon that the withdrawal manager has completed // its initialization. initChan chan struct{} - // initiationHeight stores the currently best known block height. - initiationHeight uint32 + // newWithdrawalRequestChan receives a list of outpoints that should be + // withdrawn. The request is forwarded to the managers main loop. + newWithdrawalRequestChan chan newWithdrawalRequest + + // exitChan signals subroutines that the withdrawal manager is exiting. + exitChan chan struct{} - // currentHeight stores the currently best known block height. - currentHeight uint32 + // errChan forwards errors from the withdrawal manager to the server. + errChan chan error - // activeWithdrawals stores pending withdrawals by their withdrawal - // address. - activeWithdrawals map[string][]*deposit.Deposit + // initiationHeight stores the currently best known block height. + initiationHeight uint32 // finalizedWithdrawalTx are the finalized withdrawal transactions that // are published to the network and re-published on block arrivals. @@ -99,27 +112,27 @@ type Manager struct { // NewManager creates a new deposit withdrawal manager. func NewManager(cfg *ManagerConfig) *Manager { return &Manager{ - cfg: cfg, - initChan: make(chan struct{}), - activeWithdrawals: make(map[string][]*deposit.Deposit), - finalizedWithdrawalTxns: make(map[chainhash.Hash]*wire.MsgTx), + cfg: cfg, + initChan: make(chan struct{}), + finalizedWithdrawalTxns: make(map[chainhash.Hash]*wire.MsgTx), + exitChan: make(chan struct{}), + newWithdrawalRequestChan: make(chan newWithdrawalRequest), + errChan: make(chan error), } } // Run runs the deposit withdrawal manager. func (m *Manager) Run(ctx context.Context, currentHeight uint32) error { - m.runCtx = ctx + m.initiationHeight = currentHeight - m.Lock() - m.currentHeight, m.initiationHeight = currentHeight, currentHeight - m.Unlock() + newBlockChan, newBlockErrChan, err := + m.cfg.ChainNotifier.RegisterBlockEpochNtfn(ctx) - newBlockChan, newBlockErrChan, err := m.cfg.ChainNotifier.RegisterBlockEpochNtfn(m.runCtx) //nolint:lll if err != nil { return err } - err = m.recover() + err = m.recoverWithdrawals(ctx) if err != nil { return err } @@ -128,29 +141,59 @@ func (m *Manager) Run(ctx context.Context, currentHeight uint32) error { // initialization. close(m.initChan) + var ( + txHash string + pkScript string + ) for { select { - case height := <-newBlockChan: - m.Lock() - m.currentHeight = uint32(height) - m.Unlock() - - err = m.republishWithdrawals() + case <-newBlockChan: + err = m.republishWithdrawals(ctx) if err != nil { log.Errorf("Error republishing withdrawals: %v", err) } + case request := <-m.newWithdrawalRequestChan: + txHash, pkScript, err = m.WithdrawDeposits( + ctx, request.outpoints, + ) + if err != nil { + log.Errorf("Error withdrawing deposits: %v", + err) + } + + // We forward the initialized loop-in and error to + // DeliverLoopInRequest. + resp := &newWithdrawalResponse{ + txHash: txHash, + withdrawalPkScript: pkScript, + err: err, + } + select { + case request.respChan <- resp: + + case <-ctx.Done(): + // Notify subroutines that the main loop has + // been canceled. + close(m.exitChan) + + return ctx.Err() + } + case err = <-newBlockErrChan: return err - case <-m.runCtx.Done(): - return m.runCtx.Err() + case <-ctx.Done(): + // Signal subroutines that the manager is exiting. + close(m.exitChan) + + return ctx.Err() } } } -func (m *Manager) recover() error { +func (m *Manager) recoverWithdrawals(ctx context.Context) error { // To recover withdrawals we skim through all active deposits and check // if they have a withdrawal address set. For the ones that do we // cluster those with equal withdrawal addresses and kick-off @@ -163,58 +206,43 @@ func (m *Manager) recover() error { return err } - // Group the deposits by their withdrawal address. - depositsByWithdrawalAddress := make(map[string][]*deposit.Deposit) + // Group the deposits by their finalized withdrawal transaction. + depositsByWithdrawalTx := make(map[*wire.MsgTx][]*deposit.Deposit) for _, d := range activeDeposits { - sweepAddress := d.WithdrawalSweepAddress - if sweepAddress == "" { + withdrawalTx := d.FinalizedWithdrawalTx + if withdrawalTx == nil { continue } - depositsByWithdrawalAddress[sweepAddress] = append( - depositsByWithdrawalAddress[sweepAddress], d, + depositsByWithdrawalTx[withdrawalTx] = append( + depositsByWithdrawalTx[withdrawalTx], d, ) } // We can now reinstate each cluster of deposits for a withdrawal. - for address, deposits := range depositsByWithdrawalAddress { - withdrawalAddress, err := btcutil.DecodeAddress( - address, m.cfg.ChainParams, - ) - if err != nil { - return err - } - + for finalizedWithdrawalTx, deposits := range depositsByWithdrawalTx { + tx := finalizedWithdrawalTx err = m.cfg.DepositManager.TransitionDeposits( - deposits, deposit.OnWithdrawInitiated, + ctx, deposits, deposit.OnWithdrawInitiated, deposit.Withdrawing, ) if err != nil { return err } - finalizedTx, err := m.createFinalizedWithdrawalTx( - m.runCtx, deposits, withdrawalAddress, - ) - if err != nil { - return err - } - - err = m.publishFinalizedWithdrawalTx(finalizedTx) + err = m.publishFinalizedWithdrawalTx(ctx, tx) if err != nil { return err } err = m.handleWithdrawal( - deposits, finalizedTx.TxHash(), withdrawalAddress, + ctx, deposits, tx.TxHash(), tx.TxOut[0].PkScript, ) if err != nil { return err } - m.Lock() - m.activeWithdrawals[address] = deposits - m.Unlock() + m.finalizedWithdrawalTxns[tx.TxHash()] = tx } return nil @@ -230,10 +258,11 @@ func (m *Manager) WaitInitComplete() { // WithdrawDeposits starts a deposits withdrawal flow. func (m *Manager) WithdrawDeposits(ctx context.Context, - outpoints []wire.OutPoint) error { + outpoints []wire.OutPoint) (string, string, error) { if len(outpoints) == 0 { - return fmt.Errorf("no outpoints selected to withdraw") + return "", "", fmt.Errorf("no outpoints selected to " + + "withdraw, unconfirmed deposits can't be withdrawn") } // Ensure that the deposits are in a state in which they can be @@ -242,7 +271,7 @@ func (m *Manager) WithdrawDeposits(ctx context.Context, outpoints, deposit.Deposited) if !allActive { - return ErrWithdrawingInactiveDeposits + return "", "", ErrWithdrawingInactiveDeposits } // Generate the withdrawal address from our local lnd wallet. @@ -251,16 +280,23 @@ func (m *Manager) WithdrawDeposits(ctx context.Context, walletrpc.AddressType_TAPROOT_PUBKEY, false, ) if err != nil { - return err + return "", "", err } - // Attach the withdrawal address to the deposits. After a client restart - // we can use this address as an indicator to continue the withdrawal. - // If there are multiple deposits with the same withdrawal address, we - // bundle them together in the same withdrawal transaction. + finalizedTx, err := m.createFinalizedWithdrawalTx( + ctx, deposits, withdrawalAddress, + ) + if err != nil { + return "", "", err + } + + // Attach the finalized withdrawal tx to the deposits. After a client + // restart we can use this address as an indicator to republish the + // withdrawal tx and continue the withdrawal. + // Deposits with the same withdrawal tx are part of the same withdrawal. for _, d := range deposits { d.Lock() - d.WithdrawalSweepAddress = withdrawalAddress.String() + d.FinalizedWithdrawalTx = finalizedTx d.Unlock() } @@ -270,36 +306,32 @@ func (m *Manager) WithdrawDeposits(ctx context.Context, // to an error in the state machine. The already transitioned deposits // should be reset to the Deposit state after a restart. err = m.cfg.DepositManager.TransitionDeposits( - deposits, deposit.OnWithdrawInitiated, deposit.Withdrawing, + ctx, deposits, deposit.OnWithdrawInitiated, deposit.Withdrawing, ) if err != nil { - return err + return "", "", err } - finalizedTx, err := m.createFinalizedWithdrawalTx( - ctx, deposits, withdrawalAddress, - ) + err = m.publishFinalizedWithdrawalTx(ctx, finalizedTx) if err != nil { - return err + return "", "", err } - err = m.publishFinalizedWithdrawalTx(finalizedTx) + withdrawalPkScript, err := txscript.PayToAddrScript(withdrawalAddress) if err != nil { - return err + return "", "", err } err = m.handleWithdrawal( - deposits, finalizedTx.TxHash(), withdrawalAddress, + ctx, deposits, finalizedTx.TxHash(), withdrawalPkScript, ) if err != nil { - return err + return "", "", err } - m.Lock() - m.activeWithdrawals[withdrawalAddress.String()] = deposits - m.Unlock() + m.finalizedWithdrawalTxns[finalizedTx.TxHash()] = finalizedTx - return nil + return finalizedTx.TxID(), withdrawalAddress.String(), nil } func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context, @@ -316,7 +348,7 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context, // Get the fee rate for the withdrawal sweep. withdrawalSweepFeeRate, err := m.cfg.WalletKit.EstimateFeeRate( - m.runCtx, defaultConfTarget, + ctx, defaultConfTarget, ) if err != nil { return nil, err @@ -324,7 +356,7 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context, outpoints := toOutpoints(deposits) resp, err := m.cfg.StaticAddressServerClient.ServerWithdrawDeposits( - m.runCtx, &staticaddressrpc.ServerWithdrawRequest{ + ctx, &staticaddressrpc.ServerWithdrawRequest{ Outpoints: toPrevoutInfo(outpoints), ClientNonces: clientNonces, ClientSweepAddr: withdrawalAddress.String(), @@ -340,7 +372,7 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context, ) if err != nil { return nil, fmt.Errorf("couldn't get confirmation height for "+ - "deposit, %v", err) + "deposit, %w", err) } prevOuts := m.toPrevOuts(deposits, addressParams.PkScript) @@ -361,7 +393,7 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context, // Next we'll get our sweep tx signatures. prevOutFetcher := txscript.NewMultiPrevOutFetcher(prevOuts) _, err = m.signMusig2Tx( - m.runCtx, prevOutFetcher, outpoints, m.cfg.Signer, withdrawalTx, + ctx, prevOutFetcher, outpoints, m.cfg.Signer, withdrawalTx, withdrawalSessions, coopServerNonces, ) if err != nil { @@ -370,19 +402,19 @@ func (m *Manager) createFinalizedWithdrawalTx(ctx context.Context, // Now we'll finalize the sweepless sweep transaction. finalizedTx, err := m.finalizeMusig2Transaction( - m.runCtx, outpoints, m.cfg.Signer, withdrawalSessions, + ctx, outpoints, m.cfg.Signer, withdrawalSessions, withdrawalTx, resp.Musig2SweepSigs, ) if err != nil { return nil, err } - m.finalizedWithdrawalTxns[finalizedTx.TxHash()] = finalizedTx - return finalizedTx, nil } -func (m *Manager) publishFinalizedWithdrawalTx(tx *wire.MsgTx) error { +func (m *Manager) publishFinalizedWithdrawalTx(ctx context.Context, + tx *wire.MsgTx) error { + if tx == nil { return errors.New("can't publish, finalized withdrawal tx is " + "nil") @@ -391,7 +423,7 @@ func (m *Manager) publishFinalizedWithdrawalTx(tx *wire.MsgTx) error { txLabel := fmt.Sprintf("deposit-withdrawal-%v", tx.TxHash()) // Publish the withdrawal sweep transaction. - err := m.cfg.WalletKit.PublishTransaction(m.runCtx, tx, txLabel) + err := m.cfg.WalletKit.PublishTransaction(ctx, tx, txLabel) if err != nil { if !strings.Contains(err.Error(), "output already spent") { @@ -404,20 +436,14 @@ func (m *Manager) publishFinalizedWithdrawalTx(tx *wire.MsgTx) error { return nil } -func (m *Manager) handleWithdrawal(deposits []*deposit.Deposit, - txHash chainhash.Hash, withdrawalAddress btcutil.Address) error { +func (m *Manager) handleWithdrawal(ctx context.Context, + deposits []*deposit.Deposit, txHash chainhash.Hash, + withdrawalPkScript []byte) error { - withdrawalPkScript, err := txscript.PayToAddrScript(withdrawalAddress) - if err != nil { - return err - } - - m.Lock() confChan, errChan, err := m.cfg.ChainNotifier.RegisterConfirmationsNtfn( - m.runCtx, &txHash, withdrawalPkScript, MinConfs, + ctx, &txHash, withdrawalPkScript, MinConfs, int32(m.initiationHeight), ) - m.Unlock() if err != nil { return err } @@ -426,7 +452,7 @@ func (m *Manager) handleWithdrawal(deposits []*deposit.Deposit, select { case <-confChan: err = m.cfg.DepositManager.TransitionDeposits( - deposits, deposit.OnWithdrawn, + ctx, deposits, deposit.OnWithdrawn, deposit.Withdrawn, ) if err != nil { @@ -437,15 +463,12 @@ func (m *Manager) handleWithdrawal(deposits []*deposit.Deposit, // Remove the withdrawal from the active withdrawals and // remove its finalized to stop republishing it on block // arrivals. - m.Lock() - delete(m.activeWithdrawals, withdrawalAddress.String()) delete(m.finalizedWithdrawalTxns, txHash) - m.Unlock() case err := <-errChan: log.Errorf("Error waiting for confirmation: %v", err) - case <-m.runCtx.Done(): + case <-ctx.Done(): log.Errorf("Withdrawal tx confirmation wait canceled") } }() @@ -701,7 +724,7 @@ func (m *Manager) createMusig2Session(ctx context.Context) ( ) if err != nil { return nil, fmt.Errorf("couldn't get confirmation height for "+ - "deposit, %v", err) + "deposit, %w", err) } signers := [][]byte{ @@ -712,7 +735,7 @@ func (m *Manager) createMusig2Session(ctx context.Context) ( address, err := m.cfg.AddressManager.GetStaticAddress(ctx) if err != nil { return nil, fmt.Errorf("couldn't get confirmation height for "+ - "deposit, %v", err) + "deposit, %w", err) } expiryLeaf := address.TimeoutLeaf @@ -744,14 +767,14 @@ func (m *Manager) toPrevOuts(deposits []*deposit.Deposit, return prevOuts } -func (m *Manager) republishWithdrawals() error { +func (m *Manager) republishWithdrawals(ctx context.Context) error { for _, finalizedTx := range m.finalizedWithdrawalTxns { if finalizedTx == nil { log.Warnf("Finalized withdrawal tx is nil") continue } - err := m.publishFinalizedWithdrawalTx(finalizedTx) + err := m.publishFinalizedWithdrawalTx(ctx, finalizedTx) if err != nil { log.Errorf("Error republishing withdrawal: %v", err) @@ -761,3 +784,40 @@ func (m *Manager) republishWithdrawals() error { return nil } + +// DeliverWithdrawalRequest forwards a withdrawal request to the manager main +// loop. +func (m *Manager) DeliverWithdrawalRequest(ctx context.Context, + outpoints []wire.OutPoint) (string, string, error) { + + request := newWithdrawalRequest{ + outpoints: outpoints, + respChan: make(chan *newWithdrawalResponse), + } + + // Send the new loop-in request to the manager run loop. + select { + case m.newWithdrawalRequestChan <- request: + + case <-m.exitChan: + return "", "", fmt.Errorf("withdrawal manager has been " + + "canceled") + + case <-ctx.Done(): + return "", "", fmt.Errorf("context canceled while withdrawing") + } + + // Wait for the response from the manager run loop. + select { + case resp := <-request.respChan: + return resp.txHash, resp.withdrawalPkScript, resp.err + + case <-m.exitChan: + return "", "", fmt.Errorf("withdrawal manager has been " + + "canceled") + + case <-ctx.Done(): + return "", "", fmt.Errorf("context canceled while waiting " + + "for withdrawal response") + } +} From e361eb5622594693a62fe33436242552816b07ee Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Thu, 26 Sep 2024 11:42:44 +0200 Subject: [PATCH 06/14] loopin: public scope for ValidateLoopInContract --- client.go | 6 +++++- loopin.go | 18 ++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/client.go b/client.go index 9adac31bb..c15505b70 100644 --- a/client.go +++ b/client.go @@ -43,9 +43,13 @@ var ( ErrSwapAmountTooHigh = errors.New("swap amount too high") // ErrExpiryTooFar is returned when the server proposes an expiry that - // is too soon for us. + // is too far in the future. ErrExpiryTooFar = errors.New("swap expiry too far") + // ErrExpiryTooSoon is returned when the server proposes an expiry that + // is too soon. + ErrExpiryTooSoon = errors.New("swap expiry too soon") + // ErrInsufficientBalance indicates insufficient confirmed balance to // publish a swap. ErrInsufficientBalance = errors.New("insufficient confirmed balance") diff --git a/loopin.go b/loopin.go index 21480a1ac..99295d915 100644 --- a/loopin.go +++ b/loopin.go @@ -38,6 +38,10 @@ var ( // getting us to lock up our funds to an arbitrary point in the future. MaxLoopInAcceptDelta = int32(1500) + // MinLoopInExpiryDelta defines the minimum number of remaining blocks + // that we accept until htlc expiry path that opens up for us to sweep. + MinLoopInExpiryDelta = int32(100) + // MinLoopInPublishDelta defines the minimum number of remaining blocks // until on-chain htlc expiry required to proceed to publishing the htlc // tx. This value isn't critical, as we could even safely publish the @@ -248,7 +252,7 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig, // Validate if the response parameters are outside our allowed range // preventing us from continuing with a swap. - err = validateLoopInContract(currentHeight, swapResp) + err = ValidateLoopInContract(currentHeight, swapResp.expiry) if err != nil { return nil, err } @@ -429,14 +433,20 @@ func resumeLoopInSwap(_ context.Context, cfg *swapConfig, return swap, nil } -// validateLoopInContract validates the contract parameters against our request. -func validateLoopInContract(height int32, response *newLoopInResponse) error { +// ValidateLoopInContract validates the contract parameters against our +// configured maximum values. +func ValidateLoopInContract(height int32, htlcExpiry int32) error { // Verify that we are not forced to publish a htlc that locks up our // funds for too long in case the server doesn't follow through. - if response.expiry-height > MaxLoopInAcceptDelta { + if htlcExpiry-height > MaxLoopInAcceptDelta { return ErrExpiryTooFar } + // Ensure that the expiry height is in the future. + if htlcExpiry < height+MinLoopInExpiryDelta { + return ErrExpiryTooSoon + } + return nil } From 0df0f7d3ee59b710ba9ac738f46c1934d0200c7c Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Thu, 26 Sep 2024 11:43:38 +0200 Subject: [PATCH 07/14] interface: add StaticAddressLoopInRequest interface --- interface.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/interface.go b/interface.go index 37e5e01c4..8764bf76e 100644 --- a/interface.go +++ b/interface.go @@ -234,6 +234,48 @@ type LoopInRequest struct { RouteHints [][]zpay32.HopHint } +// StaticAddressLoopInRequest contains the required parameters for the swap. +type StaticAddressLoopInRequest struct { + // DepositOutpoints contain the outpoints in format txid:idx of the + // static address deposits that are being looped in. The sum of output + // values constitute the swap amount. + DepositOutpoints []string + + // MaxSwapFee is the maximum we are willing to pay the server for the + // swap. This value is not disclosed in the swap initiation call, but if + // the server asks for a higher fee, we abort the swap. Typically, this + // value is taken from the response of the LoopInQuote call. It + // includes the pre-pay amount. + MaxSwapFee btcutil.Amount + + // LastHop optionally specifies the last hop to use for the loop in + // payment. + LastHop *route.Vertex + + // Label contains an optional text label for the swap. + Label string + + // Initiator is an optional string that identifies what software + // initiated the swap (loop CLI, autolooper, LiT UI and so on) and is + // appended to the user agent string. + Initiator string + + // Private indicates whether the destination node should be considered + // private. In which case, loop will generate hophints to assist with + // probing and payment. + Private bool + + // RouteHints are optional route hints to reach the destination through + // private channels. + RouteHints [][]zpay32.HopHint + + // PaymentTimeoutSeconds allows the user to specify an upper limit for + // the amount of time the server is allowed to fulfill the off-chain + // swap payment. If the timeout is reached the swap will be aborted and + // the client can retry the swap if desired with different parameters. + PaymentTimeoutSeconds uint32 +} + // LoopInTerms are the server terms on which it executes loop in swaps. type LoopInTerms struct { // MinSwapAmount is the minimum amount that the server requires for a From b31cb6fe4d0f2ca8a9da38059748db0530cd2edb Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Thu, 17 Oct 2024 13:29:43 +0200 Subject: [PATCH 08/14] staticaddr: configurable max htlc tx fee In this commit we introduce maximum fee percentages for the static loop-in htlc transactions. Since the server has the ability to publish htlc transactions without settling the swap payment we have to restrict the amount the server allocates for fees of these transactions. --- client.go | 16 ++++++++++++++++ loopd/config.go | 47 +++++++++++++++++++++++++++-------------------- loopd/utils.go | 22 ++++++++++++---------- 3 files changed, 55 insertions(+), 30 deletions(-) diff --git a/client.go b/client.go index c15505b70..9317316f7 100644 --- a/client.go +++ b/client.go @@ -135,6 +135,22 @@ type ClientConfig struct { // MaxPaymentRetries is the maximum times we retry an off-chain payment // (used in loop out). MaxPaymentRetries int + + // MaxStaticAddrHtlcFeePercentage is the percentage of the swap amount + // that we allow the server to charge for the htlc transaction. + // Although highly unlikely, this is a defense against the server + // publishing the htlc without paying the swap invoice, forcing us to + // sweep the timeout path. + MaxStaticAddrHtlcFeePercentage float64 + + // MaxStaticAddrHtlcBackupFeePercentage is the percentage of the swap + // amount that we allow the server to charge for the htlc backup + // transactions. This is a defense against the server publishing the + // htlc backup without paying the swap invoice, forcing us to sweep the + // timeout path. This value is elevated compared to + // MaxStaticAddrHtlcFeePercentage since it serves the server as backup + // transaction in case of fee spikes. + MaxStaticAddrHtlcBackupFeePercentage float64 } // NewClient returns a new instance to initiate swaps with. diff --git a/loopd/config.go b/loopd/config.go index 586f9ffb4..b84926d68 100644 --- a/loopd/config.go +++ b/loopd/config.go @@ -42,11 +42,13 @@ var ( LoopDirBase, DefaultNetwork, defaultSqliteDatabaseFileName, ) - defaultMaxLogFiles = 3 - defaultMaxLogFileSize = 10 - defaultLoopOutMaxParts = uint32(5) - defaultTotalPaymentTimeout = time.Minute * 60 - defaultMaxPaymentRetries = 3 + defaultMaxLogFiles = 3 + defaultMaxLogFileSize = 10 + defaultLoopOutMaxParts = uint32(5) + defaultTotalPaymentTimeout = time.Minute * 60 + defaultMaxPaymentRetries = 3 + defaultMaxStaticAddrHtlcFeePercentage = 0.2 + defaultMaxStaticAddrHtlcBackupFeePercentage = 0.5 // defaultRPCBatchSize is the default batch size to use for RPC calls // we make to LND during migrations. If operations on the LND side are @@ -183,6 +185,9 @@ type Config struct { TotalPaymentTimeout time.Duration `long:"totalpaymenttimeout" description:"The timeout to use for off-chain payments."` MaxPaymentRetries int `long:"maxpaymentretries" description:"The maximum number of times an off-chain payment may be retried."` + MaxStaticAddrHtlcFeePercentage float64 `long:"maxstaticaddrhtlcfeepercentage" description:"The maximum fee percentage that the server can charge for the htlc tx."` + MaxStaticAddrHtlcBackupFeePercentage float64 `long:"maxstaticaddrhtlcbackupfeepercentage" description:"The maximum fee percentage that the server can charge for the htlc backup tx. The backup transaction is only used in rare cases when the regular htlc tx is not confirmed on time. These backup transactions refer to high fee or extremely high fee transactions in the API."` + EnableExperimental bool `long:"experimental" description:"Enable experimental features: reservations"` MigrationRPCBatchSize int `long:"migrationrpcbatchsize" description:"The RPC batch size to use during migrations."` @@ -215,21 +220,23 @@ func DefaultConfig() Config { Sqlite: &loopdb.SqliteConfig{ DatabaseFileName: defaultSqliteDatabasePath, }, - LogDir: defaultLogDir, - MaxLogFiles: defaultMaxLogFiles, - MaxLogFileSize: defaultMaxLogFileSize, - DebugLevel: defaultLogLevel, - TLSCertPath: DefaultTLSCertPath, - TLSKeyPath: DefaultTLSKeyPath, - TLSValidity: DefaultAutogenValidity, - MacaroonPath: DefaultMacaroonPath, - MaxL402Cost: l402.DefaultMaxCostSats, - MaxL402Fee: l402.DefaultMaxRoutingFeeSats, - LoopOutMaxParts: defaultLoopOutMaxParts, - TotalPaymentTimeout: defaultTotalPaymentTimeout, - MaxPaymentRetries: defaultMaxPaymentRetries, - EnableExperimental: false, - MigrationRPCBatchSize: defaultRPCBatchSize, + LogDir: defaultLogDir, + MaxLogFiles: defaultMaxLogFiles, + MaxLogFileSize: defaultMaxLogFileSize, + DebugLevel: defaultLogLevel, + TLSCertPath: DefaultTLSCertPath, + TLSKeyPath: DefaultTLSKeyPath, + TLSValidity: DefaultAutogenValidity, + MacaroonPath: DefaultMacaroonPath, + MaxL402Cost: l402.DefaultMaxCostSats, + MaxL402Fee: l402.DefaultMaxRoutingFeeSats, + LoopOutMaxParts: defaultLoopOutMaxParts, + TotalPaymentTimeout: defaultTotalPaymentTimeout, + MaxPaymentRetries: defaultMaxPaymentRetries, + MaxStaticAddrHtlcFeePercentage: defaultMaxStaticAddrHtlcFeePercentage, + MaxStaticAddrHtlcBackupFeePercentage: defaultMaxStaticAddrHtlcBackupFeePercentage, + EnableExperimental: false, + MigrationRPCBatchSize: defaultRPCBatchSize, Lnd: &lndConfig{ Host: "localhost:10009", MacaroonPath: DefaultLndMacaroonPath, diff --git a/loopd/utils.go b/loopd/utils.go index e6c7547f4..2aa4083a2 100644 --- a/loopd/utils.go +++ b/loopd/utils.go @@ -40,16 +40,18 @@ func getClient(cfg *Config, swapDb loopdb.SwapStore, } clientConfig := &loop.ClientConfig{ - ServerAddress: cfg.Server.Host, - ProxyAddress: cfg.Server.Proxy, - SwapServerNoTLS: cfg.Server.NoTLS, - TLSPathServer: cfg.Server.TLSPath, - Lnd: lnd, - MaxL402Cost: btcutil.Amount(cfg.MaxL402Cost), - MaxL402Fee: btcutil.Amount(cfg.MaxL402Fee), - LoopOutMaxParts: cfg.LoopOutMaxParts, - TotalPaymentTimeout: cfg.TotalPaymentTimeout, - MaxPaymentRetries: cfg.MaxPaymentRetries, + ServerAddress: cfg.Server.Host, + ProxyAddress: cfg.Server.Proxy, + SwapServerNoTLS: cfg.Server.NoTLS, + TLSPathServer: cfg.Server.TLSPath, + Lnd: lnd, + MaxL402Cost: btcutil.Amount(cfg.MaxL402Cost), + MaxL402Fee: btcutil.Amount(cfg.MaxL402Fee), + LoopOutMaxParts: cfg.LoopOutMaxParts, + TotalPaymentTimeout: cfg.TotalPaymentTimeout, + MaxPaymentRetries: cfg.MaxPaymentRetries, + MaxStaticAddrHtlcFeePercentage: cfg.MaxStaticAddrHtlcFeePercentage, + MaxStaticAddrHtlcBackupFeePercentage: cfg.MaxStaticAddrHtlcBackupFeePercentage, } if cfg.MaxL402Cost == defaultCost && cfg.MaxLSATCost != 0 { From 9ad60e48f1dd543b91f27ff221d66da9eee18699 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Wed, 30 Oct 2024 13:21:21 +0100 Subject: [PATCH 09/14] staticaddr: loopin --- staticaddr/loopin/loopin.go | 590 ++++++++++++++++++++++++++++++++++++ 1 file changed, 590 insertions(+) create mode 100644 staticaddr/loopin/loopin.go diff --git a/staticaddr/loopin/loopin.go b/staticaddr/loopin/loopin.go new file mode 100644 index 000000000..4318c4b6e --- /dev/null +++ b/staticaddr/loopin/loopin.go @@ -0,0 +1,590 @@ +package loopin + +import ( + "context" + "errors" + "fmt" + "reflect" + "sync" + "time" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/staticaddr/address" + "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/script" + "github.com/lightninglabs/loop/staticaddr/version" + "github.com/lightninglabs/loop/swap" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/zpay32" +) + +// StaticAddressLoopIn represents the in-memory loop-in information. +type StaticAddressLoopIn struct { + // SwapHash is the hashed preimage of the swap invoice. It represents + // the primary identifier of the swap. + SwapHash lntypes.Hash + + // SwapPreimage is the preimage that is used for the swap. + SwapPreimage lntypes.Preimage + + // HtlcCltvExpiry is the expiry of the swap. + HtlcCltvExpiry int32 + + // MaxSwapFee is the swap fee in sats that the user accepted when + // initiating the swap. It is the upper limit for the QuotedSwapFee. + MaxSwapFee btcutil.Amount + + // InitiationHeight is the height at which the swap was initiated. + InitiationHeight uint32 + + // InitiationTime is the time at which the swap was initiated. + InitiationTime time.Time + + // ProtocolVersion is the protocol version of the static address. + ProtocolVersion version.AddressProtocolVersion + + // Label contains an optional label for the swap. + Label string + + // Htlc key fields. + + // ClientPubkey is the pubkey of the client that is used for the swap. + ClientPubkey *btcec.PublicKey + + // ServerPubkey is the pubkey of the server that is used for the swap. + ServerPubkey *btcec.PublicKey + + // HtlcKeyLocator is the locator of the server's htlc key. + HtlcKeyLocator keychain.KeyLocator + + // Static address loop-in fields. + + // SwapInvoice is the invoice that needs to be paid by the server to + // complete the loop-in swap. + SwapInvoice string + + // LastHop is an optional parameter that specifies the last hop to be + // used for a loop in swap. + LastHop []byte + + // The swap payment timeout allows the user to specify an upper limit + // for the amount of time the server is allowed to take to fulfill the + // off-chain swap payment. If the timeout is reached the swap will be + // aborted on the server side and the client can retry the swap with + // different parameters. + PaymentTimeoutSeconds uint32 + + // QuotedSwapFee is the swap fee in sats that the server returned in the + // swap quote. + QuotedSwapFee btcutil.Amount + + // The outpoints in the format txid:vout that are part of the loop-in + // swap. + DepositOutpoints []string + + // state is the current state of the swap. + state fsm.StateType + + // Non-persistent convenience fields. + + // Initiator is an optional identification string that will be appended + // to the user agent string sent to the server to give information about + // the usage of loop. This initiator part is meant for user interfaces + // to add their name to give the full picture of the binary used + // (loopd, lit) and the method used for triggering the swap + // (loop cli, autolooper, lit ui, other 3rd party ui). + Initiator string + + // Private indicates whether the destination node should be considered + // private. In which case, loop will generate hop hints to assist with + // probing and payment. + Private bool + + // Optional route hints to reach the destination through private + // channels. + RouteHints [][]zpay32.HopHint + + // Deposits are the deposits that are part of the loop-in swap. They + // implicitly carry the swap amount. + Deposits []*deposit.Deposit + + // AddressParams are the parameters of the address that is used for the + // swap. + AddressParams *address.Parameters + + // Address is the address script that is used for the swap. + Address *script.StaticAddress + + // HTLC fields. + + // HtlcTxFeeRate is the fee rate that is used for the htlc transaction. + HtlcTxFeeRate chainfee.SatPerKWeight + + // HtlcTxHighFeeRate is the fee rate that is used for the htlc + // transaction. + HtlcTxHighFeeRate chainfee.SatPerKWeight + + // HtlcTxExtremelyHighFeeRate is the fee rate that is used for the htlc + // transaction. + HtlcTxExtremelyHighFeeRate chainfee.SatPerKWeight + + // HtlcTimeoutSweepTxHash is the hash of the htlc timeout sweep tx. + HtlcTimeoutSweepTxHash *chainhash.Hash + + // HtlcTimeoutSweepAddress + HtlcTimeoutSweepAddress btcutil.Address + + mu sync.Mutex +} + +func (l *StaticAddressLoopIn) getHtlc(chainParams *chaincfg.Params) (*swap.Htlc, + error) { + + return swap.NewHtlcV2( + l.HtlcCltvExpiry, pubkeyTo33ByteSlice(l.ClientPubkey), + pubkeyTo33ByteSlice(l.ServerPubkey), l.SwapHash, chainParams, + ) +} + +// 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), + ) +} + +// createSweeplessSweepTx creates the sweepless sweep transaction that the +// server wishes to publish. It spends the deposit outpoints to a +// server-specified sweep address. +func (l *StaticAddressLoopIn) createSweeplessSweepTx(address btcutil.Address, + feeRate chainfee.SatPerKWeight) (*wire.MsgTx, error) { + // Create the tx. + msgTx := wire.NewMsgTx(2) + + // Add the deposit inputs to the transaction in the order the server + // signed them. + outpoints := l.Outpoints() + for _, outpoint := range outpoints { + msgTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: outpoint, + }) + } + + // Calculate tx fee for the server provided fee rate. + weight, err := sweeplessSweepWeight(len(outpoints), address) + if err != nil { + return nil, err + } + fee := feeRate.FeeForWeight(weight) + + pkscript, err := txscript.PayToAddrScript(address) + if err != nil { + return nil, err + } + + // Create the sweep output. + sweepOutput := &wire.TxOut{ + Value: int64(l.TotalDepositAmount()) - int64(fee), + PkScript: pkscript, + } + + msgTx.AddTxOut(sweepOutput) + + return msgTx, nil +} + +// sweeplessSweepWeight returns weight units for a sweepless sweep transaction +// with N taproot inputs and one sweep output. +func sweeplessSweepWeight(numInputs int, + sweepAddress btcutil.Address) (lntypes.WeightUnit, error) { + + var weightEstimator input.TxWeightEstimator + for i := 0; i < numInputs; i++ { + weightEstimator.AddTaprootKeySpendInput( + txscript.SigHashDefault, + ) + } + + // Get the weight of the sweep output. + switch sweepAddress.(type) { + case *btcutil.AddressWitnessPubKeyHash: + weightEstimator.AddP2WKHOutput() + + case *btcutil.AddressTaproot: + weightEstimator.AddP2TROutput() + + default: + return 0, fmt.Errorf("invalid sweep address type %T", + sweepAddress) + } + + return weightEstimator.Weight(), nil +} + +// signMusig2Tx adds the server nonces to the musig2 sessions and signs the +// transaction. +func (l *StaticAddressLoopIn) signMusig2Tx(ctx context.Context, + tx *wire.MsgTx, signer lndclient.SignerClient, + musig2sessions []*input.MuSig2SessionInfo, + counterPartyNonces [][musig2.PubNonceSize]byte) ([][]byte, error) { + + prevOuts, err := l.toPrevOuts(l.Deposits, l.AddressParams.PkScript) + if err != nil { + return nil, err + } + prevOutFetcher := txscript.NewMultiPrevOutFetcher(prevOuts) + + outpoints := l.Outpoints() + sigHashes := txscript.NewTxSigHashes(tx, prevOutFetcher) + sigs := make([][]byte, len(outpoints)) + + for idx, outpoint := range outpoints { + if !reflect.DeepEqual(tx.TxIn[idx].PreviousOutPoint, + outpoint) { + + return nil, fmt.Errorf("tx input does not match " + + "deposits") + } + + 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. + haveAllNonces, err := signer.MuSig2RegisterNonces( + ctx, musig2sessions[idx].SessionID, + [][musig2.PubNonceSize]byte{counterPartyNonces[idx]}, + ) + if err != nil { + return nil, err + } + + // Sanity check that we have all the nonces. + if !haveAllNonces { + return nil, fmt.Errorf("invalid MuSig2 session: " + + "nonces missing") + } + + // 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, + ) + if err != nil { + return nil, err + } + + sigs[idx] = sig + } + + return sigs, nil +} + +// createHtlcTx creates the transaction that spend the deposit outpoints into +// a htlc outpoint. +func (l *StaticAddressLoopIn) createHtlcTx(chainParams *chaincfg.Params, + feeRate chainfee.SatPerKWeight, maxFeePercentage float64) (*wire.MsgTx, + error) { + + // First Create the tx. + msgTx := wire.NewMsgTx(2) + + // Add the deposit inputs to the transaction in the order the server + // signed them. + outpoints := l.Outpoints() + for _, outpoint := range outpoints { + msgTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: outpoint, + }) + } + + // Calculate htlc tx fee for server provided fee rate. + weight := l.htlcWeight() + fee := feeRate.FeeForWeight(weight) + + // Check if the server breaches our fee limits. + amt := float64(l.TotalDepositAmount()) + feeLimit := btcutil.Amount(amt * maxFeePercentage) + + if fee > feeLimit { + return nil, fmt.Errorf("htlc tx fee %v exceeds max fee %v", + fee, feeLimit) + } + + htlc, err := l.getHtlc(chainParams) + if err != nil { + return nil, err + } + + pkscript, err := txscript.PayToAddrScript(htlc.Address) + if err != nil { + return nil, err + } + + // Create the sweep output + sweepOutput := &wire.TxOut{ + Value: int64(l.TotalDepositAmount()) - int64(fee), + PkScript: pkscript, + } + + msgTx.AddTxOut(sweepOutput) + + return msgTx, nil +} + +// isHtlcTimedOut returns true if the htlc's cltv expiry is reached. In this +// case the client is able to broadcast a timeout refund transaction that can be +// included in blocks greater than height. +func (l *StaticAddressLoopIn) isHtlcTimedOut(height int32) bool { + return height >= l.HtlcCltvExpiry +} + +// htlcWeight returns the weight for the htlc transaction. +func (l *StaticAddressLoopIn) htlcWeight() lntypes.WeightUnit { + var weightEstimator input.TxWeightEstimator + for i := 0; i < len(l.Deposits); i++ { + weightEstimator.AddTaprootKeySpendInput( + txscript.SigHashDefault, + ) + } + + weightEstimator.AddP2WSHOutput() + + return weightEstimator.Weight() +} + +// createHtlcSweepTx creates the htlc sweep transaction for the timeout path of +// the loop-in htlc. +func (l *StaticAddressLoopIn) createHtlcSweepTx(ctx context.Context, + signer lndclient.SignerClient, sweepAddress btcutil.Address, + feeRate chainfee.SatPerKWeight, network *chaincfg.Params, + blockHeight uint32, maxFeePercentage float64) (*wire.MsgTx, error) { + + if network == nil { + return nil, errors.New("no network provided") + } + + htlc, err := l.getHtlc(network) + if err != nil { + return nil, err + } + + // Create the sweep transaction. + sweepTx := wire.NewMsgTx(2) + sweepTx.LockTime = blockHeight + + var weightEstimator input.TxWeightEstimator + weightEstimator.AddP2TROutput() + + err = htlc.AddSuccessToEstimator(&weightEstimator) + if err != nil { + return nil, err + } + + htlcTx, err := l.createHtlcTx( + network, l.HtlcTxFeeRate, maxFeePercentage, + ) + if err != nil { + return nil, err + } + + // Add the htlc input. + sweepTx.AddTxIn(&wire.TxIn{ + PreviousOutPoint: wire.OutPoint{ + Hash: htlcTx.TxHash(), + Index: 0, + }, + SignatureScript: htlc.SigScript, + Sequence: htlc.SuccessSequence(), + }) + + // Add the sweep output. + sweepPkScript, err := txscript.PayToAddrScript(sweepAddress) + if err != nil { + return nil, err + } + + fee := feeRate.FeeForWeight(weightEstimator.Weight()) + + htlcOutValue := htlcTx.TxOut[0].Value + output := &wire.TxOut{ + Value: htlcOutValue - int64(fee), + PkScript: sweepPkScript, + } + + sweepTx.AddTxOut(output) + + signDesc := lndclient.SignDescriptor{ + WitnessScript: htlc.TimeoutScript(), + Output: &wire.TxOut{ + Value: htlcOutValue, + PkScript: htlc.PkScript, + }, + HashType: htlc.SigHash(), + InputIndex: 0, + KeyDesc: keychain.KeyDescriptor{ + KeyLocator: l.HtlcKeyLocator, + }, + } + signDesc.SignMethod = input.WitnessV0SignMethod + + rawSigs, err := signer.SignOutputRaw( + ctx, sweepTx, []*lndclient.SignDescriptor{&signDesc}, nil, + ) + if err != nil { + return nil, fmt.Errorf("sign output error: %w", err) + } + sig := rawSigs[0] + + // Add witness stack to the tx input. + sweepTx.TxIn[0].Witness, err = htlc.GenTimeoutWitness(sig) + if err != nil { + return nil, err + } + + return sweepTx, nil +} + +// pubkeyTo33ByteSlice converts a pubkey to a 33 byte slice. +func pubkeyTo33ByteSlice(pubkey *btcec.PublicKey) [33]byte { + var pubkeyBytes [33]byte + copy(pubkeyBytes[:], pubkey.SerializeCompressed()) + + return pubkeyBytes +} + +// TotalDepositAmount returns the total amount of the deposits. +func (l *StaticAddressLoopIn) TotalDepositAmount() btcutil.Amount { + var total btcutil.Amount + if len(l.Deposits) == 0 { + return total + } + + for _, d := range l.Deposits { + total += d.Value + } + return total +} + +// RemainingPaymentTimeSeconds returns the remaining time in seconds until the +// payment timeout is reached. The remaining time is calculated from the +// initiation time of the swap. If more than the swaps configured payment +// timeout has passed, the remaining time will be negative. +func (l *StaticAddressLoopIn) RemainingPaymentTimeSeconds() int64 { + elapsedSinceInitiation := time.Since(l.InitiationTime).Seconds() + + return int64(l.PaymentTimeoutSeconds) - int64(elapsedSinceInitiation) +} + +// Outpoints returns the wire outpoints of the deposits. +func (l *StaticAddressLoopIn) Outpoints() []wire.OutPoint { + outpoints := make([]wire.OutPoint, len(l.Deposits)) + for i, d := range l.Deposits { + outpoints[i] = wire.OutPoint{ + Hash: d.Hash, + Index: d.Index, + } + } + + 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() + defer l.mu.Unlock() + + return l.state +} + +// SetState sets the current state of the loop-in swap. +func (l *StaticAddressLoopIn) SetState(state fsm.StateType) { + l.mu.Lock() + defer l.mu.Unlock() + + l.state = state +} + +// IsInState returns true if the deposit is in the given state. +func (l *StaticAddressLoopIn) IsInState(state fsm.StateType) bool { + l.mu.Lock() + defer l.mu.Unlock() + + return l.state == state +} From 9424b04d73bdd910a185a98408bdc74f33be2e8d Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Wed, 30 Oct 2024 13:20:52 +0100 Subject: [PATCH 10/14] staticaddr: sql_store --- staticaddr/loopin/sql_store.go | 370 +++++++++++++++++++++++++++++++++ 1 file changed, 370 insertions(+) create mode 100644 staticaddr/loopin/sql_store.go diff --git a/staticaddr/loopin/sql_store.go b/staticaddr/loopin/sql_store.go new file mode 100644 index 000000000..06caae632 --- /dev/null +++ b/staticaddr/loopin/sql_store.go @@ -0,0 +1,370 @@ +package loopin + +import ( + "context" + "database/sql" + "errors" + "strings" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/loopdb" + "github.com/lightninglabs/loop/loopdb/sqlc" + "github.com/lightninglabs/loop/staticaddr/version" + "github.com/lightningnetwork/lnd/clock" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" +) + +const outpointSeparator = ";" + +var ( + // ErrInvalidOutpoint is returned when an outpoint contains the outpoint + // separator. + ErrInvalidOutpoint = errors.New("outpoint contains outpoint separator") +) + +// Querier is the interface that contains all the queries generated by sqlc for +// the static_address_swaps table. +type Querier interface { + // InsertSwap inserts a new base swap. + InsertSwap(ctx context.Context, arg sqlc.InsertSwapParams) error + + // InsertHtlcKeys inserts the htlc keys for a swap. + InsertHtlcKeys(ctx context.Context, arg sqlc.InsertHtlcKeysParams) error + + // InsertStaticAddressLoopIn inserts a new static address loop-in swap. + InsertStaticAddressLoopIn(ctx context.Context, + arg sqlc.InsertStaticAddressLoopInParams) error + + // InsertStaticAddressMetaUpdate inserts metadata about loop-in + // updates. + InsertStaticAddressMetaUpdate(ctx context.Context, + arg sqlc.InsertStaticAddressMetaUpdateParams) error + + // UpdateStaticAddressLoopIn updates a loop-in swap. + UpdateStaticAddressLoopIn(ctx context.Context, + arg sqlc.UpdateStaticAddressLoopInParams) error + + // GetStaticAddressLoopInSwap retrieves a loop-in swap by its swap hash. + GetStaticAddressLoopInSwap(ctx context.Context, + swapHash []byte) (sqlc.GetStaticAddressLoopInSwapRow, error) + + // GetStaticAddressLoopInSwapsByStates retrieves all swaps with the + // given states. The states string is an input for the IN primitive in + // sqlite, hence the format needs to be '{State1,State2,...}'. + GetStaticAddressLoopInSwapsByStates(ctx context.Context, + states sql.NullString) ([]sqlc.GetStaticAddressLoopInSwapsByStatesRow, + error) + + // GetLoopInSwapUpdates retrieves all updates for a loop-in swap. + GetLoopInSwapUpdates(ctx context.Context, + swapHash []byte) ([]sqlc.StaticAddressSwapUpdate, error) + + // IsStored returns true if a swap with the given hash is stored in the + // database, false otherwise. + IsStored(ctx context.Context, swapHash []byte) (bool, error) +} + +// BaseDB is the interface that contains all the queries generated by sqlc for +// the static_address_swaps table and transaction functionality. +type BaseDB interface { + Querier + + // ExecTx allows for executing a function in the context of a database + // transaction. + ExecTx(ctx context.Context, txOptions loopdb.TxOptions, + txBody func(Querier) error) error +} + +// SqlStore is the backing store for static address loop-ins. +type SqlStore struct { + baseDB BaseDB + clock clock.Clock + network *chaincfg.Params +} + +// NewSqlStore constructs a new SQLStore from a BaseDB. The BaseDB is agnostic +// to the underlying driver which can be postgres or sqlite. +func NewSqlStore(db BaseDB, clock clock.Clock, + network *chaincfg.Params) *SqlStore { + + return &SqlStore{ + baseDB: db, + clock: clock, + network: network, + } +} + +// GetStaticAddressLoopInSwapsByStates returns all static address loop-ins from +// the db that are in the given states. +func (s *SqlStore) GetStaticAddressLoopInSwapsByStates(ctx context.Context, + states []fsm.StateType) ([]*StaticAddressLoopIn, error) { + + var ( + err error + rows []sqlc.GetStaticAddressLoopInSwapsByStatesRow + updates []sqlc.StaticAddressSwapUpdate + loopIn *StaticAddressLoopIn + ) + joinedStates := toJointStringStates(states) + joinedNullStringStates := sql.NullString{ + String: joinedStates, + Valid: joinedStates != "", + } + rows, err = s.baseDB.GetStaticAddressLoopInSwapsByStates( + ctx, joinedNullStringStates, + ) + if err != nil { + return nil, err + } + + loopIns := make([]*StaticAddressLoopIn, 0, len(rows)) + for _, row := range rows { + updates, err = s.baseDB.GetLoopInSwapUpdates( + ctx, row.SwapHash, + ) + if err != nil { + return nil, err + } + + loopIn, err = toStaticAddressLoopIn( + ctx, s.network, sqlc.GetStaticAddressLoopInSwapRow(row), + updates, + ) + if err != nil { + return nil, err + } + + loopIns = append(loopIns, loopIn) + } + + return loopIns, nil +} + +func toJointStringStates(states []fsm.StateType) string { + return "{" + strings.Join(toStrings(states), ",") + "}" +} + +func toStrings(states []fsm.StateType) []string { + stringStates := make([]string, len(states)) + for i, state := range states { + stringStates[i] = string(state) + } + + return stringStates +} + +// CreateLoopIn inserts a new loop-in swap into the database. Basic loop-in +// parameters are stored in the swaps table, htlc key information is stored in +// the htlc_keys table, and loop-in specific information is stored in the +// static_address_swaps table. +func (s *SqlStore) CreateLoopIn(ctx context.Context, + loopIn *StaticAddressLoopIn) error { + + swapArgs := sqlc.InsertSwapParams{ + SwapHash: loopIn.SwapHash[:], + Preimage: loopIn.SwapPreimage[:], + InitiationTime: loopIn.InitiationTime, + AmountRequested: int64(loopIn.TotalDepositAmount()), + CltvExpiry: loopIn.HtlcCltvExpiry, + MaxSwapFee: int64(loopIn.MaxSwapFee), + InitiationHeight: int32(loopIn.InitiationHeight), + ProtocolVersion: int32(loopIn.ProtocolVersion), + Label: loopIn.Label, + } + + htlcKeyArgs := sqlc.InsertHtlcKeysParams{ + SwapHash: loopIn.SwapHash[:], + SenderScriptPubkey: loopIn.ClientPubkey.SerializeCompressed(), + ReceiverScriptPubkey: loopIn.ServerPubkey.SerializeCompressed(), + ClientKeyFamily: int32(loopIn.HtlcKeyLocator.Family), + ClientKeyIndex: int32(loopIn.HtlcKeyLocator.Index), + } + + // Sanity check, if any of the outpoints contain the outpoint separator. + // If so, we reject the loop-in to prevent potential issues with + // parsing. + for _, outpoint := range loopIn.DepositOutpoints { + if strings.Contains(outpoint, outpointSeparator) { + return ErrInvalidOutpoint + } + } + + joinedOutpoints := strings.Join( + loopIn.DepositOutpoints, outpointSeparator, + ) + staticAddressLoopInParams := sqlc.InsertStaticAddressLoopInParams{ + SwapHash: loopIn.SwapHash[:], + SwapInvoice: loopIn.SwapInvoice, + LastHop: loopIn.LastHop, + QuotedSwapFeeSatoshis: int64(loopIn.QuotedSwapFee), + HtlcTimeoutSweepAddress: loopIn.HtlcTimeoutSweepAddress.String(), + HtlcTxFeeRateSatKw: int64(loopIn.HtlcTxFeeRate), + DepositOutpoints: joinedOutpoints, + PaymentTimeoutSeconds: int32(loopIn.PaymentTimeoutSeconds), + } + + updateArgs := sqlc.InsertStaticAddressMetaUpdateParams{ + SwapHash: loopIn.SwapHash[:], + UpdateTimestamp: s.clock.Now(), + UpdateState: string(loopIn.GetState()), + } + + return s.baseDB.ExecTx(ctx, loopdb.NewSqlWriteOpts(), + func(q Querier) error { + err := q.InsertSwap(ctx, swapArgs) + if err != nil { + return err + } + + err = q.InsertHtlcKeys(ctx, htlcKeyArgs) + if err != nil { + return err + } + + err = q.InsertStaticAddressLoopIn( + ctx, staticAddressLoopInParams, + ) + if err != nil { + return err + } + + return q.InsertStaticAddressMetaUpdate(ctx, updateArgs) + }) +} + +// UpdateLoopIn updates the loop-in in the database. +func (s *SqlStore) UpdateLoopIn(ctx context.Context, + loopIn *StaticAddressLoopIn) error { + + var htlcTimeoutSweepTxID string + if loopIn.HtlcTimeoutSweepTxHash != nil { + htlcTimeoutSweepTxID = loopIn.HtlcTimeoutSweepTxHash.String() + } + + updateParams := sqlc.UpdateStaticAddressLoopInParams{ + SwapHash: loopIn.SwapHash[:], + HtlcTxFeeRateSatKw: int64(loopIn.HtlcTxFeeRate), + HtlcTimeoutSweepTxID: sql.NullString{ + String: htlcTimeoutSweepTxID, + Valid: htlcTimeoutSweepTxID != "", + }, + } + + updateArgs := sqlc.InsertStaticAddressMetaUpdateParams{ + SwapHash: loopIn.SwapHash[:], + UpdateState: string(loopIn.GetState()), + UpdateTimestamp: s.clock.Now(), + } + + return s.baseDB.ExecTx(ctx, loopdb.NewSqlWriteOpts(), + func(q Querier) error { + err := q.UpdateStaticAddressLoopIn(ctx, updateParams) + if err != nil { + return err + } + + return q.InsertStaticAddressMetaUpdate(ctx, updateArgs) + }, + ) +} + +// IsStored returns true if a swap with the given hash is stored in the +// database, false otherwise. +func (s *SqlStore) IsStored(ctx context.Context, swapHash lntypes.Hash) (bool, + error) { + + return s.baseDB.IsStored(ctx, swapHash[:]) +} + +// toStaticAddressLoopIn converts sql rows to an instant out struct. +func toStaticAddressLoopIn(_ context.Context, network *chaincfg.Params, + row sqlc.GetStaticAddressLoopInSwapRow, + updates []sqlc.StaticAddressSwapUpdate) (*StaticAddressLoopIn, error) { + + swapHash, err := lntypes.MakeHash(row.SwapHash) + if err != nil { + return nil, err + } + + swapPreImage, err := lntypes.MakePreimage(row.Preimage) + if err != nil { + return nil, err + } + + clientKey, err := btcec.ParsePubKey(row.SenderScriptPubkey) + if err != nil { + return nil, err + } + + serverKey, err := btcec.ParsePubKey(row.ReceiverScriptPubkey) + if err != nil { + return nil, err + } + + var htlcTimeoutSweepTxHash *chainhash.Hash + if row.HtlcTimeoutSweepTxID.Valid { + htlcTimeoutSweepTxHash, err = chainhash.NewHashFromStr( + row.HtlcTimeoutSweepTxID.String, + ) + if err != nil { + return nil, err + } + } + + depositOutpoints := strings.Split( + row.DepositOutpoints, outpointSeparator, + ) + + timeoutAddressString := row.HtlcTimeoutSweepAddress + var timeoutAddress btcutil.Address + if timeoutAddressString != "" { + timeoutAddress, err = btcutil.DecodeAddress( + timeoutAddressString, network, + ) + if err != nil { + return nil, err + } + } + + loopIn := &StaticAddressLoopIn{ + SwapHash: swapHash, + SwapPreimage: swapPreImage, + HtlcCltvExpiry: row.CltvExpiry, + MaxSwapFee: btcutil.Amount(row.MaxSwapFee), + InitiationHeight: uint32(row.InitiationHeight), + InitiationTime: row.InitiationTime, + ProtocolVersion: version.AddressProtocolVersion( + row.ProtocolVersion, + ), + Label: row.Label, + ClientPubkey: clientKey, + ServerPubkey: serverKey, + HtlcKeyLocator: keychain.KeyLocator{ + Family: keychain.KeyFamily(row.ClientKeyFamily), + Index: uint32(row.ClientKeyIndex), + }, + SwapInvoice: row.SwapInvoice, + PaymentTimeoutSeconds: uint32(row.PaymentTimeoutSeconds), + LastHop: row.LastHop, + QuotedSwapFee: btcutil.Amount(row.QuotedSwapFeeSatoshis), + DepositOutpoints: depositOutpoints, + HtlcTxFeeRate: chainfee.SatPerKWeight( + row.HtlcTxFeeRateSatKw, + ), + HtlcTimeoutSweepAddress: timeoutAddress, + HtlcTimeoutSweepTxHash: htlcTimeoutSweepTxHash, + } + + if len(updates) > 0 { + lastUpdate := updates[len(updates)-1] + loopIn.SetState(fsm.StateType(lastUpdate.UpdateState)) + } + + return loopIn, nil +} From 4368d94068f83cc1a8e0df852f0e87c3e72db4e0 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Thu, 24 Oct 2024 12:18:02 +0200 Subject: [PATCH 11/14] staticaddr: swap manager and fsm In this commit we add the static address loop-in state machine and its orchestration through the manager. --- staticaddr/loopin/actions.go | 1069 ++++++++++++++++++++++++++++++++ staticaddr/loopin/fsm.go | 365 +++++++++++ staticaddr/loopin/interface.go | 72 +++ staticaddr/loopin/manager.go | 458 ++++++++++++++ 4 files changed, 1964 insertions(+) create mode 100644 staticaddr/loopin/actions.go create mode 100644 staticaddr/loopin/fsm.go create mode 100644 staticaddr/loopin/interface.go create mode 100644 staticaddr/loopin/manager.go diff --git a/staticaddr/loopin/actions.go b/staticaddr/loopin/actions.go new file mode 100644 index 000000000..48ff03974 --- /dev/null +++ b/staticaddr/loopin/actions.go @@ -0,0 +1,1069 @@ +package loopin + +import ( + "context" + "crypto/rand" + "errors" + "fmt" + "strings" + "time" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcwallet/chain" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop" + "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/version" + "github.com/lightninglabs/loop/swap" + looprpc "github.com/lightninglabs/loop/swapserverrpc" + "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/invoices" + "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" + "github.com/lightningnetwork/lnd/lnrpc/walletrpc" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/lnwire" +) + +const ( + defaultConfTarget = 3 + + DefaultPaymentTimeoutSeconds = 60 +) + +var ( + // ErrFeeTooHigh is returned if the server sets a fee rate for the htlc + // tx that is too high. We prevent here against a low htlc timeout sweep + // amount. + ErrFeeTooHigh = errors.New("server htlc tx fee is higher than the " + + "configured allowed maximum") + + // ErrBackupFeeTooHigh is returned if the server sets a fee rate for the + // htlc backup tx that is too high. We prevent here against a low htlc + // timeout sweep amount. + ErrBackupFeeTooHigh = errors.New("server htlc backup tx fee is " + + "higher than the configured allowed maximum") +) + +// InitHtlcAction is executed if all loop-in information has been validated. We +// assemble a loop-in request and send it to the server. +func (f *FSM) InitHtlcAction(ctx context.Context, + _ fsm.EventContext) fsm.EventType { + + // Lock the deposits and transition them to the LoopingIn state. + err := f.cfg.DepositManager.TransitionDeposits( + ctx, f.loopIn.Deposits, deposit.OnLoopInInitiated, + deposit.LoopingIn, + ) + if err != nil { + err = fmt.Errorf("unable to loop-in deposits: %w", err) + + return f.HandleError(err) + } + + // Calculate the swap invoice amount. The server needs to pay us the + // sum of all deposits minus the fees that the server charges for the + // swap. + swapInvoiceAmt := f.loopIn.TotalDepositAmount() - f.loopIn.QuotedSwapFee + + // Generate random preimage. + var swapPreimage lntypes.Preimage + if _, err = rand.Read(swapPreimage[:]); err != nil { + err = fmt.Errorf("unable to create random swap preimage: %w", + err) + + return f.HandleError(err) + } + f.loopIn.SwapPreimage = swapPreimage + f.loopIn.SwapHash = swapPreimage.Hash() + + // Derive a client key for the HTLC. + keyDesc, err := f.cfg.WalletKit.DeriveNextKey( + ctx, swap.StaticAddressKeyFamily, + ) + if err != nil { + err = fmt.Errorf("unable to derive client htlc key: %w", err) + + return f.HandleError(err) + } + f.loopIn.ClientPubkey = keyDesc.PubKey + f.loopIn.HtlcKeyLocator = keyDesc.KeyLocator + + // Create the swap invoice in lnd. + _, swapInvoice, err := f.cfg.LndClient.AddInvoice( + ctx, &invoicesrpc.AddInvoiceData{ + Preimage: &swapPreimage, + Value: lnwire.NewMSatFromSatoshis(swapInvoiceAmt), + Memo: "static address loop-in", + Expiry: 3600 * 24 * 365, + RouteHints: f.loopIn.RouteHints, + }, + ) + if err != nil { + err = fmt.Errorf("unable to create swap invoice: %w", err) + + return f.HandleError(err) + } + f.loopIn.SwapInvoice = swapInvoice + + f.loopIn.ProtocolVersion = version.AddressProtocolVersion( + version.CurrentRPCProtocolVersion(), + ) + + loopInReq := &looprpc.ServerStaticAddressLoopInRequest{ + SwapHash: f.loopIn.SwapHash[:], + DepositOutpoints: f.loopIn.DepositOutpoints, + HtlcClientPubKey: f.loopIn.ClientPubkey.SerializeCompressed(), + SwapInvoice: f.loopIn.SwapInvoice, + ProtocolVersion: version.CurrentRPCProtocolVersion(), + UserAgent: loop.UserAgent(f.loopIn.Initiator), + PaymentTimeoutSeconds: f.loopIn.PaymentTimeoutSeconds, + } + if f.loopIn.LastHop != nil { + loopInReq.LastHop = f.loopIn.LastHop + } + + loopInResp, err := f.cfg.Server.ServerStaticAddressLoopIn( + ctx, loopInReq, + ) + if err != nil { + err = fmt.Errorf("unable to initiate the loop-in with the "+ + "server: %w", err) + + return f.HandleError(err) + } + + // Pushing empty sigs signals the server that we abandoned the swap + // attempt. + pushEmptySigs := func() { + _, err = f.cfg.Server.PushStaticAddressHtlcSigs( + ctx, &looprpc.PushStaticAddressHtlcSigsRequest{ + SwapHash: f.loopIn.SwapHash[:], + }, + ) + if err != nil { + log.Warnf("unable to push htlc tx sigs to server: %w", + err) + } + } + + serverPubkey, err := btcec.ParsePubKey(loopInResp.HtlcServerPubKey) + if err != nil { + pushEmptySigs() + err = fmt.Errorf("unable to parse server pubkey: %w", err) + + return f.HandleError(err) + } + f.loopIn.ServerPubkey = serverPubkey + + // Validate if the response parameters are outside our allowed range + // preventing us from continuing with a swap. + err = f.cfg.ValidateLoopInContract( + int32(f.loopIn.InitiationHeight), loopInResp.HtlcExpiry, + ) + if err != nil { + pushEmptySigs() + err = fmt.Errorf("server response parameters are outside "+ + "our allowed range: %w", err) + + return f.HandleError(err) + } + + f.loopIn.HtlcCltvExpiry = loopInResp.HtlcExpiry + f.htlcServerNonces, err = toNonces(loopInResp.StandardHtlcInfo.Nonces) + if err != nil { + pushEmptySigs() + err = fmt.Errorf("unable to convert server nonces: %w", err) + + return f.HandleError(err) + } + f.htlcServerNoncesHighFee, err = toNonces( + loopInResp.HighFeeHtlcInfo.Nonces, + ) + if err != nil { + pushEmptySigs() + + return f.HandleError(err) + } + f.htlcServerNoncesExtremelyHighFee, err = toNonces( + loopInResp.ExtremeFeeHtlcInfo.Nonces, + ) + if err != nil { + pushEmptySigs() + + return f.HandleError(err) + } + + // We need to defend against the server setting high fees for the htlc + // tx since we might have to sweep the timeout path. We maximally allow + // a configured percentage of the swap value to be spent on fees. + amt := float64(f.loopIn.TotalDepositAmount()) + maxHtlcTxFee := btcutil.Amount(amt * + f.cfg.MaxStaticAddrHtlcFeePercentage) + + maxHtlcTxBackupFee := btcutil.Amount(amt * + f.cfg.MaxStaticAddrHtlcBackupFeePercentage) + + feeRate := chainfee.SatPerKWeight(loopInResp.StandardHtlcInfo.FeeRate) + fee := feeRate.FeeForWeight(f.loopIn.htlcWeight()) + if fee > maxHtlcTxFee { + // Abort the swap by pushing empty sigs to the server. + pushEmptySigs() + + log.Errorf("server htlc tx fee is higher than the configured "+ + "allowed maximum: %v > %v", fee, maxHtlcTxFee) + + return f.HandleError(ErrFeeTooHigh) + } + f.loopIn.HtlcTxFeeRate = feeRate + + highFeeRate := chainfee.SatPerKWeight(loopInResp.HighFeeHtlcInfo.FeeRate) + fee = highFeeRate.FeeForWeight(f.loopIn.htlcWeight()) + if fee > maxHtlcTxBackupFee { + // Abort the swap by pushing empty sigs to the server. + pushEmptySigs() + + log.Errorf("server htlc backup tx fee is higher than the "+ + "configured allowed maximum: %v > %v", fee, + maxHtlcTxBackupFee) + + return f.HandleError(ErrFeeTooHigh) + } + f.loopIn.HtlcTxHighFeeRate = highFeeRate + + extremelyHighFeeRate := chainfee.SatPerKWeight( + loopInResp.ExtremeFeeHtlcInfo.FeeRate, + ) + fee = extremelyHighFeeRate.FeeForWeight(f.loopIn.htlcWeight()) + if fee > maxHtlcTxBackupFee { + // Abort the swap by pushing empty sigs to the server. + pushEmptySigs() + + log.Errorf("server htlc backup tx fee is higher than the "+ + "configured allowed maximum: %v > %v", fee, + maxHtlcTxBackupFee) + + return f.HandleError(ErrFeeTooHigh) + } + f.loopIn.HtlcTxExtremelyHighFeeRate = extremelyHighFeeRate + + // Derive the sweep address for the htlc timeout sweep tx. + sweepAddress, err := f.cfg.WalletKit.NextAddr( + ctx, lnwallet.DefaultAccountName, + walletrpc.AddressType_TAPROOT_PUBKEY, false, + ) + if err != nil { + pushEmptySigs() + err = fmt.Errorf("unable to derive htlc timeout sweep "+ + "address: %w", err) + + return f.HandleError(err) + } + f.loopIn.HtlcTimeoutSweepAddress = sweepAddress + + // Once the htlc tx is initiated, we store the loop-in in the database. + err = f.cfg.Store.CreateLoopIn(ctx, f.loopIn) + if err != nil { + pushEmptySigs() + err = fmt.Errorf("unable to store loop-in in db: %w", err) + + return f.HandleError(err) + } + + return OnHtlcInitiated +} + +// SignHtlcTxAction is called if the htlc was initialized and the server +// provided the necessary information to construct the htlc tx. We sign the htlc +// tx and send the signatures to the server. +func (f *FSM) SignHtlcTxAction(ctx context.Context, + _ fsm.EventContext) fsm.EventType { + + var err error + + f.loopIn.AddressParams, err = + f.cfg.AddressManager.GetStaticAddressParameters(ctx) + + if err != nil { + err = fmt.Errorf("unable to get static address parameters: "+ + "%w", err) + + return f.HandleError(err) + } + + f.loopIn.Address, err = f.cfg.AddressManager.GetStaticAddress(ctx) + if err != nil { + err = fmt.Errorf("unable to get static address: %w", err) + + return f.HandleError(err) + } + + // 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) + if err != nil { + err = fmt.Errorf("unable to create musig2 sessions: %w", err) + + return f.HandleError(err) + } + defer f.cleanUpSessions(ctx, htlcSessions) + + htlcSessionsHighFee, highFeeNonces, err := createSession( + ctx, f.cfg.Signer, + ) + if err != nil { + return f.HandleError(err) + } + defer f.cleanUpSessions(ctx, htlcSessionsHighFee) + + htlcSessionsExtremelyHighFee, extremelyHighNonces, err := createSession( + ctx, f.cfg.Signer, + ) + if err != nil { + err = fmt.Errorf("unable to convert nonces: %w", err) + return f.HandleError(err) + } + defer f.cleanUpSessions(ctx, htlcSessionsExtremelyHighFee) + + // Create the htlc txns for different fee rates. + htlcTx, err := f.loopIn.createHtlcTx( + f.cfg.ChainParams, f.loopIn.HtlcTxFeeRate, + f.cfg.MaxStaticAddrHtlcFeePercentage, + ) + if err != nil { + return f.HandleError(err) + } + htlcTxHighFee, err := f.loopIn.createHtlcTx( + f.cfg.ChainParams, f.loopIn.HtlcTxHighFeeRate, + f.cfg.MaxStaticAddrHtlcBackupFeePercentage, + ) + if err != nil { + return f.HandleError(err) + } + htlcTxExtremelyHighFee, err := f.loopIn.createHtlcTx( + f.cfg.ChainParams, f.loopIn.HtlcTxExtremelyHighFeeRate, + f.cfg.MaxStaticAddrHtlcBackupFeePercentage, + ) + if err != nil { + err = fmt.Errorf("unable to create the htlc tx: %w", err) + return f.HandleError(err) + } + + // Next we'll get our htlc tx signatures for different fee rates. + htlcSigs, err := f.loopIn.signMusig2Tx( + ctx, htlcTx, f.cfg.Signer, htlcSessions, f.htlcServerNonces, + ) + if err != nil { + err = fmt.Errorf("unable to sign htlc tx: %w", err) + return f.HandleError(err) + } + + htlcSigsHighFee, err := f.loopIn.signMusig2Tx( + ctx, htlcTxHighFee, f.cfg.Signer, htlcSessionsHighFee, + f.htlcServerNoncesHighFee, + ) + if err != nil { + return f.HandleError(err) + } + htlcSigsExtremelyHighFee, err := f.loopIn.signMusig2Tx( + ctx, htlcTxExtremelyHighFee, f.cfg.Signer, + htlcSessionsExtremelyHighFee, f.htlcServerNoncesExtremelyHighFee, + ) + if err != nil { + return f.HandleError(err) + } + + // Push htlc tx sigs to server. + pushHtlcReq := &looprpc.PushStaticAddressHtlcSigsRequest{ + SwapHash: f.loopIn.SwapHash[:], + StandardHtlcInfo: &looprpc.ClientHtlcSigningInfo{ + Nonces: clientHtlcNonces, + Sigs: htlcSigs, + }, + HighFeeHtlcInfo: &looprpc.ClientHtlcSigningInfo{ + Nonces: highFeeNonces, + Sigs: htlcSigsHighFee, + }, + ExtremeFeeHtlcInfo: &looprpc.ClientHtlcSigningInfo{ + Nonces: extremelyHighNonces, + Sigs: htlcSigsExtremelyHighFee, + }, + } + _, err = f.cfg.Server.PushStaticAddressHtlcSigs(ctx, pushHtlcReq) + if err != nil { + err = fmt.Errorf("unable to push htlc tx sigs to server: %w", + err) + + return f.HandleError(err) + } + + // Note: + // From here on we need to monitor for the htlc tx hitting the chain + // until the invoice is settled because the server can now publish the + // htlc tx without paying the invoice. In this case we need to wait till + // the htlc times out and then sweep it back to us. + return OnHtlcTxSigned +} + +// cleanUpSessions releases allocated memory of the musig2 sessions. +func (f *FSM) cleanUpSessions(ctx context.Context, + sessions []*input.MuSig2SessionInfo) { + + for _, s := range sessions { + err := f.cfg.Signer.MuSig2Cleanup(ctx, s.SessionID) + if err != nil { + f.Warnf("unable to cleanup musig2 session: %v", err) + } + } +} + +// MonitorInvoiceAndHtlcTxAction is called after the htlc tx has been signed by +// us. The server from here on has the ability to publish the htlc tx. If the +// server publishes the htlc tx without paying the invoice, we have to monitor +// for the timeout path and sweep the funds back to us. If, while waiting for +// the htlc timeout, our invoice gets paid, the swap is considered successful, +// and we can stop monitoring the htlc confirmation and continue to sign the +// sweepless sweep. +func (f *FSM) MonitorInvoiceAndHtlcTxAction(ctx context.Context, + _ fsm.EventContext) fsm.EventType { + + // Subscribe to the state of the swap invoice. If upon restart recovery, + // we land here and observe that the invoice is already canceled, it can + // only be the case where a user-provided payment timeout was hit, the + // invoice got canceled and the timeout of the htlc was not reached yet. + // So we want to wait until the htlc timeout path opens up so that we + // could sweep the funds back to us if the server published it without + // paying the invoice. + subscribeCtx, cancelInvoiceSubscription := context.WithCancel(ctx) + defer cancelInvoiceSubscription() + + invoiceUpdateChan, invoiceErrChan, err := + f.cfg.InvoicesClient.SubscribeSingleInvoice( + subscribeCtx, f.loopIn.SwapHash, + ) + if err != nil { + err = fmt.Errorf("unable to subscribe to swap "+ + "invoice: %w", err) + + return f.HandleError(err) + } + + htlc, err := f.loopIn.getHtlc(f.cfg.ChainParams) + if err != nil { + err = fmt.Errorf("unable to get htlc: %w", err) + + return f.HandleError(err) + } + + // Subscribe to htlc tx confirmation. + reorgChan := make(chan struct{}, 1) + registerHtlcConf := func() (chan *chainntnfs.TxConfirmation, chan error, + error) { + + return f.cfg.ChainNotifier.RegisterConfirmationsNtfn( + ctx, nil, htlc.PkScript, defaultConfTarget, + int32(f.loopIn.InitiationHeight), + lndclient.WithReOrgChan(reorgChan), + ) + } + + htlcConfChan, htlcErrConfChan, err := registerHtlcConf() + if err != nil { + err = fmt.Errorf("unable to monitor htlc tx confirmation: %w", + err) + + return f.HandleError(err) + } + + // Subscribe to new blocks. + registerBlocks := f.cfg.ChainNotifier.RegisterBlockEpochNtfn + blockChan, blockChanErr, err := registerBlocks(ctx) + if err != nil { + err = fmt.Errorf("unable to subscribe to new blocks: %w", err) + + return f.HandleError(err) + } + + htlcConfirmed := false + + invoice, err := f.cfg.LndClient.LookupInvoice(ctx, f.loopIn.SwapHash) + if err != nil { + err = fmt.Errorf("unable to look up invoice by swap hash: %w", + err) + + return f.HandleError(err) + } + + // Create the swap payment timeout timer. If it runs out we cancel the + // invoice, but keep monitoring the htlc confirmation. + // If the invoice was canceled, e.g. before a restart, we don't need to + // set a new deadline. + var deadlineChan <-chan time.Time + if invoice.State != invoices.ContractCanceled { + // If the invoice is still live we set the timeout to the + // remaining payment time. If too much time has elapsed, e.g. + // after a restart, we set the timeout to 0 to cancel the + // invoice and unlock the deposits immediately. + remainingTimeSeconds := f.loopIn.RemainingPaymentTimeSeconds() + + // If the invoice isn't cancelled yet and the payment timeout + // elapsed, we set the timeout to 0 to cancel the invoice and + // unlock the deposits immediately. Otherwise, we start the + // timer with the remaining seconds to timeout. + timeout := time.Duration(0) * time.Second + if remainingTimeSeconds > 0 { + timeout = time.Duration(remainingTimeSeconds) * + time.Second + } + + deadlineChan = time.NewTimer(timeout).C + } else { + // If the invoice was canceled previously we end our + // subscription to invoice updates. + cancelInvoiceSubscription() + } + + cancelInvoice := func() { + f.Errorf("timeout waiting for invoice to be " + + "paid, canceling invoice") + + // Cancel the lndclient invoice subscription. + cancelInvoiceSubscription() + + err = f.cfg.InvoicesClient.CancelInvoice(ctx, f.loopIn.SwapHash) + if err != nil { + f.Warnf("unable to cancel invoice "+ + "for swap hash: %v", err) + } + } + + for { + select { + case <-htlcConfChan: + f.Infof("htlc tx confirmed") + + htlcConfirmed = true + + case err = <-htlcErrConfChan: + f.Errorf("htlc tx conf chan error: %v", err) + + case <-reorgChan: + // A reorg happened. We invalidate a previous htlc + // confirmation and re-register for the next + // confirmation. + htlcConfirmed = false + + htlcConfChan, htlcErrConfChan, err = registerHtlcConf() + if err != nil { + f.Errorf("unable to monitor htlc tx "+ + "confirmation: %v", err) + } + + case <-deadlineChan: + // If the server didn't pay the invoice on time, we + // cancel the invoice and keep monitoring the htlc tx + // confirmation. We also need to unlock the deposits to + // re-enable them for loop-ins and withdrawals. + cancelInvoice() + + event := f.UnlockDepositsAction(ctx, nil) + if event != fsm.OnError { + f.Errorf("unable to unlock deposits after " + + "payment deadline") + } + + case currentHeight := <-blockChan: + // If the htlc is confirmed but blockChan fires before + // htlcConfChan, we would wrongfully assume that the + // htlc tx was not confirmed which would lead to + // returning OnSwapTimedOut in the code below. This in + // turn would prevent us from sweeping the htlc timeout + // path back to us. + // Hence, we delay the timeout check here by one block + // to ensure that htlcConfChan fires first. + if !f.loopIn.isHtlcTimedOut(currentHeight - 1) { + // If the htlc hasn't timed out yet, we continue + // monitoring the htlc confirmation and the + // invoice settlement. + continue + } + + f.Infof("htlc timed out at block height %v", + currentHeight) + + // If the timeout path opened up we consider the swap + // failed and cancel the invoice. + cancelInvoice() + + if !htlcConfirmed { + f.Infof("swap timed out, htlc not confirmed") + + // If the htlc hasn't confirmed but the timeout + // path opened up, and we didn't receive the + // swap payment, we consider the swap attempt to + // be failed. We cancelled the invoice, but + // don't need to unlock the deposits because + // that happened when the payment deadline was + // reached. + return OnSwapTimedOut + } + + // If the htlc has confirmed and the timeout path has + // opened up we sweep the funds back to us. + err = f.cfg.DepositManager.TransitionDeposits( + ctx, f.loopIn.Deposits, + deposit.OnSweepingHtlcTimeout, + deposit.SweepHtlcTimeout, + ) + if err != nil { + log.Errorf("unable to transition "+ + "deposits to the htlc timeout "+ + "sweeping state: %w", err) + } + + return OnSweepHtlcTimeout + + case err = <-blockChanErr: + f.Errorf("block subscription error: %v", err) + + return f.HandleError(err) + + case update := <-invoiceUpdateChan: + switch update.State { + case invoices.ContractOpen: + case invoices.ContractAccepted: + case invoices.ContractSettled: + f.Debugf("received off-chain payment update "+ + "%v", update.State) + + return OnPaymentReceived + + case invoices.ContractCanceled: + // If the invoice was canceled we only log here + // since we still need to monitor until the htlc + // timed out. + log.Warnf("invoice for swap hash %v canceled", + f.loopIn.SwapHash) + + default: + err = fmt.Errorf("unexpected invoice state %v "+ + "for swap hash %v canceled", + update.State, f.loopIn.SwapHash) + + return f.HandleError(err) + } + + case err = <-invoiceErrChan: + f.Errorf("invoice subscription error: %v", err) + + case <-ctx.Done(): + return f.HandleError(ctx.Err()) + } + } +} + +// SweepHtlcTimeoutAction is called if the server published the htlc tx without +// paying the invoice. We wait for the timeout path to open up and sweep the +// funds back to us. +func (f *FSM) SweepHtlcTimeoutAction(ctx context.Context, + _ fsm.EventContext) fsm.EventType { + + for { + err := f.createAndPublishHtlcTimeoutSweepTx(ctx) + if err == nil { + break + } + + f.Errorf("unable to create and publish htlc timeout sweep "+ + "tx: %v, retrying in %v", err, time.Hour.String()) + + select { + case <-ctx.Done(): + f.Errorf(ctx.Err().Error()) + + default: + <-time.After(1 * time.Hour) + } + } + + return OnHtlcTimeoutSweepPublished +} + +// MonitorHtlcTimeoutSweepAction is called after the htlc timeout sweep tx has +// been published. We monitor the confirmation of the htlc timeout sweep tx and +// finalize the deposits once swept. +func (f *FSM) MonitorHtlcTimeoutSweepAction(ctx context.Context, + _ fsm.EventContext) fsm.EventType { + + f.Infof("monitoring htlc timeout sweep tx %v", + f.loopIn.HtlcTimeoutSweepTxHash) + + timeoutSweepPkScript, err := txscript.PayToAddrScript( + f.loopIn.HtlcTimeoutSweepAddress, + ) + if err != nil { + err = fmt.Errorf("unable to convert timeout sweep address to "+ + "pkscript: %w", err) + + return f.HandleError(err) + } + + htlcTimeoutTxidChan, errChan, err := + f.cfg.ChainNotifier.RegisterConfirmationsNtfn( + ctx, f.loopIn.HtlcTimeoutSweepTxHash, + timeoutSweepPkScript, defaultConfTarget, + int32(f.loopIn.InitiationHeight), + ) + + if err != nil { + err = fmt.Errorf("unable to register to the htlc timeout "+ + "sweep tx: %w", err) + + return f.HandleError(err) + } + + for { + select { + case err := <-errChan: + return f.HandleError(err) + + case conf := <-htlcTimeoutTxidChan: + err = f.cfg.DepositManager.TransitionDeposits( + ctx, f.loopIn.Deposits, + deposit.OnHtlcTimeoutSwept, + deposit.HtlcTimeoutSwept, + ) + if err != nil { + err = fmt.Errorf("unable to transition the "+ + "deposits to the htlc timeout swept "+ + "state: %w", err) + + return f.HandleError(err) + } + + f.Infof("htlc timeout sweep tx got %d confirmations "+ + "at block %d", defaultConfTarget, + conf.BlockHeight-defaultConfTarget+1) + + return OnHtlcTimeoutSwept + + case <-ctx.Done(): + return f.HandleError(ctx.Err()) + } + } +} + +// PaymentReceivedAction is called if the invoice was settled. We finalize the +// deposits by transitioning them to the LoopedIn state. +func (f *FSM) PaymentReceivedAction(ctx context.Context, + _ fsm.EventContext) fsm.EventType { + + // Unlock the deposits and transition them to the LoopedIn state. + err := f.cfg.DepositManager.TransitionDeposits( + ctx, f.loopIn.Deposits, deposit.OnLoopedIn, deposit.LoopedIn, + ) + if err != nil { + err = fmt.Errorf("payment received, but unable to transition "+ + "deposits into the final state: %w", err) + + return f.HandleError(err) + } + + return OnFetchSignPushSweeplessSweepTx +} + +// FetchSignPushSweeplessSweepTxAction requests server nonces, fee rate and +// destination address for the sweepless sweep transaction. It then creates the +// sweep transaction and signs it with the server and client nonces. If signing +// succeeds it pushes the signatures to the server. +func (f *FSM) FetchSignPushSweeplessSweepTxAction(ctx context.Context, + _ fsm.EventContext) fsm.EventType { + + // Fetch the sweepless sweep tx details from server. + fetchReq := &looprpc.FetchSweeplessSweepTxRequest{ + SwapHash: f.loopIn.SwapHash[:], + } + fetchResp, err := f.cfg.Server.FetchSweeplessSweepTx(ctx, fetchReq) + if err != nil { + err = fmt.Errorf("unable to fetch sweepless sweep tx: %w", err) + + return f.HandleError(err) + } + + address, err := btcutil.DecodeAddress( + fetchResp.SweepAddr, f.cfg.ChainParams, + ) + if err != nil { + f.Warnf("unable to decode sweep address: %v", err) + } + + // Standard fee. + feeRate := chainfee.SatPerKWeight(fetchResp.StandardFeeInfo.FeeRate) + serverNonces, err := toNonces(fetchResp.StandardFeeInfo.Nonces) + if err != nil { + err = fmt.Errorf("unable to convert server nonces: %w", err) + + return f.HandleError(err) + } + + // High fee. + highFeeRate := chainfee.SatPerKWeight(fetchResp.HighFeeInfo.FeeRate) + serverHighFeeNonces, err := toNonces(fetchResp.HighFeeInfo.Nonces) + if err != nil { + err = fmt.Errorf("unable to convert high fee server "+ + "nonces: %w", err) + + return f.HandleError(err) + } + + // Extremely high fee. + extremeFeeRate := chainfee.SatPerKWeight( + fetchResp.ExtremeFeeInfo.FeeRate, + ) + serverExtremeNonces, err := toNonces( + fetchResp.ExtremeFeeInfo.Nonces, + ) + if err != nil { + err = fmt.Errorf("unable to convert extremely high fee "+ + "server nonces: %w", err) + + return f.HandleError(err) + } + + // Standard sessions. + sessions, nonces, err := f.loopIn.createMusig2Sessions( + ctx, f.cfg.Signer, + ) + if err != nil { + return f.HandleError(err) + } + clientNonces, err := toNonces(nonces) + if err != nil { + return f.HandleError(err) + } + + // High fee sessions. + highFeeSessions, highFeeClientNonces, err := + f.loopIn.createMusig2Sessions(ctx, f.cfg.Signer) + + if err != nil { + return f.HandleError(err) + } + highClientNonces, err := toNonces(highFeeClientNonces) + if err != nil { + return f.HandleError(err) + } + + // Extremely high sessions. + extremeSessions, extremeClientNonces, err := + f.loopIn.createMusig2Sessions(ctx, f.cfg.Signer) + + if err != nil { + return f.HandleError(err) + } + extremelyHighClientNonces, err := toNonces(extremeClientNonces) + if err != nil { + return f.HandleError(err) + } + + // Create standard fee. + sweepTx, err := f.loopIn.createSweeplessSweepTx(address, feeRate) + if err != nil { + err = fmt.Errorf("unable to create sweepless sweep tx: %w", err) + return f.HandleError(err) + } + + // Create high fee. + highFeeSweepTx, err := f.loopIn.createSweeplessSweepTx( + address, highFeeRate, + ) + if err != nil { + err = fmt.Errorf("unable to create high fee sweepless sweep "+ + "tx: %w", err) + + return f.HandleError(err) + } + + // Create extremely high fee. + extremelyHighFeeSweepTx, err := f.loopIn.createSweeplessSweepTx( + address, extremeFeeRate, + ) + if err != nil { + err = fmt.Errorf("unable to create extremely high fee "+ + "sweepless sweep tx: %w", err) + + return f.HandleError(err) + } + + // Sign standard. + sweeplessClientSigs, err := f.loopIn.signMusig2Tx( + ctx, sweepTx, f.cfg.Signer, sessions, serverNonces, + ) + if err != nil { + err = fmt.Errorf("unable to sign sweepless sweep tx: %w", err) + return f.HandleError(err) + } + + // Sign high fee. + highFeeSigs, err := f.loopIn.signMusig2Tx( + ctx, highFeeSweepTx, f.cfg.Signer, highFeeSessions, + serverHighFeeNonces, + ) + if err != nil { + err = fmt.Errorf("unable to sign high fee sweepless sweep "+ + "tx: %w", err) + + return f.HandleError(err) + } + + // Sign extremely high fee. + extremelyHighSigs, err := f.loopIn.signMusig2Tx( + ctx, extremelyHighFeeSweepTx, f.cfg.Signer, extremeSessions, + serverExtremeNonces, + ) + if err != nil { + err = fmt.Errorf("unable to sign extremely high fee "+ + "sweepless sweep tx: %w", err) + + return f.HandleError(err) + } + + // Push sweepless sigs to the server. + req := &looprpc.PushStaticAddressSweeplessSigsRequest{ + SwapHash: f.loopIn.SwapHash[:], + StandardSigningInfo: &looprpc.ClientSweeplessSigningInfo{ + Nonces: fromNonces(clientNonces), + Sigs: sweeplessClientSigs, + }, + HighFeeSigningInfo: &looprpc.ClientSweeplessSigningInfo{ + Nonces: fromNonces(highClientNonces), + Sigs: highFeeSigs, + }, + ExtremeFeeSigningInfo: &looprpc.ClientSweeplessSigningInfo{ + Nonces: fromNonces(extremelyHighClientNonces), + Sigs: extremelyHighSigs, + }, + } + _, err = f.cfg.Server.PushStaticAddressSweeplessSigs(ctx, req) + if err != nil { + err = fmt.Errorf("unable to push sweepless sweep sigs: %w", err) + + return f.HandleError(err) + } + + return OnSweeplessSweepSigned +} + +// UnlockDepositsAction is called if the loop-in failed and its deposits should +// be available in a future loop-in request. +func (f *FSM) UnlockDepositsAction(ctx context.Context, + _ fsm.EventContext) fsm.EventType { + + err := f.cfg.DepositManager.TransitionDeposits( + ctx, f.loopIn.Deposits, fsm.OnError, deposit.Deposited, + ) + if err != nil { + err = fmt.Errorf("unable to unlock deposits: %w", err) + + return f.HandleError(err) + } + + return fsm.OnError +} + +// createAndPublishHtlcTimeoutSweepTx creates and publishes the htlc timeout +// sweep transaction. +func (f *FSM) createAndPublishHtlcTimeoutSweepTx(ctx context.Context) error { + // Get a fee rate. + feeRate, err := f.cfg.WalletKit.EstimateFeeRate(ctx, defaultConfTarget) + if err != nil { + return err + } + + getInfo, err := f.cfg.LndClient.GetInfo(ctx) + if err != nil { + return err + } + + // Create htlc timeout transaction. + timeoutTx, err := f.loopIn.createHtlcSweepTx( + ctx, f.cfg.Signer, f.loopIn.HtlcTimeoutSweepAddress, feeRate, + f.cfg.ChainParams, getInfo.BlockHeight, + f.cfg.MaxStaticAddrHtlcFeePercentage, + ) + if err != nil { + return fmt.Errorf("unable to create htlc timeout sweep tx: %w", + err) + } + + // Broadcast htlc timeout transaction. + txLabel := fmt.Sprintf( + "htlc-timeout-sweep-%v", f.loopIn.SwapHash, + ) + + err = f.cfg.WalletKit.PublishTransaction(ctx, timeoutTx, txLabel) + if err != nil { + e := err.Error() + if !strings.Contains(e, "output already spent") || + strings.Contains(e, chain.ErrInsufficientFee.Error()) { + + f.Errorf("%v: %v", txLabel, err) + f.LastActionError = err + return err + } + } else { + f.Debugf("published htlc timeout sweep with txid: %v", + timeoutTx.TxHash()) + + hash := timeoutTx.TxHash() + f.loopIn.HtlcTimeoutSweepTxHash = &hash + } + + return nil +} + +// 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) + if err != nil { + return nil, err + } + + res = append(res, nonce) + } + + return res, nil +} + +// 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") + } + + var res [musig2.PubNonceSize]byte + copy(res[:], b) + + return res, nil +} + +func fromNonces(nonces [][musig2.PubNonceSize]byte) [][]byte { + result := make([][]byte, 0, len(nonces)) + for _, nonce := range nonces { + temp := make([]byte, musig2.PubNonceSize) + copy(temp, nonce[:]) + result = append(result, temp) + } + + return result +} diff --git a/staticaddr/loopin/fsm.go b/staticaddr/loopin/fsm.go new file mode 100644 index 000000000..400184794 --- /dev/null +++ b/staticaddr/loopin/fsm.go @@ -0,0 +1,365 @@ +package loopin + +import ( + "context" + "fmt" + + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/version" +) + +// FSM embeds an FSM and extends it with a static address loop-in and a config. +type FSM struct { + *fsm.StateMachine + + cfg *Config + + // loopIn stores the loop-in details that are relevant during the + // lifetime of the swap. + loopIn *StaticAddressLoopIn + + // MuSig2 data must not be re-used across restarts, hence it is not + // persisted. + // + // htlcServerNonces contains all the nonces that the server generated + // for the htlc musig2 sessions. + htlcServerNonces [][musig2.PubNonceSize]byte + + // htlcServerNoncesHighFee contains all the high fee nonces that the + // server generated for the htlc musig2 sessions. + htlcServerNoncesHighFee [][musig2.PubNonceSize]byte + + // htlcServerNoncesExtremelyHighFee contains all the extremely high fee + // nonces that the server generated for the htlc musig2 sessions. + htlcServerNoncesExtremelyHighFee [][musig2.PubNonceSize]byte +} + +// NewFSM creates a new loop-in state machine. +func NewFSM(ctx context.Context, loopIn *StaticAddressLoopIn, cfg *Config, + recoverStateMachine bool) (*FSM, error) { + + loopInFsm := &FSM{ + cfg: cfg, + loopIn: loopIn, + } + + params, err := cfg.AddressManager.GetStaticAddressParameters(ctx) + if err != nil { + return nil, fmt.Errorf("unable to get static address "+ + "parameters: %w", err) + } + + loopInStates := loopInFsm.LoopInStatesV0() + switch params.ProtocolVersion { + case version.ProtocolVersion_V0: + + default: + return nil, deposit.ErrProtocolVersionNotSupported + } + + if recoverStateMachine { + loopInFsm.StateMachine = fsm.NewStateMachineWithState( + loopInStates, loopIn.GetState(), + deposit.DefaultObserverSize, + ) + } else { + loopInFsm.StateMachine = fsm.NewStateMachine( + loopInStates, deposit.DefaultObserverSize, + ) + } + + loopInFsm.ActionEntryFunc = loopInFsm.updateLoopIn + + return loopInFsm, nil +} + +// States that the loop-in fsm can transition to. +var ( + // InitHtlcTx initiates the htlc tx creation with the server. + InitHtlcTx = fsm.StateType("InitHtlcTx") + + // SignHtlcTx partially signs the htlc transaction with the received + // server nonces. The client doesn't hold a final signature hence can't + // publish the htlc. + SignHtlcTx = fsm.StateType("SignHtlcTx") + + // MonitorInvoiceAndHtlcTx monitors the swap invoice payment and the + // htlc transaction confirmation. + // Since the client provided its partial signature to spend to the htlc + // pkScript, the server could publish the htlc transaction prematurely. + // We need to monitor the htlc transaction to sweep our timeout path in + // this case. + // If the server pays the swap invoice as expected we can stop to + // monitor the htlc timeout path. + MonitorInvoiceAndHtlcTx = fsm.StateType("MonitorInvoiceAndHtlcTx") + + // PaymentReceived is the state where the swap invoice was paid by the + // server. The client can now sign the sweepless sweep transaction. + PaymentReceived = fsm.StateType("PaymentReceived") + + // SweepHtlcTimeout is the state where the htlc timeout path is + // published because the server did not pay the invoice on time. + SweepHtlcTimeout = fsm.StateType("SweepHtlcTimeout") + + // MonitorHtlcTimeoutSweep monitors the htlc timeout sweep transaction + // confirmation. + MonitorHtlcTimeoutSweep = fsm.StateType("MonitorHtlcTimeoutSweep") + + // HtlcTimeoutSwept is the state where the htlc timeout sweep + // transaction was sufficiently confirmed. + HtlcTimeoutSwept = fsm.StateType("HtlcTimeoutSwept") + + // FetchSignPushSweeplessSweepTx is the state where the client fetches, + // signs and pushes the sweepless sweep tx signatures to the server. + FetchSignPushSweeplessSweepTx = fsm.StateType("FetchSignPushSweeplessSweepTx") //nolint:lll + + // Succeeded is the state the swap is in if it was successful. + Succeeded = fsm.StateType("Succeeded") + + // SucceededSweeplessSigFailed is the state the swap is in if the swap + // payment was received but the client failed to sign the sweepless + // sweep transaction. This is considered a successful case from the + // client's perspective. + SucceededSweeplessSigFailed = fsm.StateType("SucceededSweeplessSigFailed") //nolint:lll + + // UnlockDeposits is the state where the deposits are reset. This + // happens when the state machine encountered an error and the swap + // process needs to start from the beginning. + UnlockDeposits = fsm.StateType("UnlockDeposits") + + // Failed is the state the swap is in if it failed. + Failed = fsm.StateType("Failed") +) + +var PendingStates = []fsm.StateType{ + InitHtlcTx, SignHtlcTx, MonitorInvoiceAndHtlcTx, PaymentReceived, + SweepHtlcTimeout, MonitorHtlcTimeoutSweep, FetchSignPushSweeplessSweepTx, + UnlockDeposits, +} + +var FinalStates = []fsm.StateType{ + HtlcTimeoutSwept, Succeeded, SucceededSweeplessSigFailed, Failed, +} + +var AllStates = append(PendingStates, FinalStates...) + +// Events. +var ( + OnInitHtlc = fsm.EventType("OnInitHtlc") + OnHtlcInitiated = fsm.EventType("OnHtlcInitiated") + OnHtlcTxSigned = fsm.EventType("OnHtlcTxSigned") + OnSweepHtlcTimeout = fsm.EventType("OnSweepHtlcTimeout") + OnHtlcTimeoutSweepPublished = fsm.EventType("OnHtlcTimeoutSweepPublished") + OnHtlcTimeoutSwept = fsm.EventType("OnHtlcTimeoutSwept") + OnPaymentReceived = fsm.EventType("OnPaymentReceived") + OnPaymentDeadlineExceeded = fsm.EventType("OnPaymentDeadlineExceeded") + OnSwapTimedOut = fsm.EventType("OnSwapTimedOut") + OnFetchSignPushSweeplessSweepTx = fsm.EventType("OnFetchSignPushSweeplessSweepTx") + OnSweeplessSweepSigned = fsm.EventType("OnSweeplessSweepSigned") + OnRecover = fsm.EventType("OnRecover") +) + +// LoopInStatesV0 returns the state and transition map for the loop-in state +// machine. +func (f *FSM) LoopInStatesV0() fsm.States { + return fsm.States{ + fsm.EmptyState: fsm.State{ + Transitions: fsm.Transitions{ + OnInitHtlc: InitHtlcTx, + }, + Action: fsm.NoOpAction, + }, + InitHtlcTx: fsm.State{ + Transitions: fsm.Transitions{ + OnHtlcInitiated: SignHtlcTx, + OnRecover: UnlockDeposits, + fsm.OnError: UnlockDeposits, + }, + Action: f.InitHtlcAction, + }, + SignHtlcTx: fsm.State{ + Transitions: fsm.Transitions{ + OnHtlcTxSigned: MonitorInvoiceAndHtlcTx, + OnRecover: UnlockDeposits, + fsm.OnError: UnlockDeposits, + }, + Action: f.SignHtlcTxAction, + }, + MonitorInvoiceAndHtlcTx: fsm.State{ + Transitions: fsm.Transitions{ + OnPaymentReceived: PaymentReceived, + OnSweepHtlcTimeout: SweepHtlcTimeout, + OnSwapTimedOut: Failed, + OnRecover: MonitorInvoiceAndHtlcTx, + fsm.OnError: UnlockDeposits, + }, + Action: f.MonitorInvoiceAndHtlcTxAction, + }, + SweepHtlcTimeout: fsm.State{ + Transitions: fsm.Transitions{ + OnHtlcTimeoutSweepPublished: MonitorHtlcTimeoutSweep, + OnRecover: SweepHtlcTimeout, + fsm.OnError: Failed, + }, + Action: f.SweepHtlcTimeoutAction, + }, + MonitorHtlcTimeoutSweep: fsm.State{ + Transitions: fsm.Transitions{ + OnHtlcTimeoutSwept: HtlcTimeoutSwept, + OnRecover: MonitorHtlcTimeoutSweep, + fsm.OnError: Failed, + }, + Action: f.MonitorHtlcTimeoutSweepAction, + }, + PaymentReceived: fsm.State{ + Transitions: fsm.Transitions{ + OnFetchSignPushSweeplessSweepTx: FetchSignPushSweeplessSweepTx, + OnRecover: SucceededSweeplessSigFailed, + fsm.OnError: SucceededSweeplessSigFailed, + }, + Action: f.PaymentReceivedAction, + }, + FetchSignPushSweeplessSweepTx: fsm.State{ + Transitions: fsm.Transitions{ + OnSweeplessSweepSigned: Succeeded, + OnRecover: SucceededSweeplessSigFailed, + fsm.OnError: SucceededSweeplessSigFailed, + }, + Action: f.FetchSignPushSweeplessSweepTxAction, + }, + HtlcTimeoutSwept: fsm.State{ + Action: fsm.NoOpAction, + }, + Succeeded: fsm.State{ + Action: fsm.NoOpAction, + }, + SucceededSweeplessSigFailed: fsm.State{ + Action: fsm.NoOpAction, + }, + UnlockDeposits: fsm.State{ + Transitions: fsm.Transitions{ + OnRecover: UnlockDeposits, + fsm.OnError: Failed, + }, + Action: f.UnlockDepositsAction, + }, + Failed: fsm.State{ + Action: fsm.NoOpAction, + }, + } +} + +// updateLoopIn is called after every action and updates the loop-in in the db. +func (f *FSM) updateLoopIn(ctx context.Context, notification fsm.Notification) { + f.Infof("Current: %v", notification.NextState) + + // Skip the update if the loop-in is not yet initialized. This happens + // on the entry action of the fsm. + if f.loopIn == nil { + return + } + + f.loopIn.SetState(notification.NextState) + + // Check if we can skip updating the loop-in in the database. + if isUpdateSkipped(notification, f.loopIn) { + return + } + + stored, err := f.cfg.Store.IsStored(ctx, f.loopIn.SwapHash) + if err != nil { + f.Errorf("Error checking if loop-in is stored: %v", err) + + return + } + + if !stored { + f.Warnf("Loop-in not stored in db, can't update") + + return + } + + err = f.cfg.Store.UpdateLoopIn(ctx, f.loopIn) + if err != nil { + f.Errorf("Error updating loop-in: %v", err) + + return + } +} + +// isUpdateSkipped returns true if the loop-in should not be updated for the +// given notification. +func isUpdateSkipped(notification fsm.Notification, + l *StaticAddressLoopIn) bool { + + prevState := notification.PreviousState + + // Skip if we are in the empty state because no loop-in has been + // persisted yet. + if l.IsInState(fsm.EmptyState) { + return true + } + + // We don't update in self-loops, e.g. in the case of recovery. + if l.IsInState(prevState) { + return true + } + + // If we transitioned from the empty state to InitHtlcTx there's still + // no loop-in persisted, so we don't need to update it. + if prevState == fsm.EmptyState && l.IsInState(InitHtlcTx) { + return true + } + + return false +} + +// Infof logs an info message with the loop-in swap hash. +func (f *FSM) Infof(format string, args ...interface{}) { + if f.loopIn == nil { + log.Infof(format, args...) + return + } + log.Infof( + "StaticAddr loop-in %s: %s", f.loopIn.SwapHash.String(), + fmt.Sprintf(format, args...), + ) +} + +// Debugf logs a debug message with the loop-in swap hash. +func (f *FSM) Debugf(format string, args ...interface{}) { + if f.loopIn == nil { + log.Infof(format, args...) + return + } + log.Debugf( + "StaticAddr loop-in %s: %s", f.loopIn.SwapHash.String(), + fmt.Sprintf(format, args...), + ) +} + +// Warnf logs a warning message with the loop-in swap hash. +func (f *FSM) Warnf(format string, args ...interface{}) { + if f.loopIn == nil { + log.Warnf(format, args...) + return + } + log.Warnf( + "StaticAddr loop-in %s: %s", f.loopIn.SwapHash.String(), + fmt.Sprintf(format, args...), + ) +} + +// Errorf logs an error message with the loop-in swap hash. +func (f *FSM) Errorf(format string, args ...interface{}) { + if f.loopIn == nil { + log.Errorf(format, args...) + return + } + log.Errorf( + "StaticAddr loop-in %s: %s", f.loopIn.SwapHash.String(), + fmt.Sprintf(format, args...), + ) +} diff --git a/staticaddr/loopin/interface.go b/staticaddr/loopin/interface.go new file mode 100644 index 000000000..88bd543bb --- /dev/null +++ b/staticaddr/loopin/interface.go @@ -0,0 +1,72 @@ +package loopin + +import ( + "context" + + "github.com/btcsuite/btcd/btcutil" + "github.com/lightninglabs/loop" + "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/staticaddr/address" + "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/script" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/zpay32" +) + +type ( + // ValidateLoopInContract validates the contract parameters against our + // request. + ValidateLoopInContract func(height int32, htlcExpiry int32) error +) + +// AddressManager handles fetching of address parameters. +type AddressManager interface { + // GetStaticAddressParameters returns the static address parameters. + GetStaticAddressParameters(ctx context.Context) (*address.Parameters, + error) + + // GetStaticAddress returns the deposit address for the given client and + // server public keys. + GetStaticAddress(ctx context.Context) (*script.StaticAddress, error) +} + +// DepositManager handles the interaction of loop-ins with deposits. +type DepositManager interface { + // AllStringOutpointsActiveDeposits returns all deposits that have the + // given outpoints and are in the given state. If any of the outpoints + // does not correspond to an active deposit, the function returns false. + AllStringOutpointsActiveDeposits(outpoints []string, + stateFilter fsm.StateType) ([]*deposit.Deposit, bool) + + // TransitionDeposits transitions the given deposits to the next state + // based on the given event. It returns an error if the transition is + // invalid. + TransitionDeposits(ctx context.Context, deposits []*deposit.Deposit, + event fsm.EventType, expectedFinalState fsm.StateType) error +} + +// StaticAddressLoopInStore provides access to the static address loop-in DB. +type StaticAddressLoopInStore interface { + // CreateLoopIn creates a loop-in record in the database. + CreateLoopIn(ctx context.Context, loopIn *StaticAddressLoopIn) error + + // UpdateLoopIn updates a loop-in record in the database. + UpdateLoopIn(ctx context.Context, loopIn *StaticAddressLoopIn) error + + // GetStaticAddressLoopInSwapsByStates returns all loop-ins with given + // states. + GetStaticAddressLoopInSwapsByStates(ctx context.Context, + states []fsm.StateType) ([]*StaticAddressLoopIn, error) + + // IsStored checks if the loop-in is already stored in the database. + IsStored(ctx context.Context, swapHash lntypes.Hash) (bool, error) +} + +type QuoteGetter interface { + // GetLoopInQuote returns a quote for a loop-in swap. + GetLoopInQuote(ctx context.Context, amt btcutil.Amount, + pubKey route.Vertex, lastHop *route.Vertex, + routeHints [][]zpay32.HopHint, + initiator string, numDeposits uint32) (*loop.LoopInQuote, error) +} diff --git a/staticaddr/loopin/manager.go b/staticaddr/loopin/manager.go new file mode 100644 index 000000000..35768d170 --- /dev/null +++ b/staticaddr/loopin/manager.go @@ -0,0 +1,458 @@ +package loopin + +import ( + "context" + "fmt" + "sync/atomic" + "time" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/lightninglabs/lndclient" + "github.com/lightninglabs/loop" + "github.com/lightninglabs/loop/fsm" + "github.com/lightninglabs/loop/labels" + "github.com/lightninglabs/loop/staticaddr/deposit" + looprpc "github.com/lightninglabs/loop/swapserverrpc" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/routing/route" +) + +// Config contains the services required for the loop-in manager. +type Config struct { + // Server is the client that is used to communicate with the static + // address server. + Server looprpc.StaticAddressServerClient + + // AddressManager gives the withdrawal manager access to static address + // parameters. + AddressManager AddressManager + + // DepositManager gives the withdrawal manager access to the deposits + // enabling it to create and manage loop-ins. + DepositManager DepositManager + + // LndClient is used to add invoices and select hop hints. + LndClient lndclient.LightningClient + + // InvoicesClient is used to subscribe to invoice settlements and + // cancel invoices. + InvoicesClient lndclient.InvoicesClient + + // SwapClient is used to get loop in quotes. + QuoteGetter QuoteGetter + + // NodePubKey is used to get a loo-in quote. + NodePubkey route.Vertex + + // WalletKit is the wallet client that is used to derive new keys from + // lnd's wallet. + WalletKit lndclient.WalletKitClient + + // ChainParams is the chain configuration(mainnet, testnet...) this + // manager uses. + ChainParams *chaincfg.Params + + // Chain is the chain notifier that is used to listen for new + // blocks. + ChainNotifier lndclient.ChainNotifierClient + + // Signer is the signer client that is used to sign transactions. + Signer lndclient.SignerClient + + // Store is the database store that is used to store static address + // loop-in related records. + Store StaticAddressLoopInStore + + // ValidateLoopInContract validates the contract parameters against our + // request. + ValidateLoopInContract ValidateLoopInContract + + // MaxStaticAddrHtlcFeePercentage is the percentage of the swap amount + // that we allow the server to charge for the htlc transaction. + // Although highly unlikely, this is a defense against the server + // publishing the htlc without paying the swap invoice, forcing us to + // sweep the timeout path. + MaxStaticAddrHtlcFeePercentage float64 + + // MaxStaticAddrHtlcBackupFeePercentage is the percentage of the swap + // amount that we allow the server to charge for the htlc backup + // transactions. This is a defense against the server publishing the + // htlc backup without paying the swap invoice, forcing us to sweep the + // timeout path. This value is elevated compared to + // MaxStaticAddrHtlcFeePercentage since it serves the server as backup + // transaction in case of fee spikes. + MaxStaticAddrHtlcBackupFeePercentage float64 +} + +// newSwapRequest is used to send a loop-in request to the manager main loop. +type newSwapRequest struct { + loopInRequest *loop.StaticAddressLoopInRequest + respChan chan *newSwapResponse +} + +// newSwapResponse is used to return the loop-in swap and error to the server. +type newSwapResponse struct { + loopIn *StaticAddressLoopIn + err error +} + +// Manager manages the address state machines. +type Manager struct { + cfg *Config + + // initChan signals the daemon that the address manager has completed + // its initialization. + initChan chan struct{} + + // newLoopInChan receives swap requests from the server and initiates + // loop-in swaps. + newLoopInChan chan *newSwapRequest + + // exitChan signals the manager's subroutines that the main looop ctx + // has been canceled. + exitChan chan struct{} + + // errChan forwards errors from the loop-in manager to the server. + errChan chan error + + // currentHeight stores the currently best known block height. + currentHeight atomic.Uint32 + + activeLoopIns map[lntypes.Hash]*FSM +} + +// NewManager creates a new deposit withdrawal manager. +func NewManager(cfg *Config) *Manager { + return &Manager{ + cfg: cfg, + initChan: make(chan struct{}), + newLoopInChan: make(chan *newSwapRequest), + exitChan: make(chan struct{}), + errChan: make(chan error), + activeLoopIns: make(map[lntypes.Hash]*FSM), + } +} + +// Run runs the static address loop-in manager. +func (m *Manager) Run(ctx context.Context, currentHeight uint32) error { + m.currentHeight.Store(currentHeight) + + registerBlockNtfn := m.cfg.ChainNotifier.RegisterBlockEpochNtfn + newBlockChan, newBlockErrChan, err := registerBlockNtfn(ctx) + if err != nil { + return err + } + + // Upon start of the loop-in manager we reinstate all previous loop-ins + // that are not yet completed. + err = m.recoverLoopIns(ctx) + if err != nil { + return err + } + + // Communicate to the caller that the address manager has completed its + // initialization. + close(m.initChan) + + var loopIn *StaticAddressLoopIn + for { + select { + case height := <-newBlockChan: + m.currentHeight.Store(uint32(height)) + + case err = <-newBlockErrChan: + return err + + case request := <-m.newLoopInChan: + loopIn, err = m.initiateLoopIn( + ctx, request.loopInRequest, + ) + if err != nil { + log.Errorf("Error initiating loop-in swap: %v", + err) + } + + // We forward the initialized loop-in and error to + // DeliverLoopInRequest. + resp := &newSwapResponse{ + loopIn: loopIn, + err: err, + } + select { + case request.respChan <- resp: + + case <-ctx.Done(): + // Noify subroutines that the main loop has been + // canceled. + close(m.exitChan) + + return ctx.Err() + } + + case <-ctx.Done(): + return ctx.Err() + } + } +} + +// recover stars a loop-in state machine for each non-final loop-in to pick up +// work where it was left off before the restart. +func (m *Manager) recoverLoopIns(ctx context.Context) error { + log.Infof("Recovering static address loop-ins...") + + // Recover loop-ins. + // Recover pending static address loop-ins. + pendingLoopIns, err := m.cfg.Store.GetStaticAddressLoopInSwapsByStates( + ctx, PendingStates, + ) + if err != nil { + return err + } + + for _, loopIn := range pendingLoopIns { + log.Debugf("Recovering loopIn %x", loopIn.SwapHash[:]) + + // Retrieve all deposits regardless of deposit state. If any of + // the deposits is not active in the in-mem map of the deposits + // manager we log it, but continue to recover the loop-in. + var allActive bool + loopIn.Deposits, allActive = + m.cfg.DepositManager.AllStringOutpointsActiveDeposits( + loopIn.DepositOutpoints, fsm.EmptyState, + ) + + if !allActive { + log.Errorf("one or more deposits are not active") + } + + loopIn.AddressParams, err = + m.cfg.AddressManager.GetStaticAddressParameters(ctx) + + if err != nil { + return err + } + + loopIn.Address, err = m.cfg.AddressManager.GetStaticAddress( + ctx, + ) + if err != nil { + return err + } + + // Create a state machine for a given loop-in. + var ( + recovery = true + fsm *FSM + ) + fsm, err = NewFSM(ctx, loopIn, m.cfg, recovery) + if err != nil { + return err + } + + // Send the OnRecover event to the state machine. + swapHash := loopIn.SwapHash + go func() { + err = fsm.SendEvent(ctx, OnRecover, nil) + if err != nil { + log.Errorf("Error sending OnStart event: %v", + err) + } + + m.activeLoopIns[swapHash] = fsm + }() + } + + return nil +} + +// WaitInitComplete waits until the static address loop-in manager has completed +// its setup. +func (m *Manager) WaitInitComplete() { + defer log.Debugf("Static address loop-in manager initiation complete.") + <-m.initChan +} + +// DeliverLoopInRequest forwards a loop-in request from the server to the +// manager run loop to initiate a new loop-in swap. +func (m *Manager) DeliverLoopInRequest(ctx context.Context, + req *loop.StaticAddressLoopInRequest) (*StaticAddressLoopIn, error) { + + request := &newSwapRequest{ + loopInRequest: req, + respChan: make(chan *newSwapResponse), + } + + // Send the new loop-in request to the manager run loop. + select { + case m.newLoopInChan <- request: + + case <-m.exitChan: + return nil, fmt.Errorf("loop-in manager has been canceled") + + case <-ctx.Done(): + return nil, fmt.Errorf("context canceled while initiating " + + "a loop-in swap") + } + + // Wait for the response from the manager run loop. + select { + case resp := <-request.respChan: + return resp.loopIn, resp.err + + case <-m.exitChan: + return nil, fmt.Errorf("loop-in manager has been canceled") + + case <-ctx.Done(): + return nil, fmt.Errorf("context canceled while waiting for " + + "loop-in swap response") + } +} + +// initiateLoopIn initiates a loop-in swap. It passes the request to the server +// along with all relevant loop-in information. +func (m *Manager) initiateLoopIn(ctx context.Context, + req *loop.StaticAddressLoopInRequest) (*StaticAddressLoopIn, error) { + + // Validate the loop-in request. + if len(req.DepositOutpoints) == 0 { + return nil, fmt.Errorf("no deposit outpoints provided") + } + + // Retrieve all deposits referenced by the outpoints and ensure that + // they are in state Deposited. + deposits, active := m.cfg.DepositManager.AllStringOutpointsActiveDeposits( //nolint:lll + req.DepositOutpoints, deposit.Deposited, + ) + if !active { + return nil, fmt.Errorf("one or more deposits are not in "+ + "state %s", deposit.Deposited) + } + + // Calculate the total deposit amount. + tmp := &StaticAddressLoopIn{ + Deposits: deposits, + } + totalDepositAmount := tmp.TotalDepositAmount() + + // Check that the label is valid. + err := labels.Validate(req.Label) + if err != nil { + return nil, fmt.Errorf("invalid label: %w", err) + } + + // Private and route hints are mutually exclusive as setting private + // means we retrieve our own route hints from the connected node. + if len(req.RouteHints) != 0 && req.Private { + return nil, fmt.Errorf("private and route hints are mutually " + + "exclusive") + } + + // If private is set, we generate route hints. + if req.Private { + // If last_hop is set, we'll only add channels with peers set to + // the last_hop parameter. + includeNodes := make(map[route.Vertex]struct{}) + if req.LastHop != nil { + includeNodes[*req.LastHop] = struct{}{} + } + + // Because the Private flag is set, we'll generate our own set + // of hop hints. + req.RouteHints, err = loop.SelectHopHints( + ctx, m.cfg.LndClient, totalDepositAmount, + loop.DefaultMaxHopHints, includeNodes, + ) + if err != nil { + return nil, fmt.Errorf("unable to generate hop "+ + "hints: %w", err) + } + } + + // Request current server loop in terms and use these to calculate the + // swap fee that we should subtract from the swap amount in the payment + // request that we send to the server. We pass nil as optional route + // hints as hop hint selection when generating invoices with private + // channels is an LND side black box feature. Advanced users will quote + // directly anyway and there they have the option to add specific route + // hints. + // The quote call will also request a probe from the server to ensure + // feasibility of a loop-in for the totalDepositAmount. + numDeposits := uint32(len(deposits)) + quote, err := m.cfg.QuoteGetter.GetLoopInQuote( + ctx, totalDepositAmount, m.cfg.NodePubkey, req.LastHop, + req.RouteHints, req.Initiator, numDeposits, + ) + if err != nil { + return nil, fmt.Errorf("unable to get loop in quote: %w", err) + } + + // If the previously accepted quote fee is lower than what is quoted now + // we abort the swap. + if quote.SwapFee > req.MaxSwapFee { + log.Warnf("Swap fee %v exceeding maximum of %v", + quote.SwapFee, req.MaxSwapFee) + + return nil, loop.ErrSwapFeeTooHigh + } + + paymentTimeoutSeconds := uint32(DefaultPaymentTimeoutSeconds) + if req.PaymentTimeoutSeconds != 0 { + paymentTimeoutSeconds = req.PaymentTimeoutSeconds + } + + swap := &StaticAddressLoopIn{ + DepositOutpoints: req.DepositOutpoints, + Deposits: deposits, + Label: req.Label, + Initiator: req.Initiator, + InitiationTime: time.Now(), + RouteHints: req.RouteHints, + QuotedSwapFee: quote.SwapFee, + MaxSwapFee: req.MaxSwapFee, + PaymentTimeoutSeconds: paymentTimeoutSeconds, + } + if req.LastHop != nil { + swap.LastHop = req.LastHop[:] + } + + swap.InitiationHeight = m.currentHeight.Load() + + return m.startLoopInFsm(ctx, swap) +} + +// startLoopInFsm initiates a loop-in state machine based on the user-provided +// swap information, sends that info to the server and waits for the server to +// return htlc signature information. It then creates the loop-in object in the +// database. +func (m *Manager) startLoopInFsm(ctx context.Context, + loopIn *StaticAddressLoopIn) (*StaticAddressLoopIn, error) { + + // Create a state machine for a given deposit. + recovery := false + loopInFsm, err := NewFSM(ctx, loopIn, m.cfg, recovery) + if err != nil { + return nil, err + } + + // Send the start event to the state machine. + go func() { + err = loopInFsm.SendEvent(ctx, OnInitHtlc, nil) + if err != nil { + log.Errorf("Error sending OnNewRequest event: %v", err) + } + }() + + // If an error occurs before SignHtlcTx is reached we consider the swap + // failed and abort early. + err = loopInFsm.DefaultObserver.WaitForState( + ctx, time.Minute, SignHtlcTx, + fsm.WithAbortEarlyOnErrorOption(), + ) + if err != nil { + return nil, err + } + + m.activeLoopIns[loopIn.SwapHash] = loopInFsm + + return loopIn, nil +} From e623061eec8ea0bd004e753b5fd20e852cd4692e Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Tue, 30 Jul 2024 16:14:24 +0200 Subject: [PATCH 12/14] unit: manager mocks for static address loop-in --- staticaddr/address/manager_test.go | 44 ++++++++++++++++++++++++++++++ staticaddr/deposit/manager_test.go | 44 ++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/staticaddr/address/manager_test.go b/staticaddr/address/manager_test.go index 9548640d8..101cef582 100644 --- a/staticaddr/address/manager_test.go +++ b/staticaddr/address/manager_test.go @@ -32,6 +32,50 @@ type mockStaticAddressClient struct { mock.Mock } +func (m *mockStaticAddressClient) ServerStaticAddressLoopIn(ctx context.Context, + in *swapserverrpc.ServerStaticAddressLoopInRequest, + opts ...grpc.CallOption) ( + *swapserverrpc.ServerStaticAddressLoopInResponse, error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.ServerStaticAddressLoopInResponse), + args.Error(1) +} + +func (m *mockStaticAddressClient) PushStaticAddressSweeplessSigs(ctx context.Context, + in *swapserverrpc.PushStaticAddressSweeplessSigsRequest, + opts ...grpc.CallOption) ( + *swapserverrpc.PushStaticAddressSweeplessSigsResponse, error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.PushStaticAddressSweeplessSigsResponse), + args.Error(1) +} + +func (m *mockStaticAddressClient) FetchSweeplessSweepTx(ctx context.Context, + in *swapserverrpc.FetchSweeplessSweepTxRequest, + opts ...grpc.CallOption) ( + *swapserverrpc.FetchSweeplessSweepTxResponse, error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.FetchSweeplessSweepTxResponse), + args.Error(1) +} + +func (m *mockStaticAddressClient) PushStaticAddressHtlcSigs(ctx context.Context, + in *swapserverrpc.PushStaticAddressHtlcSigsRequest, + opts ...grpc.CallOption) ( + *swapserverrpc.PushStaticAddressHtlcSigsResponse, error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.PushStaticAddressHtlcSigsResponse), + args.Error(1) +} + func (m *mockStaticAddressClient) ServerWithdrawDeposits(ctx context.Context, in *swapserverrpc.ServerWithdrawRequest, opts ...grpc.CallOption) (*swapserverrpc.ServerWithdrawResponse, diff --git a/staticaddr/deposit/manager_test.go b/staticaddr/deposit/manager_test.go index 49c3d4691..009f0c4fe 100644 --- a/staticaddr/deposit/manager_test.go +++ b/staticaddr/deposit/manager_test.go @@ -51,6 +51,50 @@ type mockStaticAddressClient struct { mock.Mock } +func (m *mockStaticAddressClient) ServerStaticAddressLoopIn(ctx context.Context, + in *swapserverrpc.ServerStaticAddressLoopInRequest, + opts ...grpc.CallOption) ( + *swapserverrpc.ServerStaticAddressLoopInResponse, error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.ServerStaticAddressLoopInResponse), + args.Error(1) +} + +func (m *mockStaticAddressClient) PushStaticAddressSweeplessSigs(ctx context.Context, + in *swapserverrpc.PushStaticAddressSweeplessSigsRequest, + opts ...grpc.CallOption) ( + *swapserverrpc.PushStaticAddressSweeplessSigsResponse, error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.PushStaticAddressSweeplessSigsResponse), + args.Error(1) +} + +func (m *mockStaticAddressClient) FetchSweeplessSweepTx(ctx context.Context, + in *swapserverrpc.FetchSweeplessSweepTxRequest, + opts ...grpc.CallOption) ( + *swapserverrpc.FetchSweeplessSweepTxResponse, error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.FetchSweeplessSweepTxResponse), + args.Error(1) +} + +func (m *mockStaticAddressClient) PushStaticAddressHtlcSigs(ctx context.Context, + in *swapserverrpc.PushStaticAddressHtlcSigsRequest, + opts ...grpc.CallOption) ( + *swapserverrpc.PushStaticAddressHtlcSigsResponse, error) { + + args := m.Called(ctx, in, opts) + + return args.Get(0).(*swapserverrpc.PushStaticAddressHtlcSigsResponse), + args.Error(1) +} + func (m *mockStaticAddressClient) ServerWithdrawDeposits(ctx context.Context, in *swapserverrpc.ServerWithdrawRequest, opts ...grpc.CallOption) (*swapserverrpc.ServerWithdrawResponse, From 2eec098cd52137e6079d2cefe23f8aeca2888ef5 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Tue, 30 Jul 2024 15:41:11 +0200 Subject: [PATCH 13/14] loopd: static address loop-in support --- loopd/daemon.go | 55 ++++++++++++++++++++ loopd/perms/perms.go | 7 +++ loopd/swapclient_server.go | 103 ++++++++++++++++++++++++++++++++++--- 3 files changed, 157 insertions(+), 8 deletions(-) diff --git a/loopd/daemon.go b/loopd/daemon.go index db3ceb054..4b0117791 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -24,6 +24,7 @@ import ( "github.com/lightninglabs/loop/notifications" "github.com/lightninglabs/loop/staticaddr/address" "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/loopin" "github.com/lightninglabs/loop/staticaddr/withdraw" loop_swaprpc "github.com/lightninglabs/loop/swapserverrpc" "github.com/lightninglabs/loop/sweepbatcher" @@ -536,6 +537,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { staticAddressManager *address.Manager depositManager *deposit.Manager withdrawalManager *withdraw.Manager + staticLoopInManager *loopin.Manager ) // Create the reservation and instantout managers. @@ -613,6 +615,30 @@ func (d *Daemon) initialize(withMacaroonService bool) error { Signer: d.lnd.Signer, } withdrawalManager = withdraw.NewManager(withdrawalCfg) + + // Static address loop-in manager setup. + staticAddressLoopInStore := loopin.NewSqlStore( + loopdb.NewTypedStore[loopin.Querier](baseDb), + clock.NewDefaultClock(), d.lnd.ChainParams, + ) + + staticLoopInManager = loopin.NewManager(&loopin.Config{ + Server: staticAddressClient, + QuoteGetter: swapClient.Server, + LndClient: d.lnd.Client, + InvoicesClient: d.lnd.Invoices, + NodePubkey: d.lnd.NodePubkey, + AddressManager: staticAddressManager, + DepositManager: depositManager, + Store: staticAddressLoopInStore, + WalletKit: d.lnd.WalletKit, + ChainNotifier: d.lnd.ChainNotifier, + ChainParams: d.lnd.ChainParams, + Signer: d.lnd.Signer, + ValidateLoopInContract: loop.ValidateLoopInContract, + MaxStaticAddrHtlcFeePercentage: d.cfg.MaxStaticAddrHtlcFeePercentage, + MaxStaticAddrHtlcBackupFeePercentage: d.cfg.MaxStaticAddrHtlcBackupFeePercentage, + }) } // Now finally fully initialize the swap client RPC server instance. @@ -631,6 +657,7 @@ func (d *Daemon) initialize(withMacaroonService bool) error { staticAddressManager: staticAddressManager, depositManager: depositManager, withdrawalManager: withdrawalManager, + staticLoopInManager: staticLoopInManager, } // Retrieve all currently existing swaps from the database. @@ -840,6 +867,34 @@ func (d *Daemon) initialize(withMacaroonService bool) error { withdrawalManager.WaitInitComplete() } + // Start the static address loop-in manager. + if staticLoopInManager != nil { + d.wg.Add(1) + go func() { + defer d.wg.Done() + + // Lnd's GetInfo call supplies us with the current block + // height. + info, err := d.lnd.Client.GetInfo(d.mainCtx) + if err != nil { + d.internalErrChan <- err + + return + } + + log.Info("Starting static address loop-in manager...") + err = staticLoopInManager.Run( + d.mainCtx, info.BlockHeight, + ) + if err != nil && !errors.Is(context.Canceled, err) { + d.internalErrChan <- err + } + log.Info("Starting static address loop-in manager " + + "stopped") + }() + staticLoopInManager.WaitInitComplete() + } + // Last, start our internal error handler. This will return exactly one // error or nil on the main error channel to inform the caller that // something went wrong or that shutdown is complete. We don't add to diff --git a/loopd/perms/perms.go b/loopd/perms/perms.go index 98ffb5927..d47257e82 100644 --- a/loopd/perms/perms.go +++ b/loopd/perms/perms.go @@ -101,6 +101,13 @@ var RequiredPermissions = map[string][]bakery.Op{ Entity: "loop", Action: "in", }}, + "/looprpc.SwapClient/StaticAddressLoopIn": {{ + Entity: "swap", + Action: "read", + }, { + Entity: "loop", + Action: "in", + }}, "/looprpc.SwapClient/GetLsatTokens": {{ Entity: "auth", Action: "read", diff --git a/loopd/swapclient_server.go b/loopd/swapclient_server.go index 8982dd8e2..88500ef5c 100644 --- a/loopd/swapclient_server.go +++ b/loopd/swapclient_server.go @@ -28,6 +28,7 @@ import ( "github.com/lightninglabs/loop/looprpc" "github.com/lightninglabs/loop/staticaddr/address" "github.com/lightninglabs/loop/staticaddr/deposit" + "github.com/lightninglabs/loop/staticaddr/loopin" "github.com/lightninglabs/loop/staticaddr/withdraw" "github.com/lightninglabs/loop/swap" "github.com/lightninglabs/loop/swapserverrpc" @@ -91,6 +92,7 @@ type swapClientServer struct { staticAddressManager *address.Manager depositManager *deposit.Manager withdrawalManager *withdraw.Manager + staticLoopInManager *loopin.Manager swaps map[lntypes.Hash]loop.SwapInfo subscribers map[int]chan<- interface{} statusChan chan loop.SwapInfo @@ -1481,6 +1483,57 @@ func (s *swapClientServer) GetStaticAddressSummary(ctx context.Context, ) } +// StaticAddressLoopIn initiates a loop-in request using static address +// deposits. +func (s *swapClientServer) StaticAddressLoopIn(ctx context.Context, + in *looprpc.StaticAddressLoopInRequest) ( + *looprpc.StaticAddressLoopInResponse, error) { + + log.Infof("Static loop-in request received") + + routeHints, err := unmarshallRouteHints(in.RouteHints) + if err != nil { + return nil, err + } + + req := &loop.StaticAddressLoopInRequest{ + DepositOutpoints: in.Outpoints, + MaxSwapFee: btcutil.Amount(in.MaxSwapFeeSatoshis), + Label: in.Label, + Initiator: in.Initiator, + Private: in.Private, + RouteHints: routeHints, + PaymentTimeoutSeconds: in.PaymentTimeoutSeconds, + } + + if in.LastHop != nil { + lastHop, err := route.NewVertexFromBytes(in.LastHop) + if err != nil { + return nil, err + } + req.LastHop = &lastHop + } + + loopIn, err := s.staticLoopInManager.DeliverLoopInRequest(ctx, req) + if err != nil { + return nil, err + } + + return &looprpc.StaticAddressLoopInResponse{ + SwapHash: loopIn.SwapHash[:], + State: string(loopIn.GetState()), + Amount: uint64(loopIn.TotalDepositAmount()), + HtlcCltv: loopIn.HtlcCltvExpiry, + MaxSwapFeeSatoshis: int64(loopIn.MaxSwapFee), + InitiationHeight: loopIn.InitiationHeight, + ProtocolVersion: loopIn.ProtocolVersion.String(), + Initiator: loopIn.Initiator, + Label: loopIn.Label, + PaymentTimeoutSeconds: loopIn.PaymentTimeoutSeconds, + QuotedSwapFeeSatoshis: int64(loopIn.QuotedSwapFee), + }, nil +} + func (s *swapClientServer) depositSummary(ctx context.Context, deposits []*deposit.Deposit, stateFilter looprpc.DepositState, outpointsFilter []string) (*looprpc.StaticAddressSummaryResponse, @@ -1492,6 +1545,8 @@ func (s *swapClientServer) depositSummary(ctx context.Context, valueDeposited int64 valueExpired int64 valueWithdrawn int64 + valueLoopedIn int64 + htlcTimeoutSwept int64 ) // Value unconfirmed. @@ -1517,6 +1572,12 @@ func (s *swapClientServer) depositSummary(ctx context.Context, case deposit.Withdrawn: valueWithdrawn += value + + case deposit.LoopedIn: + valueLoopedIn += value + + case deposit.HtlcTimeoutSwept: + htlcTimeoutSwept += value } } @@ -1545,7 +1606,7 @@ func (s *swapClientServer) depositSummary(ctx context.Context, return true } - return d.GetState() == toServerState(stateFilter) + return d.IsInState(toServerState(stateFilter)) } clientDeposits = filter(deposits, f) } @@ -1563,13 +1624,15 @@ func (s *swapClientServer) depositSummary(ctx context.Context, } return &looprpc.StaticAddressSummaryResponse{ - StaticAddress: address.String(), - TotalNumDeposits: uint32(totalNumDeposits), - ValueUnconfirmedSatoshis: valueUnconfirmed, - ValueDepositedSatoshis: valueDeposited, - ValueExpiredSatoshis: valueExpired, - ValueWithdrawnSatoshis: valueWithdrawn, - FilteredDeposits: clientDeposits, + StaticAddress: address.String(), + TotalNumDeposits: uint32(totalNumDeposits), + ValueUnconfirmedSatoshis: valueUnconfirmed, + ValueDepositedSatoshis: valueDeposited, + ValueExpiredSatoshis: valueExpired, + ValueWithdrawnSatoshis: valueWithdrawn, + ValueLoopedInSatoshis: valueLoopedIn, + ValueHtlcTimeoutSweepsSatoshis: htlcTimeoutSwept, + FilteredDeposits: clientDeposits, }, nil } @@ -1612,6 +1675,18 @@ func toClientState(state fsm.StateType) looprpc.DepositState { case deposit.PublishExpirySweep: return looprpc.DepositState_PUBLISH_EXPIRED + case deposit.LoopingIn: + return looprpc.DepositState_LOOPING_IN + + case deposit.LoopedIn: + return looprpc.DepositState_LOOPED_IN + + case deposit.SweepHtlcTimeout: + return looprpc.DepositState_SWEEP_HTLC_TIMEOUT + + case deposit.HtlcTimeoutSwept: + return looprpc.DepositState_HTLC_TIMEOUT_SWEPT + case deposit.WaitForExpirySweep: return looprpc.DepositState_WAIT_FOR_EXPIRY_SWEEP @@ -1637,6 +1712,18 @@ func toServerState(state looprpc.DepositState) fsm.StateType { case looprpc.DepositState_PUBLISH_EXPIRED: return deposit.PublishExpirySweep + case looprpc.DepositState_LOOPING_IN: + return deposit.LoopingIn + + case looprpc.DepositState_LOOPED_IN: + return deposit.LoopedIn + + case looprpc.DepositState_SWEEP_HTLC_TIMEOUT: + return deposit.SweepHtlcTimeout + + case looprpc.DepositState_HTLC_TIMEOUT_SWEPT: + return deposit.HtlcTimeoutSwept + case looprpc.DepositState_WAIT_FOR_EXPIRY_SWEEP: return deposit.WaitForExpirySweep From ac657262fe8f422814ae4c9ddbc4fdee1d21ea1d Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Tue, 30 Jul 2024 15:47:29 +0200 Subject: [PATCH 14/14] cmd: static address loop-in --- cmd/loop/loopin.go | 3 - cmd/loop/main.go | 6 + cmd/loop/quote.go | 6 +- cmd/loop/staticaddr.go | 241 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 242 insertions(+), 14 deletions(-) diff --git a/cmd/loop/loopin.go b/cmd/loop/loopin.go index 2d84a5e8d..52e889009 100644 --- a/cmd/loop/loopin.go +++ b/cmd/loop/loopin.go @@ -52,9 +52,6 @@ var ( Name: "in", Usage: "perform an on-chain to off-chain swap (loop in)", ArgsUsage: "amt", - Subcommands: []cli.Command{ - staticAddressCommands, - }, Description: ` Send the amount in satoshis specified by the amt argument off-chain. diff --git a/cmd/loop/main.go b/cmd/loop/main.go index 88ddd67f5..77d788824 100644 --- a/cmd/loop/main.go +++ b/cmd/loop/main.go @@ -280,6 +280,12 @@ func displayInDetails(req *looprpc.QuoteRequest, "wallet.\n\n") } + if req.DepositOutpoints != nil { + fmt.Printf("On-chain fees for static address loop-ins are not " + + "included.\nThey were already paid when the deposits " + + "were created.\n\n") + } + printQuoteInResp(req, resp, verbose) fmt.Printf("\nCONTINUE SWAP? (y/n): ") diff --git a/cmd/loop/quote.go b/cmd/loop/quote.go index f92b7243c..ec2cdb657 100644 --- a/cmd/loop/quote.go +++ b/cmd/loop/quote.go @@ -228,7 +228,11 @@ func printQuoteInResp(req *looprpc.QuoteRequest, totalFee := resp.HtlcPublishFeeSat + resp.SwapFeeSat - fmt.Printf(satAmtFmt, "Send on-chain:", req.Amt) + if req.DepositOutpoints != nil { + fmt.Printf(satAmtFmt, "Previously deposited on-chain:", req.Amt) + } else { + fmt.Printf(satAmtFmt, "Send on-chain:", req.Amt) + } fmt.Printf(satAmtFmt, "Receive off-chain:", req.Amt-totalFee) switch { diff --git a/cmd/loop/staticaddr.go b/cmd/loop/staticaddr.go index dcd02834c..de8d3a57a 100644 --- a/cmd/loop/staticaddr.go +++ b/cmd/loop/staticaddr.go @@ -9,21 +9,55 @@ import ( "strings" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/lightninglabs/loop/labels" "github.com/lightninglabs/loop/looprpc" + "github.com/lightninglabs/loop/staticaddr/loopin" + "github.com/lightninglabs/loop/swapserverrpc" + "github.com/lightningnetwork/lnd/routing/route" "github.com/urfave/cli" ) var staticAddressCommands = cli.Command{ Name: "static", ShortName: "s", - Usage: "manage static loop-in addresses", - Category: "StaticAddress", + Usage: "perform on-chain to off-chain swaps using static addresses.", Subcommands: []cli.Command{ newStaticAddressCommand, listUnspentCommand, withdrawalCommand, summaryCommand, }, + Description: ` + Requests a loop-in swap based on static address deposits. After the + creation of a static address funds can be send to it. Once the funds are + confirmed on-chain they can be swapped instantaneously. If deposited + funds are not needed they can we withdrawn back to the local lnd wallet. + `, + Flags: []cli.Flag{ + cli.StringSliceFlag{ + Name: "utxo", + Usage: "specify the utxos of deposits as " + + "outpoints(tx:idx) that should be looped in.", + }, + cli.BoolFlag{ + Name: "all", + Usage: "loop in all static address deposits.", + }, + cli.DurationFlag{ + Name: "payment_timeout", + Usage: "the maximum time in seconds that the server " + + "is allowed to take for the swap payment. " + + "The client can retry the swap with adjusted " + + "parameters after the payment timed out.", + }, + lastHopFlag, + labelFlag, + routeHintsFlag, + privateFlag, + forceFlag, + verboseFlag, + }, + Action: staticAddressLoopIn, } var newStaticAddressCommand = cli.Command{ @@ -169,10 +203,11 @@ func withdraw(ctx *cli.Context) error { return fmt.Errorf("unknown withdrawal request") } - resp, err := client.WithdrawDeposits(ctxb, &looprpc.WithdrawDepositsRequest{ - Outpoints: outpoints, - All: isAllSelected, - }) + resp, err := client.WithdrawDeposits(ctxb, + &looprpc.WithdrawDepositsRequest{ + Outpoints: outpoints, + All: isAllSelected, + }) if err != nil { return err } @@ -194,10 +229,14 @@ var summaryCommand = cli.Command{ cli.StringFlag{ Name: "filter", Usage: "specify a filter to only display deposits in " + - "the specified state. The state can be one " + - "of [deposited|withdrawing|withdrawn|" + - "publish_expired_deposit|" + - "wait_for_expiry_sweep|expired|failed].", + "the specified state. Leaving out the filter " + + "returns all deposits.\nThe state can be one " + + "of the following: \n" + + "deposited\nwithdrawing\nwithdrawn\n" + + "looping_in\nlooped_in\n" + + "publish_expired_deposit\n" + + "sweep_htlc_timeout\nhtlc_timeout_swept\n" + + "wait_for_expiry_sweep\nexpired\nfailed\n.", }, }, Action: summary, @@ -229,9 +268,21 @@ func summary(ctx *cli.Context) error { case "withdrawn": filterState = looprpc.DepositState_WITHDRAWN + case "looping_in": + filterState = looprpc.DepositState_LOOPING_IN + + case "looped_in": + filterState = looprpc.DepositState_LOOPED_IN + case "publish_expired_deposit": filterState = looprpc.DepositState_PUBLISH_EXPIRED + case "sweep_htlc_timeout": + filterState = looprpc.DepositState_SWEEP_HTLC_TIMEOUT + + case "htlc_timeout_swept": + filterState = looprpc.DepositState_HTLC_TIMEOUT_SWEPT + case "wait_for_expiry_sweep": filterState = looprpc.DepositState_WAIT_FOR_EXPIRY_SWEEP @@ -294,3 +345,173 @@ func NewProtoOutPoint(op string) (*looprpc.OutPoint, error) { OutputIndex: uint32(outputIndex), }, nil } + +func staticAddressLoopIn(ctx *cli.Context) error { + if ctx.NumFlags() == 0 && ctx.NArg() == 0 { + return cli.ShowAppHelp(ctx) + } + + client, cleanup, err := getClient(ctx) + if err != nil { + return err + } + defer cleanup() + + var ( + ctxb = context.Background() + isAllSelected = ctx.IsSet("all") + isUtxoSelected = ctx.IsSet("utxo") + label = ctx.String("static-loop-in") + hints []*swapserverrpc.RouteHint + lastHop []byte + paymentTimeoutSeconds = uint32(loopin.DefaultPaymentTimeoutSeconds) + ) + + // Validate our label early so that we can fail before getting a quote. + if err := labels.Validate(label); err != nil { + return err + } + + // Private and route hints are mutually exclusive as setting private + // means we retrieve our own route hints from the connected node. + hints, err = validateRouteHints(ctx) + if err != nil { + return err + } + + if ctx.IsSet(lastHopFlag.Name) { + lastHopVertex, err := route.NewVertexFromStr( + ctx.String(lastHopFlag.Name), + ) + if err != nil { + return err + } + + lastHop = lastHopVertex[:] + } + + // Get the amount we need to quote for. + summaryResp, err := client.GetStaticAddressSummary( + ctxb, &looprpc.StaticAddressSummaryRequest{ + StateFilter: looprpc.DepositState_DEPOSITED, + }, + ) + if err != nil { + return err + } + + var depositOutpoints []string + switch { + case isAllSelected == isUtxoSelected: + return errors.New("must select either all or some utxos") + + case isAllSelected: + depositOutpoints = depositsToOutpoints( + summaryResp.FilteredDeposits, + ) + + case isUtxoSelected: + depositOutpoints = ctx.StringSlice("utxo") + + default: + return fmt.Errorf("unknown quote request") + } + + if containsDuplicates(depositOutpoints) { + return errors.New("duplicate outpoints detected") + } + + quoteReq := &looprpc.QuoteRequest{ + LoopInRouteHints: hints, + LoopInLastHop: lastHop, + Private: ctx.Bool(privateFlag.Name), + DepositOutpoints: depositOutpoints, + } + quote, err := client.GetLoopInQuote(ctxb, quoteReq) + if err != nil { + return err + } + + limits := getInLimits(quote) + + // populate the quote request with the sum of selected deposits and + // prompt the user for acceptance. + quoteReq.Amt, err = sumDeposits( + depositOutpoints, summaryResp.FilteredDeposits, + ) + if err != nil { + return err + } + + if !(ctx.Bool("force") || ctx.Bool("f")) { + err = displayInDetails(quoteReq, quote, ctx.Bool("verbose")) + if err != nil { + return err + } + } + + if ctx.IsSet("payment_timeout") { + paymentTimeoutSeconds = uint32(ctx.Duration("payment_timeout").Seconds()) + } + + req := &looprpc.StaticAddressLoopInRequest{ + Outpoints: depositOutpoints, + MaxSwapFeeSatoshis: int64(limits.maxSwapFee), + LastHop: lastHop, + Label: ctx.String(labelFlag.Name), + Initiator: defaultInitiator, + RouteHints: hints, + Private: ctx.Bool("private"), + PaymentTimeoutSeconds: paymentTimeoutSeconds, + } + + resp, err := client.StaticAddressLoopIn(ctxb, req) + if err != nil { + return err + } + + printRespJSON(resp) + + return nil +} + +func containsDuplicates(outpoints []string) bool { + found := make(map[string]struct{}) + for _, outpoint := range outpoints { + if _, ok := found[outpoint]; ok { + return true + } + found[outpoint] = struct{}{} + } + + return false +} + +func sumDeposits(outpoints []string, deposits []*looprpc.Deposit) (int64, + error) { + + var sum int64 + depositMap := make(map[string]*looprpc.Deposit) + for _, deposit := range deposits { + depositMap[deposit.Outpoint] = deposit + } + + for _, outpoint := range outpoints { + if _, ok := depositMap[outpoint]; !ok { + return 0, fmt.Errorf("deposit %v not found", outpoint) + } + + sum += depositMap[outpoint].Value + } + + return sum, nil +} + +func depositsToOutpoints(deposits []*looprpc.Deposit) []string { + outpoints := make([]string, 0, len(deposits)) + for _, deposit := range deposits { + outpoints = append(outpoints, deposit.Outpoint) + } + + return outpoints +}