diff --git a/config.go b/config.go index 189046374..b4e9d800e 100644 --- a/config.go +++ b/config.go @@ -195,6 +195,8 @@ type Config struct { RfqManager *rfq.Manager + PriceOracle rfq.PriceOracle + UniverseStats universe.Telemetry AuxLeafSigner *tapchannel.AuxLeafSigner diff --git a/perms/perms.go b/perms/perms.go index 91bff0d6f..0b755adc0 100644 --- a/perms/perms.go +++ b/perms/perms.go @@ -280,6 +280,10 @@ var ( Entity: "channels", Action: "write", }}, + "/tapchannelrpc.TaprootAssetChannels/DecodeAssetPayReq": {{ + Entity: "channels", + Action: "read", + }}, "/tapchannelrpc.TaprootAssetChannels/EncodeCustomRecords": { // This RPC is completely stateless and doesn't require // any permissions to use. diff --git a/rpcserver.go b/rpcserver.go index 3408aa65c..42eb49fcb 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -7822,3 +7822,146 @@ func (r *rpcServer) getInboundPolicy(ctx context.Context, chanID uint64, return policy, nil } + +// assetInvoiceAmt calculates the amount of asset units to pay for an invoice +// which is expressed in sats. +func (r *rpcServer) assetInvoiceAmt(ctx context.Context, + targetAsset asset.Specifier, + invoiceAmt lnwire.MilliSatoshi) (uint64, error) { + + oracle := r.cfg.PriceOracle + + oracleResp, err := oracle.QueryAskPrice( + ctx, targetAsset, fn.None[uint64](), fn.Some(invoiceAmt), + fn.None[rfqmsg.AssetRate](), + ) + if err != nil { + return 0, fmt.Errorf("error querying ask price: %w", err) + } + if oracleResp.Err != nil { + return 0, fmt.Errorf("error querying ask price: %w", + oracleResp.Err) + } + + assetRate := oracleResp.AssetRate.Rate + + numAssetUnits := rfqmath.MilliSatoshiToUnits( + invoiceAmt, assetRate, + ).ScaleTo(0) + + return numAssetUnits.ToUint64(), nil +} + +// DecodeAssetPayReq decodes an incoming invoice, then uses the RFQ system to +// map the BTC amount to the amount of asset units for the specified asset ID. +func (r *rpcServer) DecodeAssetPayReq(ctx context.Context, + payReq *tchrpc.AssetPayReq) (*tchrpc.AssetPayReqResponse, error) { + + if r.cfg.PriceOracle == nil { + return nil, fmt.Errorf("price oracle is not set") + } + + // First, we'll perform some basic input validation. + switch { + case len(payReq.AssetId) == 0: + return nil, fmt.Errorf("asset ID must be specified") + + case len(payReq.AssetId) != 32: + return nil, fmt.Errorf("asset ID must be 32 bytes, "+ + "was %d", len(payReq.AssetId)) + + case len(payReq.PayReqString) == 0: + return nil, fmt.Errorf("payment request must be specified") + } + + var ( + resp tchrpc.AssetPayReqResponse + assetID asset.ID + ) + + copy(assetID[:], payReq.AssetId) + + // With the inputs validated, we'll first call out to lnd to decode the + // payment request. + rpcCtx, _, rawClient := r.cfg.Lnd.Client.RawClientWithMacAuth(ctx) + payReqInfo, err := rawClient.DecodePayReq(rpcCtx, &lnrpc.PayReqString{ + PayReq: payReq.PayReqString, + }) + if err != nil { + return nil, fmt.Errorf("unable to fetch channel: %w", err) + } + + resp.PayReq = payReqInfo + + // Next, we'll fetch the information for this asset ID through the addr + // book. This'll automatically fetch the asset if needed. + assetGroup, err := r.cfg.AddrBook.QueryAssetInfo(ctx, assetID) + if err != nil { + return nil, fmt.Errorf("unable to fetch asset info for "+ + "asset_id=%x: %w", assetID[:], err) + } + + resp.GenesisInfo = &taprpc.GenesisInfo{ + GenesisPoint: assetGroup.FirstPrevOut.String(), + AssetType: taprpc.AssetType(assetGroup.Type), + Name: assetGroup.Tag, + MetaHash: assetGroup.MetaHash[:], + AssetId: assetID[:], + } + + // If this asset ID belongs to an asset group, then we'll display thiat + // information as well. + // + // nolint:lll + if assetGroup.GroupKey != nil { + groupInfo := assetGroup.GroupKey + resp.AssetGroup = &taprpc.AssetGroup{ + RawGroupKey: groupInfo.RawKey.PubKey.SerializeCompressed(), + TweakedGroupKey: groupInfo.GroupPubKey.SerializeCompressed(), + TapscriptRoot: groupInfo.TapscriptRoot, + } + + if len(groupInfo.Witness) != 0 { + resp.AssetGroup.AssetWitness, err = asset.SerializeGroupWitness( + groupInfo.Witness, + ) + if err != nil { + return nil, err + } + } + } + + // Now that we have the basic invoice information, we'll query the RFQ + // system to obtain a quote to send this amount of BTC. Note that this + // doesn't factor in the fee limit, so this attempts just to map the + // sats amount to an asset unit. + numMsat := lnwire.NewMSatFromSatoshis( + btcutil.Amount(payReqInfo.NumSatoshis), + ) + targetAsset := asset.NewSpecifierOptionalGroupKey( + assetGroup.ID(), assetGroup.GroupKey, + ) + invoiceAmt, err := r.assetInvoiceAmt(ctx, targetAsset, numMsat) + if err != nil { + return nil, fmt.Errorf("error deriving asset amount: %w", err) + } + + resp.AssetAmount = invoiceAmt + + // The final piece of information we need is the decimal display + // information for this asset ID. + decDisplay, err := r.DecDisplayForAssetID(ctx, assetID) + if err != nil { + return nil, err + } + + resp.DecimalDisplay = fn.MapOptionZ( + decDisplay, func(d uint32) *taprpc.DecimalDisplay { + return &taprpc.DecimalDisplay{ + DecimalDisplay: d, + } + }, + ) + + return &resp, nil +} diff --git a/tapcfg/server.go b/tapcfg/server.go index c3204177f..131075a66 100644 --- a/tapcfg/server.go +++ b/tapcfg/server.go @@ -582,6 +582,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger, UniverseQueriesPerSecond: cfg.Universe.UniverseQueriesPerSecond, UniverseQueriesBurst: cfg.Universe.UniverseQueriesBurst, RfqManager: rfqManager, + PriceOracle: priceOracle, AuxLeafSigner: auxLeafSigner, AuxFundingController: auxFundingController, AuxChanCloser: auxChanCloser, diff --git a/taprpc/tapchannelrpc/tapchannel.pb.go b/taprpc/tapchannelrpc/tapchannel.pb.go index f109c7263..be64f1542 100644 --- a/taprpc/tapchannelrpc/tapchannel.pb.go +++ b/taprpc/tapchannelrpc/tapchannel.pb.go @@ -7,6 +7,7 @@ package tapchannelrpc import ( + taprpc "github.com/lightninglabs/taproot-assets/taprpc" rfqrpc "github.com/lightninglabs/taproot-assets/taprpc/rfqrpc" lnrpc "github.com/lightningnetwork/lnd/lnrpc" routerrpc "github.com/lightningnetwork/lnd/lnrpc/routerrpc" @@ -746,6 +747,149 @@ func (x *AddInvoiceResponse) GetInvoiceResult() *lnrpc.AddInvoiceResponse { return nil } +type AssetPayReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The asset ID that will be used to resolve the invoice's satoshi amount. + AssetId []byte `protobuf:"bytes,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` + // The normal LN invoice that whose amount will be mapped to units of the + // asset ID. + PayReqString string `protobuf:"bytes,2,opt,name=pay_req_string,json=payReqString,proto3" json:"pay_req_string,omitempty"` +} + +func (x *AssetPayReq) Reset() { + *x = AssetPayReq{} + if protoimpl.UnsafeEnabled { + mi := &file_tapchannelrpc_tapchannel_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AssetPayReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AssetPayReq) ProtoMessage() {} + +func (x *AssetPayReq) ProtoReflect() protoreflect.Message { + mi := &file_tapchannelrpc_tapchannel_proto_msgTypes[10] + 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 AssetPayReq.ProtoReflect.Descriptor instead. +func (*AssetPayReq) Descriptor() ([]byte, []int) { + return file_tapchannelrpc_tapchannel_proto_rawDescGZIP(), []int{10} +} + +func (x *AssetPayReq) GetAssetId() []byte { + if x != nil { + return x.AssetId + } + return nil +} + +func (x *AssetPayReq) GetPayReqString() string { + if x != nil { + return x.PayReqString + } + return "" +} + +type AssetPayReqResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The invoice amount, expressed in asset units. + AssetAmount uint64 `protobuf:"varint,1,opt,name=asset_amount,json=assetAmount,proto3" json:"asset_amount,omitempty"` + // The decimal display corresponding to the asset_id. + DecimalDisplay *taprpc.DecimalDisplay `protobuf:"bytes,2,opt,name=decimal_display,json=decimalDisplay,proto3" json:"decimal_display,omitempty"` + // The group the asset ID belong to, if applicable. + AssetGroup *taprpc.AssetGroup `protobuf:"bytes,3,opt,name=asset_group,json=assetGroup,proto3" json:"asset_group,omitempty"` + // Genesis information for the asset ID which includes the meta hash, and + // asset ID. + GenesisInfo *taprpc.GenesisInfo `protobuf:"bytes,4,opt,name=genesis_info,json=genesisInfo,proto3" json:"genesis_info,omitempty"` + // The normal decoded payment request. + PayReq *lnrpc.PayReq `protobuf:"bytes,5,opt,name=pay_req,json=payReq,proto3" json:"pay_req,omitempty"` +} + +func (x *AssetPayReqResponse) Reset() { + *x = AssetPayReqResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_tapchannelrpc_tapchannel_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AssetPayReqResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AssetPayReqResponse) ProtoMessage() {} + +func (x *AssetPayReqResponse) ProtoReflect() protoreflect.Message { + mi := &file_tapchannelrpc_tapchannel_proto_msgTypes[11] + 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 AssetPayReqResponse.ProtoReflect.Descriptor instead. +func (*AssetPayReqResponse) Descriptor() ([]byte, []int) { + return file_tapchannelrpc_tapchannel_proto_rawDescGZIP(), []int{11} +} + +func (x *AssetPayReqResponse) GetAssetAmount() uint64 { + if x != nil { + return x.AssetAmount + } + return 0 +} + +func (x *AssetPayReqResponse) GetDecimalDisplay() *taprpc.DecimalDisplay { + if x != nil { + return x.DecimalDisplay + } + return nil +} + +func (x *AssetPayReqResponse) GetAssetGroup() *taprpc.AssetGroup { + if x != nil { + return x.AssetGroup + } + return nil +} + +func (x *AssetPayReqResponse) GetGenesisInfo() *taprpc.GenesisInfo { + if x != nil { + return x.GenesisInfo + } + return nil +} + +func (x *AssetPayReqResponse) GetPayReq() *lnrpc.PayReq { + if x != nil { + return x.PayReq + } + return nil +} + var File_tapchannelrpc_tapchannel_proto protoreflect.FileDescriptor var file_tapchannelrpc_tapchannel_proto_rawDesc = []byte{ @@ -755,141 +899,170 @@ var file_tapchannelrpc_tapchannel_proto_rawDesc = []byte{ 0x10, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x66, 0x71, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x16, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc2, 0x01, 0x0a, 0x12, 0x46, - 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, - 0x1f, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, - 0x12, 0x32, 0x0a, 0x16, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x61, 0x74, - 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x12, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, - 0x62, 0x79, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x73, 0x61, 0x74, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x70, 0x75, 0x73, 0x68, 0x53, 0x61, 0x74, 0x22, - 0x4c, 0x0a, 0x13, 0x46, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xcc, 0x01, - 0x0a, 0x15, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x5b, 0x0a, 0x0d, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, - 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x66, 0x71, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x66, 0x71, 0x49, 0x64, 0x1a, 0x3f, 0x0a, 0x11, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x7d, 0x0a, 0x1a, - 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, - 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x56, 0x0a, 0x13, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x65, - 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, - 0x11, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0xc5, 0x01, 0x0a, 0x1b, - 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, - 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x0e, 0x63, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, - 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, - 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0xf7, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x61, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, - 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, - 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x46, 0x0a, 0x0f, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x66, 0x71, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x05, 0x72, 0x66, 0x71, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x70, 0x61, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4f, 0x76, 0x65, 0x72, 0x70, 0x61, 0x79, 0x22, 0xa9, 0x01, - 0x0a, 0x13, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, - 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, - 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, - 0x65, 0x48, 0x00, 0x52, 0x11, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, - 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x00, - 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x42, - 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x30, 0x0a, 0x0b, 0x48, 0x6f, 0x64, - 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, - 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x22, 0xea, 0x01, 0x0a, 0x11, - 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x1f, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, - 0x12, 0x37, 0x0a, 0x0f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x0e, 0x69, 0x6e, 0x76, 0x6f, 0x69, - 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x0c, 0x68, 0x6f, 0x64, - 0x6c, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, - 0x48, 0x6f, 0x64, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x0b, 0x68, 0x6f, 0x64, - 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x22, 0xa2, 0x01, 0x0a, 0x12, 0x41, 0x64, 0x64, - 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x4a, 0x0a, 0x12, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x79, 0x5f, - 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, - 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, - 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x10, 0x61, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x40, 0x0a, 0x0e, 0x69, - 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, - 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0d, - 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x32, 0x85, 0x03, - 0x0a, 0x14, 0x54, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x54, 0x0a, 0x0b, 0x46, 0x75, 0x6e, 0x64, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x21, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6c, 0x0a, 0x13, - 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, - 0x72, 0x64, 0x73, 0x12, 0x29, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, - 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, - 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x45, - 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, - 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x0b, 0x53, 0x65, - 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x2e, 0x74, 0x61, 0x70, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, - 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, - 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x30, 0x01, 0x12, 0x51, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x12, 0x20, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, - 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, - 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3e, 0x5a, 0x3c, 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, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, - 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2f, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x13, 0x74, 0x61, 0x70, 0x72, + 0x6f, 0x6f, 0x74, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0xc2, 0x01, 0x0a, 0x12, 0x46, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, + 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x50, + 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x16, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, + 0x65, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x53, 0x61, + 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x75, 0x73, + 0x68, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x70, 0x75, 0x73, + 0x68, 0x53, 0x61, 0x74, 0x22, 0x4c, 0x0a, 0x13, 0x46, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, + 0x21, 0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x22, 0xcc, 0x01, 0x0a, 0x15, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e, + 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x5b, 0x0a, 0x0d, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x41, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x66, 0x71, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x66, 0x71, 0x49, 0x64, + 0x1a, 0x3f, 0x0a, 0x11, 0x41, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x7d, 0x0a, 0x1a, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x56, 0x0a, 0x13, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, + 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x00, 0x52, 0x11, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x64, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x22, 0xc5, 0x01, 0x0a, 0x1b, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x64, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x43, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf7, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x6e, + 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, + 0x0b, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x46, + 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x66, 0x71, 0x5f, 0x69, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x66, 0x71, 0x49, 0x64, 0x12, 0x23, 0x0a, + 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x70, 0x61, 0x79, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4f, 0x76, 0x65, 0x72, 0x70, + 0x61, 0x79, 0x22, 0xa9, 0x01, 0x0a, 0x13, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x13, 0x61, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, + 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x48, 0x00, 0x52, 0x11, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, + 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x0e, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x30, + 0x0a, 0x0b, 0x48, 0x6f, 0x64, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x21, 0x0a, + 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, + 0x22, 0xea, 0x01, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, + 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, + 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x50, + 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x37, 0x0a, 0x0f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x0e, + 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3d, + 0x0a, 0x0c, 0x68, 0x6f, 0x64, 0x6c, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x6f, 0x64, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x52, 0x0b, 0x68, 0x6f, 0x64, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x22, 0xa2, 0x01, + 0x0a, 0x12, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x12, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, + 0x5f, 0x62, 0x75, 0x79, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x10, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, + 0x12, 0x40, 0x0a, 0x0e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x52, 0x0d, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x22, 0x4e, 0x0a, 0x0b, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x61, 0x79, 0x52, 0x65, + 0x71, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0e, + 0x70, 0x61, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x22, 0x8e, 0x02, 0x0a, 0x13, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x61, 0x79, 0x52, + 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3f, 0x0a, + 0x0f, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, + 0x44, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x0e, + 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x33, + 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x12, 0x36, 0x0a, 0x0c, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x61, 0x70, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, + 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x26, 0x0a, 0x07, 0x70, + 0x61, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x52, 0x06, 0x70, 0x61, 0x79, + 0x52, 0x65, 0x71, 0x32, 0xda, 0x03, 0x0a, 0x14, 0x54, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x54, 0x0a, 0x0b, + 0x46, 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x21, 0x2e, 0x74, 0x61, + 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, + 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x75, 0x6e, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x6c, 0x0a, 0x13, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x29, 0x2e, 0x74, 0x61, 0x70, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, + 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x56, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, + 0x21, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x51, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x49, + 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x20, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, + 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x11, 0x44, + 0x65, 0x63, 0x6f, 0x64, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, + 0x12, 0x1a, 0x2e, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x22, 0x2e, 0x74, + 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x42, 0x3e, 0x5a, 0x3c, 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, 0x74, 0x61, 0x70, + 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, + 0x70, 0x63, 0x2f, 0x74, 0x61, 0x70, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x72, 0x70, 0x63, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -904,7 +1077,7 @@ func file_tapchannelrpc_tapchannel_proto_rawDescGZIP() []byte { return file_tapchannelrpc_tapchannel_proto_rawDescData } -var file_tapchannelrpc_tapchannel_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_tapchannelrpc_tapchannel_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_tapchannelrpc_tapchannel_proto_goTypes = []interface{}{ (*FundChannelRequest)(nil), // 0: tapchannelrpc.FundChannelRequest (*FundChannelResponse)(nil), // 1: tapchannelrpc.FundChannelResponse @@ -916,39 +1089,51 @@ var file_tapchannelrpc_tapchannel_proto_goTypes = []interface{}{ (*HodlInvoice)(nil), // 7: tapchannelrpc.HodlInvoice (*AddInvoiceRequest)(nil), // 8: tapchannelrpc.AddInvoiceRequest (*AddInvoiceResponse)(nil), // 9: tapchannelrpc.AddInvoiceResponse - nil, // 10: tapchannelrpc.RouterSendPaymentData.AssetAmountsEntry - nil, // 11: tapchannelrpc.EncodeCustomRecordsResponse.CustomRecordsEntry - (*routerrpc.SendPaymentRequest)(nil), // 12: routerrpc.SendPaymentRequest - (*rfqrpc.PeerAcceptedSellQuote)(nil), // 13: rfqrpc.PeerAcceptedSellQuote - (*lnrpc.Payment)(nil), // 14: lnrpc.Payment - (*lnrpc.Invoice)(nil), // 15: lnrpc.Invoice - (*rfqrpc.PeerAcceptedBuyQuote)(nil), // 16: rfqrpc.PeerAcceptedBuyQuote - (*lnrpc.AddInvoiceResponse)(nil), // 17: lnrpc.AddInvoiceResponse + (*AssetPayReq)(nil), // 10: tapchannelrpc.AssetPayReq + (*AssetPayReqResponse)(nil), // 11: tapchannelrpc.AssetPayReqResponse + nil, // 12: tapchannelrpc.RouterSendPaymentData.AssetAmountsEntry + nil, // 13: tapchannelrpc.EncodeCustomRecordsResponse.CustomRecordsEntry + (*routerrpc.SendPaymentRequest)(nil), // 14: routerrpc.SendPaymentRequest + (*rfqrpc.PeerAcceptedSellQuote)(nil), // 15: rfqrpc.PeerAcceptedSellQuote + (*lnrpc.Payment)(nil), // 16: lnrpc.Payment + (*lnrpc.Invoice)(nil), // 17: lnrpc.Invoice + (*rfqrpc.PeerAcceptedBuyQuote)(nil), // 18: rfqrpc.PeerAcceptedBuyQuote + (*lnrpc.AddInvoiceResponse)(nil), // 19: lnrpc.AddInvoiceResponse + (*taprpc.DecimalDisplay)(nil), // 20: taprpc.DecimalDisplay + (*taprpc.AssetGroup)(nil), // 21: taprpc.AssetGroup + (*taprpc.GenesisInfo)(nil), // 22: taprpc.GenesisInfo + (*lnrpc.PayReq)(nil), // 23: lnrpc.PayReq } var file_tapchannelrpc_tapchannel_proto_depIdxs = []int32{ - 10, // 0: tapchannelrpc.RouterSendPaymentData.asset_amounts:type_name -> tapchannelrpc.RouterSendPaymentData.AssetAmountsEntry + 12, // 0: tapchannelrpc.RouterSendPaymentData.asset_amounts:type_name -> tapchannelrpc.RouterSendPaymentData.AssetAmountsEntry 2, // 1: tapchannelrpc.EncodeCustomRecordsRequest.router_send_payment:type_name -> tapchannelrpc.RouterSendPaymentData - 11, // 2: tapchannelrpc.EncodeCustomRecordsResponse.custom_records:type_name -> tapchannelrpc.EncodeCustomRecordsResponse.CustomRecordsEntry - 12, // 3: tapchannelrpc.SendPaymentRequest.payment_request:type_name -> routerrpc.SendPaymentRequest - 13, // 4: tapchannelrpc.SendPaymentResponse.accepted_sell_order:type_name -> rfqrpc.PeerAcceptedSellQuote - 14, // 5: tapchannelrpc.SendPaymentResponse.payment_result:type_name -> lnrpc.Payment - 15, // 6: tapchannelrpc.AddInvoiceRequest.invoice_request:type_name -> lnrpc.Invoice + 13, // 2: tapchannelrpc.EncodeCustomRecordsResponse.custom_records:type_name -> tapchannelrpc.EncodeCustomRecordsResponse.CustomRecordsEntry + 14, // 3: tapchannelrpc.SendPaymentRequest.payment_request:type_name -> routerrpc.SendPaymentRequest + 15, // 4: tapchannelrpc.SendPaymentResponse.accepted_sell_order:type_name -> rfqrpc.PeerAcceptedSellQuote + 16, // 5: tapchannelrpc.SendPaymentResponse.payment_result:type_name -> lnrpc.Payment + 17, // 6: tapchannelrpc.AddInvoiceRequest.invoice_request:type_name -> lnrpc.Invoice 7, // 7: tapchannelrpc.AddInvoiceRequest.hodl_invoice:type_name -> tapchannelrpc.HodlInvoice - 16, // 8: tapchannelrpc.AddInvoiceResponse.accepted_buy_quote:type_name -> rfqrpc.PeerAcceptedBuyQuote - 17, // 9: tapchannelrpc.AddInvoiceResponse.invoice_result:type_name -> lnrpc.AddInvoiceResponse - 0, // 10: tapchannelrpc.TaprootAssetChannels.FundChannel:input_type -> tapchannelrpc.FundChannelRequest - 3, // 11: tapchannelrpc.TaprootAssetChannels.EncodeCustomRecords:input_type -> tapchannelrpc.EncodeCustomRecordsRequest - 5, // 12: tapchannelrpc.TaprootAssetChannels.SendPayment:input_type -> tapchannelrpc.SendPaymentRequest - 8, // 13: tapchannelrpc.TaprootAssetChannels.AddInvoice:input_type -> tapchannelrpc.AddInvoiceRequest - 1, // 14: tapchannelrpc.TaprootAssetChannels.FundChannel:output_type -> tapchannelrpc.FundChannelResponse - 4, // 15: tapchannelrpc.TaprootAssetChannels.EncodeCustomRecords:output_type -> tapchannelrpc.EncodeCustomRecordsResponse - 6, // 16: tapchannelrpc.TaprootAssetChannels.SendPayment:output_type -> tapchannelrpc.SendPaymentResponse - 9, // 17: tapchannelrpc.TaprootAssetChannels.AddInvoice:output_type -> tapchannelrpc.AddInvoiceResponse - 14, // [14:18] is the sub-list for method output_type - 10, // [10:14] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + 18, // 8: tapchannelrpc.AddInvoiceResponse.accepted_buy_quote:type_name -> rfqrpc.PeerAcceptedBuyQuote + 19, // 9: tapchannelrpc.AddInvoiceResponse.invoice_result:type_name -> lnrpc.AddInvoiceResponse + 20, // 10: tapchannelrpc.AssetPayReqResponse.decimal_display:type_name -> taprpc.DecimalDisplay + 21, // 11: tapchannelrpc.AssetPayReqResponse.asset_group:type_name -> taprpc.AssetGroup + 22, // 12: tapchannelrpc.AssetPayReqResponse.genesis_info:type_name -> taprpc.GenesisInfo + 23, // 13: tapchannelrpc.AssetPayReqResponse.pay_req:type_name -> lnrpc.PayReq + 0, // 14: tapchannelrpc.TaprootAssetChannels.FundChannel:input_type -> tapchannelrpc.FundChannelRequest + 3, // 15: tapchannelrpc.TaprootAssetChannels.EncodeCustomRecords:input_type -> tapchannelrpc.EncodeCustomRecordsRequest + 5, // 16: tapchannelrpc.TaprootAssetChannels.SendPayment:input_type -> tapchannelrpc.SendPaymentRequest + 8, // 17: tapchannelrpc.TaprootAssetChannels.AddInvoice:input_type -> tapchannelrpc.AddInvoiceRequest + 10, // 18: tapchannelrpc.TaprootAssetChannels.DecodeAssetPayReq:input_type -> tapchannelrpc.AssetPayReq + 1, // 19: tapchannelrpc.TaprootAssetChannels.FundChannel:output_type -> tapchannelrpc.FundChannelResponse + 4, // 20: tapchannelrpc.TaprootAssetChannels.EncodeCustomRecords:output_type -> tapchannelrpc.EncodeCustomRecordsResponse + 6, // 21: tapchannelrpc.TaprootAssetChannels.SendPayment:output_type -> tapchannelrpc.SendPaymentResponse + 9, // 22: tapchannelrpc.TaprootAssetChannels.AddInvoice:output_type -> tapchannelrpc.AddInvoiceResponse + 11, // 23: tapchannelrpc.TaprootAssetChannels.DecodeAssetPayReq:output_type -> tapchannelrpc.AssetPayReqResponse + 19, // [19:24] is the sub-list for method output_type + 14, // [14:19] is the sub-list for method input_type + 14, // [14:14] is the sub-list for extension type_name + 14, // [14:14] is the sub-list for extension extendee + 0, // [0:14] is the sub-list for field type_name } func init() { file_tapchannelrpc_tapchannel_proto_init() } @@ -1077,6 +1262,30 @@ func file_tapchannelrpc_tapchannel_proto_init() { return nil } } + file_tapchannelrpc_tapchannel_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AssetPayReq); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tapchannelrpc_tapchannel_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AssetPayReqResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_tapchannelrpc_tapchannel_proto_msgTypes[3].OneofWrappers = []interface{}{ (*EncodeCustomRecordsRequest_RouterSendPayment)(nil), @@ -1091,7 +1300,7 @@ func file_tapchannelrpc_tapchannel_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_tapchannelrpc_tapchannel_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 14, NumExtensions: 0, NumServices: 1, }, diff --git a/taprpc/tapchannelrpc/tapchannel.pb.gw.go b/taprpc/tapchannelrpc/tapchannel.pb.gw.go index 525410927..ca89998b1 100644 --- a/taprpc/tapchannelrpc/tapchannel.pb.gw.go +++ b/taprpc/tapchannelrpc/tapchannel.pb.gw.go @@ -158,6 +158,40 @@ func local_request_TaprootAssetChannels_AddInvoice_0(ctx context.Context, marsha } +func request_TaprootAssetChannels_DecodeAssetPayReq_0(ctx context.Context, marshaler runtime.Marshaler, client TaprootAssetChannelsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AssetPayReq + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.DecodeAssetPayReq(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_TaprootAssetChannels_DecodeAssetPayReq_0(ctx context.Context, marshaler runtime.Marshaler, server TaprootAssetChannelsServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AssetPayReq + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.DecodeAssetPayReq(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterTaprootAssetChannelsHandlerServer registers the http handlers for service TaprootAssetChannels to "mux". // UnaryRPC :call TaprootAssetChannelsServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -246,6 +280,31 @@ func RegisterTaprootAssetChannelsHandlerServer(ctx context.Context, mux *runtime }) + mux.Handle("POST", pattern_TaprootAssetChannels_DecodeAssetPayReq_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/tapchannelrpc.TaprootAssetChannels/DecodeAssetPayReq", runtime.WithHTTPPathPattern("/v1/taproot-assets/channels/invoice/decode")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_TaprootAssetChannels_DecodeAssetPayReq_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_TaprootAssetChannels_DecodeAssetPayReq_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -375,6 +434,28 @@ func RegisterTaprootAssetChannelsHandlerClient(ctx context.Context, mux *runtime }) + mux.Handle("POST", pattern_TaprootAssetChannels_DecodeAssetPayReq_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/tapchannelrpc.TaprootAssetChannels/DecodeAssetPayReq", runtime.WithHTTPPathPattern("/v1/taproot-assets/channels/invoice/decode")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_TaprootAssetChannels_DecodeAssetPayReq_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_TaprootAssetChannels_DecodeAssetPayReq_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -386,6 +467,8 @@ var ( pattern_TaprootAssetChannels_SendPayment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "taproot-assets", "channels", "send-payment"}, "")) pattern_TaprootAssetChannels_AddInvoice_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "taproot-assets", "channels", "invoice"}, "")) + + pattern_TaprootAssetChannels_DecodeAssetPayReq_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "taproot-assets", "channels", "invoice", "decode"}, "")) ) var ( @@ -396,4 +479,6 @@ var ( forward_TaprootAssetChannels_SendPayment_0 = runtime.ForwardResponseStream forward_TaprootAssetChannels_AddInvoice_0 = runtime.ForwardResponseMessage + + forward_TaprootAssetChannels_DecodeAssetPayReq_0 = runtime.ForwardResponseMessage ) diff --git a/taprpc/tapchannelrpc/tapchannel.proto b/taprpc/tapchannelrpc/tapchannel.proto index f1b8e4759..546d12db9 100644 --- a/taprpc/tapchannelrpc/tapchannel.proto +++ b/taprpc/tapchannelrpc/tapchannel.proto @@ -7,6 +7,7 @@ option go_package = "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrp import "rfqrpc/rfq.proto"; import "lightning.proto"; import "routerrpc/router.proto"; +import "taprootassets.proto"; service TaprootAssetChannels { /* @@ -39,6 +40,13 @@ service TaprootAssetChannels { to the specified asset amount. */ rpc AddInvoice (AddInvoiceRequest) returns (AddInvoiceResponse); + + /* + DecodeAssetPayReq is similar to lnd's lnrpc.DecodePayReq, but it accepts an + asset ID and returns the invoice amount expressed in asset units along side + the normal information. + */ + rpc DecodeAssetPayReq (AssetPayReq) returns (AssetPayReqResponse); } message FundChannelRequest { @@ -189,3 +197,30 @@ message AddInvoiceResponse { // The result of the invoice creation. lnrpc.AddInvoiceResponse invoice_result = 2; } + +message AssetPayReq { + // The asset ID that will be used to resolve the invoice's satoshi amount. + bytes asset_id = 1; + + // The normal LN invoice that whose amount will be mapped to units of the + // asset ID. + string pay_req_string = 2; +} + +message AssetPayReqResponse { + // The invoice amount, expressed in asset units. + uint64 asset_amount = 1; + + // The decimal display corresponding to the asset_id. + taprpc.DecimalDisplay decimal_display = 2; + + // The group the asset ID belong to, if applicable. + taprpc.AssetGroup asset_group = 3; + + // Genesis information for the asset ID which includes the meta hash, and + // asset ID. + taprpc.GenesisInfo genesis_info = 4; + + // The normal decoded payment request. + lnrpc.PayReq pay_req = 5; +} diff --git a/taprpc/tapchannelrpc/tapchannel.swagger.json b/taprpc/tapchannelrpc/tapchannel.swagger.json index f13b7be49..210cd5fe9 100644 --- a/taprpc/tapchannelrpc/tapchannel.swagger.json +++ b/taprpc/tapchannelrpc/tapchannel.swagger.json @@ -115,6 +115,39 @@ ] } }, + "/v1/taproot-assets/channels/invoice/decode": { + "post": { + "summary": "DecodeAssetPayReq is similar to lnd's lnrpc.DecodePayReq, but it accepts an\nasset ID and returns the invoice amount expressed in asset units along side\nthe normal information.", + "operationId": "TaprootAssetChannels_DecodeAssetPayReq", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/tapchannelrpcAssetPayReqResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/tapchannelrpcAssetPayReq" + } + } + ], + "tags": [ + "TaprootAssetChannels" + ] + } + }, "/v1/taproot-assets/channels/send-payment": { "post": { "summary": "SendPayment is a wrapper around lnd's routerrpc.SendPaymentV2 RPC method\nwith asset specific parameters. It allows RPC users to send asset keysend\npayments (direct payments) or payments to an invoice with a specified asset\namount.", @@ -309,6 +342,44 @@ } } }, + "lnrpcBlindedHop": { + "type": "object", + "properties": { + "blinded_node": { + "type": "string", + "format": "byte", + "description": "The blinded public key of the node." + }, + "encrypted_data": { + "type": "string", + "format": "byte", + "description": "An encrypted blob of data provided to the blinded node." + } + } + }, + "lnrpcBlindedPath": { + "type": "object", + "properties": { + "introduction_node": { + "type": "string", + "format": "byte", + "description": "The unblinded pubkey of the introduction node for the route." + }, + "blinding_point": { + "type": "string", + "format": "byte", + "description": "The ephemeral pubkey used by nodes in the blinded route." + }, + "blinded_hops": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/lnrpcBlindedHop" + }, + "description": "A set of blinded node keys and data blobs for the blinded portion of the\nroute. Note that the first hop is expected to be the introduction node,\nso the route is always expected to have at least one hop." + } + } + }, "lnrpcBlindedPathConfig": { "type": "object", "properties": { @@ -337,6 +408,47 @@ } } }, + "lnrpcBlindedPaymentPath": { + "type": "object", + "properties": { + "blinded_path": { + "$ref": "#/definitions/lnrpcBlindedPath", + "description": "The blinded path to send the payment to." + }, + "base_fee_msat": { + "type": "string", + "format": "uint64", + "description": "The base fee for the blinded path provided, expressed in msat." + }, + "proportional_fee_rate": { + "type": "integer", + "format": "int64", + "description": "The proportional fee for the blinded path provided, expressed in parts\nper million." + }, + "total_cltv_delta": { + "type": "integer", + "format": "int64", + "description": "The total CLTV delta for the blinded path provided, including the\nfinal CLTV delta for the receiving node." + }, + "htlc_min_msat": { + "type": "string", + "format": "uint64", + "description": "The minimum hltc size that may be sent over the blinded path, expressed\nin msat." + }, + "htlc_max_msat": { + "type": "string", + "format": "uint64", + "description": "The maximum htlc size that may be sent over the blinded path, expressed\nin msat." + }, + "features": { + "type": "array", + "items": { + "$ref": "#/definitions/lnrpcFeatureBit" + }, + "description": "The feature bits for the route." + } + } + }, "lnrpcChannelUpdate": { "type": "object", "properties": { @@ -880,6 +992,70 @@ } } }, + "lnrpcPayReq": { + "type": "object", + "properties": { + "destination": { + "type": "string" + }, + "payment_hash": { + "type": "string" + }, + "num_satoshis": { + "type": "string", + "format": "int64" + }, + "timestamp": { + "type": "string", + "format": "int64" + }, + "expiry": { + "type": "string", + "format": "int64" + }, + "description": { + "type": "string" + }, + "description_hash": { + "type": "string" + }, + "fallback_addr": { + "type": "string" + }, + "cltv_expiry": { + "type": "string", + "format": "int64" + }, + "route_hints": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/lnrpcRouteHint" + } + }, + "payment_addr": { + "type": "string", + "format": "byte" + }, + "num_msat": { + "type": "string", + "format": "int64" + }, + "features": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lnrpcFeature" + } + }, + "blinded_paths": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/lnrpcBlindedPaymentPath" + } + } + } + }, "lnrpcPayment": { "type": "object", "properties": { @@ -1352,6 +1528,46 @@ } } }, + "tapchannelrpcAssetPayReq": { + "type": "object", + "properties": { + "asset_id": { + "type": "string", + "format": "byte", + "description": "The asset ID that will be used to resolve the invoice's satoshi amount." + }, + "pay_req_string": { + "type": "string", + "description": "The normal LN invoice that whose amount will be mapped to units of the\nasset ID." + } + } + }, + "tapchannelrpcAssetPayReqResponse": { + "type": "object", + "properties": { + "asset_amount": { + "type": "string", + "format": "uint64", + "description": "The invoice amount, expressed in asset units." + }, + "decimal_display": { + "$ref": "#/definitions/taprpcDecimalDisplay", + "description": "The decimal display corresponding to the asset_id." + }, + "asset_group": { + "$ref": "#/definitions/taprpcAssetGroup", + "description": "The group the asset ID belong to, if applicable." + }, + "genesis_info": { + "$ref": "#/definitions/taprpcGenesisInfo", + "description": "Genesis information for the asset ID which includes the meta hash, and\nasset ID." + }, + "pay_req": { + "$ref": "#/definitions/lnrpcPayReq", + "description": "The normal decoded payment request." + } + } + }, "tapchannelrpcEncodeCustomRecordsRequest": { "type": "object", "properties": { @@ -1489,6 +1705,82 @@ "description": "The payment result of a single payment attempt. Multiple attempts may\nbe returned per payment request until either the payment succeeds or\nthe payment times out." } } + }, + "taprpcAssetGroup": { + "type": "object", + "properties": { + "raw_group_key": { + "type": "string", + "format": "byte", + "description": "The raw group key which is a normal public key." + }, + "tweaked_group_key": { + "type": "string", + "format": "byte", + "description": "The tweaked group key, which is derived based on the genesis point and also\nasset type." + }, + "asset_witness": { + "type": "string", + "format": "byte", + "description": "A witness that authorizes a specific asset to be part of the asset group\nspecified by the above key." + }, + "tapscript_root": { + "type": "string", + "format": "byte", + "description": "The root hash of a tapscript tree, which enables future issuance authorized\nwith a script witness." + } + } + }, + "taprpcAssetType": { + "type": "string", + "enum": [ + "NORMAL", + "COLLECTIBLE" + ], + "default": "NORMAL", + "description": " - NORMAL: Indicates that an asset is capable of being split/merged, with each of the\nunits being fungible, even across a key asset ID boundary (assuming the\nkey group is the same).\n - COLLECTIBLE: Indicates that an asset is a collectible, meaning that each of the other\nitems under the same key group are not fully fungible with each other.\nCollectibles also cannot be split or merged." + }, + "taprpcDecimalDisplay": { + "type": "object", + "properties": { + "decimal_display": { + "type": "integer", + "format": "int64", + "description": "Decimal display dictates the number of decimal places to shift the amount to\nthe left converting from Taproot Asset integer representation to a\nUX-recognizable fractional quantity.\n\nFor example, if the decimal_display value is 2 and there's 100 of those\nassets, then a wallet would display the amount as \"1.00\". This field is\nintended as information for wallets that display balances and has no impact\non the behavior of the daemon or any other part of the protocol. This value\nis encoded in the MetaData field as a JSON field, therefore it is only\ncompatible with assets that have a JSON MetaData field." + } + } + }, + "taprpcGenesisInfo": { + "type": "object", + "properties": { + "genesis_point": { + "type": "string", + "description": "The first outpoint of the transaction that created the asset (txid:vout)." + }, + "name": { + "type": "string", + "description": "The name of the asset." + }, + "meta_hash": { + "type": "string", + "format": "byte", + "description": "The hash of the meta data for this genesis asset." + }, + "asset_id": { + "type": "string", + "format": "byte", + "description": "The asset ID that uniquely identifies the asset." + }, + "asset_type": { + "$ref": "#/definitions/taprpcAssetType", + "description": "The type of the asset." + }, + "output_index": { + "type": "integer", + "format": "int64", + "description": "The index of the output that carries the unique Taproot Asset commitment in\nthe genesis transaction." + } + } } } } diff --git a/taprpc/tapchannelrpc/tapchannel.yaml b/taprpc/tapchannelrpc/tapchannel.yaml index c4a87ada7..af8c1924a 100644 --- a/taprpc/tapchannelrpc/tapchannel.yaml +++ b/taprpc/tapchannelrpc/tapchannel.yaml @@ -15,3 +15,6 @@ http: - selector: tapchannelrpc.TaprootAssetChannels.AddInvoice post: "/v1/taproot-assets/channels/invoice" body: "*" + - selector: tapchannelrpc.TaprootAssetChannels.DecodeAssetPayReq + post: "/v1/taproot-assets/channels/invoice/decode" + body: "*" diff --git a/taprpc/tapchannelrpc/tapchannel_grpc.pb.go b/taprpc/tapchannelrpc/tapchannel_grpc.pb.go index d7fb1307b..1da8f92e8 100644 --- a/taprpc/tapchannelrpc/tapchannel_grpc.pb.go +++ b/taprpc/tapchannelrpc/tapchannel_grpc.pb.go @@ -36,6 +36,10 @@ type TaprootAssetChannelsClient interface { // specific parameters. It allows RPC users to create invoices that correspond // to the specified asset amount. AddInvoice(ctx context.Context, in *AddInvoiceRequest, opts ...grpc.CallOption) (*AddInvoiceResponse, error) + // DecodeAssetPayReq is similar to lnd's lnrpc.DecodePayReq, but it accepts an + // asset ID and returns the invoice amount expressed in asset units along side + // the normal information. + DecodeAssetPayReq(ctx context.Context, in *AssetPayReq, opts ...grpc.CallOption) (*AssetPayReqResponse, error) } type taprootAssetChannelsClient struct { @@ -105,6 +109,15 @@ func (c *taprootAssetChannelsClient) AddInvoice(ctx context.Context, in *AddInvo return out, nil } +func (c *taprootAssetChannelsClient) DecodeAssetPayReq(ctx context.Context, in *AssetPayReq, opts ...grpc.CallOption) (*AssetPayReqResponse, error) { + out := new(AssetPayReqResponse) + err := c.cc.Invoke(ctx, "/tapchannelrpc.TaprootAssetChannels/DecodeAssetPayReq", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // TaprootAssetChannelsServer is the server API for TaprootAssetChannels service. // All implementations must embed UnimplementedTaprootAssetChannelsServer // for forward compatibility @@ -127,6 +140,10 @@ type TaprootAssetChannelsServer interface { // specific parameters. It allows RPC users to create invoices that correspond // to the specified asset amount. AddInvoice(context.Context, *AddInvoiceRequest) (*AddInvoiceResponse, error) + // DecodeAssetPayReq is similar to lnd's lnrpc.DecodePayReq, but it accepts an + // asset ID and returns the invoice amount expressed in asset units along side + // the normal information. + DecodeAssetPayReq(context.Context, *AssetPayReq) (*AssetPayReqResponse, error) mustEmbedUnimplementedTaprootAssetChannelsServer() } @@ -146,6 +163,9 @@ func (UnimplementedTaprootAssetChannelsServer) SendPayment(*SendPaymentRequest, func (UnimplementedTaprootAssetChannelsServer) AddInvoice(context.Context, *AddInvoiceRequest) (*AddInvoiceResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddInvoice not implemented") } +func (UnimplementedTaprootAssetChannelsServer) DecodeAssetPayReq(context.Context, *AssetPayReq) (*AssetPayReqResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DecodeAssetPayReq not implemented") +} func (UnimplementedTaprootAssetChannelsServer) mustEmbedUnimplementedTaprootAssetChannelsServer() {} // UnsafeTaprootAssetChannelsServer may be embedded to opt out of forward compatibility for this service. @@ -234,6 +254,24 @@ func _TaprootAssetChannels_AddInvoice_Handler(srv interface{}, ctx context.Conte return interceptor(ctx, in, info, handler) } +func _TaprootAssetChannels_DecodeAssetPayReq_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AssetPayReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TaprootAssetChannelsServer).DecodeAssetPayReq(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tapchannelrpc.TaprootAssetChannels/DecodeAssetPayReq", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TaprootAssetChannelsServer).DecodeAssetPayReq(ctx, req.(*AssetPayReq)) + } + return interceptor(ctx, in, info, handler) +} + // TaprootAssetChannels_ServiceDesc is the grpc.ServiceDesc for TaprootAssetChannels service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -253,6 +291,10 @@ var TaprootAssetChannels_ServiceDesc = grpc.ServiceDesc{ MethodName: "AddInvoice", Handler: _TaprootAssetChannels_AddInvoice_Handler, }, + { + MethodName: "DecodeAssetPayReq", + Handler: _TaprootAssetChannels_DecodeAssetPayReq_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/taprpc/tapchannelrpc/taprootassetchannels.pb.json.go b/taprpc/tapchannelrpc/taprootassetchannels.pb.json.go index 30abf12a5..49e3685ec 100644 --- a/taprpc/tapchannelrpc/taprootassetchannels.pb.json.go +++ b/taprpc/tapchannelrpc/taprootassetchannels.pb.json.go @@ -137,4 +137,29 @@ func RegisterTaprootAssetChannelsJSONCallbacks(registry map[string]func(ctx cont } callback(string(respBytes), nil) } + + registry["tapchannelrpc.TaprootAssetChannels.DecodeAssetPayReq"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &AssetPayReq{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewTaprootAssetChannelsClient(conn) + resp, err := client.DecodeAssetPayReq(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } }