diff --git a/cmd/root.go b/cmd/root.go index 1ef0338..dcdc910 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -126,6 +126,9 @@ func startServer(opts *common.Options) error { promRegistry.MustRegister(common.Metrics.SendTransactionsCounter) promRegistry.MustRegister(common.Metrics.TotalSaplingParamsCounter) promRegistry.MustRegister(common.Metrics.TotalSproutParamsCounter) + promRegistry.MustRegister(common.Metrics.ZecPriceGauge) + promRegistry.MustRegister(common.Metrics.ZecPriceHistoryWebAPICounter) + promRegistry.MustRegister(common.Metrics.ZecPriceHistoryErrors) logger.SetLevel(logrus.Level(opts.LogLevel)) @@ -279,6 +282,9 @@ func startServer(opts *common.Options) error { walletrpc.RegisterDarksideStreamerServer(server, service) } + // Initialize price fetcher + common.StartPriceFetcher(dbPath, chainName) + // Start listening listener, err := net.Listen("tcp", opts.GRPCBindAddr) if err != nil { diff --git a/common/downloadhandler.go b/common/downloadhandler.go index f28bb0b..57127f9 100644 --- a/common/downloadhandler.go +++ b/common/downloadhandler.go @@ -16,7 +16,7 @@ func ParamsHandler(w http.ResponseWriter, req *http.Request) { "param": "sapling-output", }).Info("ParamsHandler") - http.Redirect(w, req, "https://z.cash/downloads/sapling-output.params", 301) + http.Redirect(w, req, "https://z.cash/downloads/sapling-output.params", http.StatusMovedPermanently) return } @@ -26,7 +26,7 @@ func ParamsHandler(w http.ResponseWriter, req *http.Request) { "param": "sapling-spend", }).Info("ParamsHandler") - http.Redirect(w, req, "https://z.cash/downloads/sapling-spend.params", 301) + http.Redirect(w, req, "https://z.cash/downloads/sapling-spend.params", http.StatusMovedPermanently) return } @@ -37,7 +37,7 @@ func ParamsHandler(w http.ResponseWriter, req *http.Request) { }).Info("ParamsHandler") Metrics.TotalSproutParamsCounter.Inc() - http.Redirect(w, req, "https://z.cash/downloads/sprout-groth16.params", 301) + http.Redirect(w, req, "https://z.cash/downloads/sprout-groth16.params", http.StatusMovedPermanently) return } diff --git a/common/prices.go b/common/prices.go new file mode 100644 index 0000000..9a0823a --- /dev/null +++ b/common/prices.go @@ -0,0 +1,387 @@ +package common + +import ( + "encoding/gob" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "net/http" + "os" + "path/filepath" + "reflect" + "runtime" + "sort" + "strconv" + "sync" + "time" + + "github.com/sirupsen/logrus" +) + +var ( + // Map of all historical prices. Date as "yyyy-mm-dd" to price in cents + historicalPrices map[string]float64 = make(map[string]float64) + + // The latest price + latestPrice float64 = -1 + + // Latest price was fetched at + latestPriceAt time.Time + + // Mutex to control both historical and latest price + pricesRwMutex sync.RWMutex + + // Full path of the persistence file + pricesFileName string +) + +func fetchAPIPrice(url string, resultPath []string) (float64, error) { + resp, err := http.Get(url) + if err != nil { + return -1, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return -1, err + } + + var priceJSON map[string]interface{} + json.Unmarshal(body, &priceJSON) + + for i := 0; i < len(resultPath); i++ { + d, ok := priceJSON[resultPath[i]] + if !ok { + return -1, fmt.Errorf("API error: couldn't find '%s'", resultPath[i]) + } + + switch v := d.(type) { + case float64: + return v, nil + case string: + { + price, err := strconv.ParseFloat(v, 64) + return price, err + } + + case map[string]interface{}: + priceJSON = v + } + } + + return -1, errors.New("path didn't result in lookup") +} + +func fetchCoinbasePrice() (float64, error) { + return fetchAPIPrice("https://api.coinbase.com/v2/exchange-rates?currency=ZEC", []string{"data", "rates", "USD"}) + +} + +func fetchCoinCapPrice() (float64, error) { + return fetchAPIPrice("https://api.coincap.io/v2/rates/zcash", []string{"data", "rateUsd"}) +} + +func fetchBinancePrice() (float64, error) { + return fetchAPIPrice("https://api.binance.com/api/v3/avgPrice?symbol=ZECUSDC", []string{"price"}) +} + +func fetchHistoricalCoingeckoPrice(ts *time.Time) (float64, error) { + dt := ts.Format("02-01-2006") // dd-mm-yyyy + url := fmt.Sprintf("https://api.coingecko.com/api/v3/coins/zcash/history?date=%s?id=zcash", dt) + + return fetchAPIPrice(url, []string{"market_data", "current_price", "usd"}) +} + +// Median gets the median number in a slice of numbers +func median(inp []float64) (median float64) { + + // Start by sorting a copy of the slice + sort.Float64s(inp) + + // No math is needed if there are no numbers + // For even numbers we add the two middle numbers + // and divide by two using the mean function above + // For odd numbers we just use the middle number + l := len(inp) + if l == 0 { + return -1 + } else if l == 2 { + return (inp[0] + inp[1]) / 2 + } else if l%2 == 0 { + return (inp[l/2-1] + inp[l/2+1]) / 2 + } else { + return inp[l/2] + } +} + +// fetchPriceFromWebAPI will fetch prices from multiple places, discard outliers and return the +// concensus price +func fetchPriceFromWebAPI() (float64, error) { + // We'll fetch prices from all our endpoints, and use the median price from that + priceProviders := []func() (float64, error){fetchBinancePrice, fetchCoinCapPrice, fetchCoinbasePrice} + + ch := make(chan float64) + + // Get all prices + for _, provider := range priceProviders { + go func(provider func() (float64, error)) { + price, err := provider() + if err != nil { + Log.WithFields(logrus.Fields{ + "method": "CurrentPrice", + "provider": runtime.FuncForPC(reflect.ValueOf(provider).Pointer()).Name(), + "error": err, + }).Error("Service") + + ch <- -1 + } else { + Log.WithFields(logrus.Fields{ + "method": "CurrentPrice", + "provider": runtime.FuncForPC(reflect.ValueOf(provider).Pointer()).Name(), + "price": price, + }).Info("Service") + + ch <- price + } + }(provider) + } + + prices := make([]float64, 0) + for range priceProviders { + price := <-ch + if price > 0 { + prices = append(prices, price) + } + } + + // sort + sort.Float64s(prices) + + // Get the median price + median1 := median(prices) + + // Discard all values that are more than 20% outside the median + validPrices := make([]float64, 0) + for _, price := range prices { + if (math.Abs(price-median1) / median1) > 0.2 { + Log.WithFields(logrus.Fields{ + "method": "CurrentPrice", + "error": fmt.Sprintf("Discarding price (%.2f) because too far away from median (%.2f", price, median1), + }).Error("Service") + } else { + validPrices = append(validPrices, price) + } + } + + // If we discarded too many, return an error + if len(validPrices) < (len(prices)/2 + 1) { + return -1, errors.New("not enough valid prices") + } else { + return median(validPrices), nil + } +} + +func readHistoricalPricesFile() (map[string]float64, error) { + f, err := os.Open(pricesFileName) + if err != nil { + Log.Errorf("Couldn't open file %s for writing: %v", pricesFileName, err) + return nil, err + } + defer f.Close() + + j := gob.NewDecoder(f) + var prices map[string]float64 + err = j.Decode(&prices) + if err != nil { + Log.Errorf("Couldn't decode historical prices: %v", err) + return nil, err + } + + Log.WithFields(logrus.Fields{ + "method": "ReadHistoricalPriceFile", + "action": "Read historical prices file", + "records": len(prices), + }).Info("Service") + return prices, nil +} + +func writeHistoricalPricesMap() { + // Serialize the map to disk. + f, err := os.OpenFile(pricesFileName, os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + Log.Errorf("Couldn't open file %s for writing: %v", pricesFileName, err) + return + } + defer f.Close() + + j := gob.NewEncoder(f) + + { + // Read lock + pricesRwMutex.RLock() + err = j.Encode(historicalPrices) + pricesRwMutex.RUnlock() + + if err != nil { + Log.Errorf("Couldn't encode historical prices: %v", err) + return + } + } + + Log.WithFields(logrus.Fields{ + "method": "WriteHistoricalPriceFile", + "action": "Wrote historical prices file", + }).Info("Service") +} + +func GetCurrentPrice() (float64, error) { + // Read lock + pricesRwMutex.RLock() + defer pricesRwMutex.RUnlock() + + // If the current price is too old, don't return it. + if time.Since(latestPriceAt).Hours() > 3 { + return -1, errors.New("price too old") + } + + return latestPrice, nil +} + +func writeLatestPrice(price float64) { + { + // Read lock + pricesRwMutex.RLock() + + // Check if the time has "rolled over", if yes then preserve the last price + // as the previous day's historical price + if latestPrice > 0 && latestPriceAt.Format("2006-01-02") != time.Now().Format("2006-01-02") { + // update the historical price. + // First, make a copy of the time + t := time.Unix(latestPriceAt.Unix(), 0) + + go addHistoricalPrice(latestPrice, &t) + } + pricesRwMutex.RUnlock() + } + + // Write lock + pricesRwMutex.Lock() + + latestPrice = price + latestPriceAt = time.Now() + + pricesRwMutex.Unlock() +} + +func GetHistoricalPrice(ts *time.Time) (float64, *time.Time, error) { + dt := ts.Format("2006-01-02") + canonicalTime, err := time.Parse("2006-01-02", dt) + if err != nil { + return -1, nil, err + } + + { + // Read lock + pricesRwMutex.RLock() + val, ok := historicalPrices[dt] + pricesRwMutex.RUnlock() + + if ok { + return val, &canonicalTime, nil + } + } + + { + // Check if this is the same as the current latest price + + // Read lock + pricesRwMutex.RLock() + var price = latestPrice + var returnPrice = price > 0 && latestPriceAt.Format("2006-01-02") == dt + pricesRwMutex.RUnlock() + + if returnPrice { + return price, &canonicalTime, nil + } + } + + // Fetch price from web API + price, err := fetchHistoricalCoingeckoPrice(ts) + if err != nil { + Log.Errorf("Couldn't read historical prices from Coingecko: %v", err) + return -1, nil, err + } + + go addHistoricalPrice(price, ts) + + return price, &canonicalTime, err +} + +func addHistoricalPrice(price float64, ts *time.Time) { + if price <= 0 { + return + } + dt := ts.Format("2006-01-02") + + // Read Lock + pricesRwMutex.RLock() + _, ok := historicalPrices[dt] + pricesRwMutex.RUnlock() + + if !ok { + // Write lock + pricesRwMutex.Lock() + historicalPrices[dt] = price + defer pricesRwMutex.Unlock() + + // Increment success counter + Metrics.ZecPriceHistoryWebAPICounter.Inc() + + go Log.WithFields(logrus.Fields{ + "method": "HistoricalPrice", + "action": "Add", + "date": dt, + "price": price, + }).Info("Service") + go writeHistoricalPricesMap() + } +} + +// StartPriceFetcher starts a new thread that will fetch historical and current prices +func StartPriceFetcher(dbPath string, chainName string) { + // Set the prices file name + pricesFileName = filepath.Join(dbPath, chainName, "prices") + + // Read the historical prices if available + if prices, err := readHistoricalPricesFile(); err != nil { + Log.Errorf("Couldn't read historical prices, starting with empty map") + } else { + // Write lock + pricesRwMutex.Lock() + historicalPrices = prices + pricesRwMutex.Unlock() + } + + // Fetch the current price every hour + go func() { + for { + price, err := fetchPriceFromWebAPI() + if err != nil { + Log.Errorf("Error getting prices from web APIs: %v", err) + } else { + Log.WithFields(logrus.Fields{ + "method": "SetCurrentPrice", + "price": price, + }).Info("Service") + + writeLatestPrice(price) + } + + // Refresh every + time.Sleep(15 * time.Minute) + } + }() +} diff --git a/common/prometheusmetrics.go b/common/prometheusmetrics.go index 5f1b647..56e8bc4 100644 --- a/common/prometheusmetrics.go +++ b/common/prometheusmetrics.go @@ -4,12 +4,15 @@ import "github.com/prometheus/client_golang/prometheus" // PrometheusMetrics is a list of collected Prometheus Counters and Guages that will be exported type PrometheusMetrics struct { - LatestBlockCounter prometheus.Counter - TotalBlocksServedConter prometheus.Counter - SendTransactionsCounter prometheus.Counter - TotalErrors prometheus.Counter - TotalSaplingParamsCounter prometheus.Counter - TotalSproutParamsCounter prometheus.Counter + LatestBlockCounter prometheus.Counter + TotalBlocksServedConter prometheus.Counter + SendTransactionsCounter prometheus.Counter + TotalErrors prometheus.Counter + TotalSaplingParamsCounter prometheus.Counter + TotalSproutParamsCounter prometheus.Counter + ZecPriceGauge prometheus.Gauge + ZecPriceHistoryWebAPICounter prometheus.Counter + ZecPriceHistoryErrors prometheus.Counter } func GetPrometheusMetrics() *PrometheusMetrics { @@ -49,5 +52,20 @@ func GetPrometheusMetrics() *PrometheusMetrics { Help: "Total number of params downloasd for sprout params", }) + m.ZecPriceGauge = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "zec_price", + Help: "Current price of Zec", + }) + + m.ZecPriceHistoryWebAPICounter = prometheus.NewCounter(prometheus.CounterOpts{ + Name: "zec_price_history_api_counter", + Help: "Counter of number of successful API calls to history API", + }) + + m.ZecPriceHistoryErrors = prometheus.NewCounter(prometheus.CounterOpts{ + Name: "zec_price_history_errors", + Help: "Counter for number of errors seen in the history price API", + }) + return m } diff --git a/frontend/service.go b/frontend/service.go index a40f54e..a699065 100644 --- a/frontend/service.go +++ b/frontend/service.go @@ -97,6 +97,46 @@ func (s *lwdStreamer) dailyActiveBlock(height uint64, peerip string) { } } +func (s *lwdStreamer) GetZECPrice(ctx context.Context, in *walletrpc.PriceRequest) (*walletrpc.PriceResponse, error) { + // Check for prices before zcash was born + if in == nil || in.Timestamp <= 1477551600 /* Zcash birthday: 2016-10-28*/ { + common.Metrics.ZecPriceHistoryErrors.Inc() + return nil, errors.New("incorrect Timestamp") + } + + if in.Currency != "USD" { + common.Metrics.ZecPriceHistoryErrors.Inc() + return nil, errors.New("unsupported currency") + } + + ts := time.Unix(int64(in.Timestamp), 0) + price, timeFetched, err := common.GetHistoricalPrice(&ts) + + if err != nil { + common.Metrics.ZecPriceHistoryErrors.Inc() + return nil, err + } + + return &walletrpc.PriceResponse{Timestamp: timeFetched.Unix(), Price: price, Currency: "USD"}, nil +} + +func (s *lwdStreamer) GetCurrentZECPrice(ctx context.Context, in *walletrpc.Empty) (*walletrpc.PriceResponse, error) { + price, err := common.GetCurrentPrice() + if err != nil { + common.Metrics.ZecPriceGauge.Set(0) + return nil, err + } + + if price <= 0 { + common.Metrics.ZecPriceGauge.Set(0) + return nil, errors.New("no price available") + } + + resp := &walletrpc.PriceResponse{Timestamp: time.Now().Unix(), Currency: "USD", Price: price} + common.Metrics.ZecPriceGauge.Set(price) + return resp, nil +} + // GetLatestBlock returns the height of the best chain, according to zcashd. func (s *lwdStreamer) GetLatestBlock(ctx context.Context, placeholder *walletrpc.ChainSpec) (*walletrpc.BlockID, error) { result, rpcErr := common.RawRequest("getblockchaininfo", []json.RawMessage{}) diff --git a/walletrpc/compact_formats.pb.go b/walletrpc/compact_formats.pb.go index 3ddb38d..cff1522 100644 --- a/walletrpc/compact_formats.pb.go +++ b/walletrpc/compact_formats.pb.go @@ -5,7 +5,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.15.7 +// protoc v3.6.1 // source: compact_formats.proto package walletrpc diff --git a/walletrpc/generate.go b/walletrpc/generate.go index e75f7fe..0d4fc77 100644 --- a/walletrpc/generate.go +++ b/walletrpc/generate.go @@ -3,5 +3,5 @@ // file COPYING or https://www.opensource.org/licenses/mit-license.php . package walletrpc -//go:generate protoc -I . ./compact_formats.proto --go_out=plugins=grpc:. -//go:generate protoc -I . ./service.proto --go_out=plugins=grpc:. +//go:generate protoc -I . --go_out=:../.. --go-grpc_out=:../.. ./compact_formats.proto +//go:generate protoc -I . --go_out=:../.. --go-grpc_out=:../.. ./service.proto diff --git a/walletrpc/service.pb.go b/walletrpc/service.pb.go index 4b13421..b6df6ec 100644 --- a/walletrpc/service.pb.go +++ b/walletrpc/service.pb.go @@ -5,7 +5,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.15.7 +// protoc v3.6.1 // source: service.proto package walletrpc @@ -1182,6 +1182,130 @@ func (x *GetAddressUtxosReplyList) GetAddressUtxos() []*GetAddressUtxosReply { return nil } +type PriceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // List of timestamps(in sec) at which the price is being requested + Timestamp uint64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // 3 letter currency-code + Currency string `protobuf:"bytes,2,opt,name=currency,proto3" json:"currency,omitempty"` +} + +func (x *PriceRequest) Reset() { + *x = PriceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_service_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PriceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PriceRequest) ProtoMessage() {} + +func (x *PriceRequest) ProtoReflect() protoreflect.Message { + mi := &file_service_proto_msgTypes[19] + 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 PriceRequest.ProtoReflect.Descriptor instead. +func (*PriceRequest) Descriptor() ([]byte, []int) { + return file_service_proto_rawDescGZIP(), []int{19} +} + +func (x *PriceRequest) GetTimestamp() uint64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *PriceRequest) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +type PriceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Timestamp at which this price quote was fetched. Note, this may not be the same + // as the request timestamp, but the server will respond with the closest timestamp that it has/can fetch + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // 3-letter currency code, matching the request + Currency string `protobuf:"bytes,2,opt,name=currency,proto3" json:"currency,omitempty"` + // price of ZEC + Price float64 `protobuf:"fixed64,3,opt,name=price,proto3" json:"price,omitempty"` +} + +func (x *PriceResponse) Reset() { + *x = PriceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_service_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PriceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PriceResponse) ProtoMessage() {} + +func (x *PriceResponse) ProtoReflect() protoreflect.Message { + mi := &file_service_proto_msgTypes[20] + 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 PriceResponse.ProtoReflect.Descriptor instead. +func (*PriceResponse) Descriptor() ([]byte, []int) { + return file_service_proto_rawDescGZIP(), []int{20} +} + +func (x *PriceResponse) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *PriceResponse) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *PriceResponse) GetPrice() float64 { + if x != nil { + return x.Price + } + return 0 +} + var File_service_proto protoreflect.FileDescriptor var file_service_proto_rawDesc = []byte{ @@ -1303,93 +1427,116 @@ var file_service_proto_rawDesc = []byte{ 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x52, 0x0c, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x32, 0xbb, 0x0a, 0x0a, - 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x65, 0x72, 0x12, 0x54, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0d, 0x47, - 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x21, 0x2e, 0x63, - 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, - 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x1a, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x48, 0x0a, 0x0c, + 0x50, 0x72, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x5f, 0x0a, 0x0d, 0x50, 0x72, 0x69, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x32, 0xf3, 0x0b, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, + 0x61, 0x63, 0x74, 0x54, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x72, 0x12, 0x54, 0x0a, + 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, + 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x70, 0x65, + 0x63, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, + 0x44, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, + 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x63, 0x61, 0x73, - 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, - 0x70, 0x63, 0x2e, 0x54, 0x78, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0x25, 0x2e, 0x63, 0x61, - 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, - 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, + 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x21, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x23, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, + 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, + 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x5a, 0x45, 0x43, 0x50, 0x72, 0x69, + 0x63, 0x65, 0x12, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x69, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, + 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, + 0x50, 0x72, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x5a, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5a, 0x45, 0x43, + 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x1a, 0x24, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x69, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, + 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, + 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x78, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, - 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x54, 0x78, 0x69, 0x64, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x61, 0x73, 0x68, + 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x63, 0x61, 0x73, + 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, + 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x78, 0x69, 0x64, 0x73, 0x12, 0x34, 0x2e, 0x63, + 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, + 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x1a, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, + 0x12, 0x47, 0x65, 0x74, 0x54, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x12, 0x22, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, + 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, + 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x18, 0x47, 0x65, 0x74, + 0x54, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x54, 0x0a, 0x0c, 0x47, 0x65, 0x74, + 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x78, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, - 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, - 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, - 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x12, 0x47, 0x65, - 0x74, 0x54, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x12, 0x22, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x54, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x54, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, - 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x78, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, - 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0c, - 0x47, 0x65, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, - 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, - 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x20, 0x2e, 0x63, - 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, - 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x00, - 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, - 0x78, 0x6f, 0x73, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2f, - 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, - 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x22, - 0x00, 0x12, 0x73, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, - 0x74, 0x78, 0x6f, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, + 0x63, 0x2e, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, + 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x22, 0x00, 0x30, 0x01, 0x12, + 0x52, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x1a, + 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, + 0x67, 0x1a, 0x2f, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, + 0x73, 0x74, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, + 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, + 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, + 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, + 0x4c, 0x69, 0x67, 0x68, 0x74, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, - 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x67, - 0x68, 0x74, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, - 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, - 0x67, 0x68, 0x74, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x04, 0x50, 0x69, - 0x6e, 0x67, 0x12, 0x1f, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x69, 0x6e, 0x67, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, - 0x67, 0x68, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, - 0x74, 0x72, 0x70, 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, + 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, + 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x00, 0x12, 0x4e, 0x0a, + 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x50, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1b, 0x5a, + 0x16, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -1404,7 +1551,7 @@ func file_service_proto_rawDescGZIP() []byte { return file_service_proto_rawDescData } -var file_service_proto_msgTypes = make([]protoimpl.MessageInfo, 19) +var file_service_proto_msgTypes = make([]protoimpl.MessageInfo, 21) var file_service_proto_goTypes = []interface{}{ (*BlockID)(nil), // 0: cash.z.wallet.sdk.rpc.BlockID (*BlockRange)(nil), // 1: cash.z.wallet.sdk.rpc.BlockRange @@ -1425,8 +1572,10 @@ var file_service_proto_goTypes = []interface{}{ (*GetAddressUtxosArg)(nil), // 16: cash.z.wallet.sdk.rpc.GetAddressUtxosArg (*GetAddressUtxosReply)(nil), // 17: cash.z.wallet.sdk.rpc.GetAddressUtxosReply (*GetAddressUtxosReplyList)(nil), // 18: cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList - (*CompactBlock)(nil), // 19: cash.z.wallet.sdk.rpc.CompactBlock - (*CompactTx)(nil), // 20: cash.z.wallet.sdk.rpc.CompactTx + (*PriceRequest)(nil), // 19: cash.z.wallet.sdk.rpc.PriceRequest + (*PriceResponse)(nil), // 20: cash.z.wallet.sdk.rpc.PriceResponse + (*CompactBlock)(nil), // 21: cash.z.wallet.sdk.rpc.CompactBlock + (*CompactTx)(nil), // 22: cash.z.wallet.sdk.rpc.CompactTx } var file_service_proto_depIdxs = []int32{ 0, // 0: cash.z.wallet.sdk.rpc.BlockRange.start:type_name -> cash.z.wallet.sdk.rpc.BlockID @@ -1437,33 +1586,37 @@ var file_service_proto_depIdxs = []int32{ 5, // 5: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLatestBlock:input_type -> cash.z.wallet.sdk.rpc.ChainSpec 0, // 6: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlock:input_type -> cash.z.wallet.sdk.rpc.BlockID 1, // 7: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlockRange:input_type -> cash.z.wallet.sdk.rpc.BlockRange - 2, // 8: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTransaction:input_type -> cash.z.wallet.sdk.rpc.TxFilter - 3, // 9: cash.z.wallet.sdk.rpc.CompactTxStreamer.SendTransaction:input_type -> cash.z.wallet.sdk.rpc.RawTransaction - 8, // 10: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressTxids:input_type -> cash.z.wallet.sdk.rpc.TransparentAddressBlockFilter - 12, // 11: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:input_type -> cash.z.wallet.sdk.rpc.AddressList - 11, // 12: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:input_type -> cash.z.wallet.sdk.rpc.Address - 14, // 13: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolTx:input_type -> cash.z.wallet.sdk.rpc.Exclude - 0, // 14: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:input_type -> cash.z.wallet.sdk.rpc.BlockID - 16, // 15: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg - 16, // 16: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg - 6, // 17: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:input_type -> cash.z.wallet.sdk.rpc.Empty - 9, // 18: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:input_type -> cash.z.wallet.sdk.rpc.Duration - 0, // 19: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLatestBlock:output_type -> cash.z.wallet.sdk.rpc.BlockID - 19, // 20: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlock:output_type -> cash.z.wallet.sdk.rpc.CompactBlock - 19, // 21: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlockRange:output_type -> cash.z.wallet.sdk.rpc.CompactBlock - 3, // 22: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTransaction:output_type -> cash.z.wallet.sdk.rpc.RawTransaction - 4, // 23: cash.z.wallet.sdk.rpc.CompactTxStreamer.SendTransaction:output_type -> cash.z.wallet.sdk.rpc.SendResponse - 3, // 24: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressTxids:output_type -> cash.z.wallet.sdk.rpc.RawTransaction - 13, // 25: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:output_type -> cash.z.wallet.sdk.rpc.Balance - 13, // 26: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:output_type -> cash.z.wallet.sdk.rpc.Balance - 20, // 27: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolTx:output_type -> cash.z.wallet.sdk.rpc.CompactTx - 15, // 28: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:output_type -> cash.z.wallet.sdk.rpc.TreeState - 18, // 29: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList - 17, // 30: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply - 7, // 31: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:output_type -> cash.z.wallet.sdk.rpc.LightdInfo - 10, // 32: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:output_type -> cash.z.wallet.sdk.rpc.PingResponse - 19, // [19:33] is the sub-list for method output_type - 5, // [5:19] is the sub-list for method input_type + 19, // 8: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetZECPrice:input_type -> cash.z.wallet.sdk.rpc.PriceRequest + 6, // 9: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetCurrentZECPrice:input_type -> cash.z.wallet.sdk.rpc.Empty + 2, // 10: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTransaction:input_type -> cash.z.wallet.sdk.rpc.TxFilter + 3, // 11: cash.z.wallet.sdk.rpc.CompactTxStreamer.SendTransaction:input_type -> cash.z.wallet.sdk.rpc.RawTransaction + 8, // 12: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressTxids:input_type -> cash.z.wallet.sdk.rpc.TransparentAddressBlockFilter + 12, // 13: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:input_type -> cash.z.wallet.sdk.rpc.AddressList + 11, // 14: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:input_type -> cash.z.wallet.sdk.rpc.Address + 14, // 15: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolTx:input_type -> cash.z.wallet.sdk.rpc.Exclude + 0, // 16: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:input_type -> cash.z.wallet.sdk.rpc.BlockID + 16, // 17: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg + 16, // 18: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg + 6, // 19: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:input_type -> cash.z.wallet.sdk.rpc.Empty + 9, // 20: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:input_type -> cash.z.wallet.sdk.rpc.Duration + 0, // 21: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLatestBlock:output_type -> cash.z.wallet.sdk.rpc.BlockID + 21, // 22: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlock:output_type -> cash.z.wallet.sdk.rpc.CompactBlock + 21, // 23: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlockRange:output_type -> cash.z.wallet.sdk.rpc.CompactBlock + 20, // 24: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetZECPrice:output_type -> cash.z.wallet.sdk.rpc.PriceResponse + 20, // 25: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetCurrentZECPrice:output_type -> cash.z.wallet.sdk.rpc.PriceResponse + 3, // 26: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTransaction:output_type -> cash.z.wallet.sdk.rpc.RawTransaction + 4, // 27: cash.z.wallet.sdk.rpc.CompactTxStreamer.SendTransaction:output_type -> cash.z.wallet.sdk.rpc.SendResponse + 3, // 28: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressTxids:output_type -> cash.z.wallet.sdk.rpc.RawTransaction + 13, // 29: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:output_type -> cash.z.wallet.sdk.rpc.Balance + 13, // 30: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:output_type -> cash.z.wallet.sdk.rpc.Balance + 22, // 31: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolTx:output_type -> cash.z.wallet.sdk.rpc.CompactTx + 15, // 32: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:output_type -> cash.z.wallet.sdk.rpc.TreeState + 18, // 33: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList + 17, // 34: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply + 7, // 35: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:output_type -> cash.z.wallet.sdk.rpc.LightdInfo + 10, // 36: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:output_type -> cash.z.wallet.sdk.rpc.PingResponse + 21, // [21:37] is the sub-list for method output_type + 5, // [5:21] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name @@ -1704,6 +1857,30 @@ func file_service_proto_init() { return nil } } + file_service_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PriceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_service_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PriceResponse); 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{ @@ -1711,7 +1888,7 @@ func file_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_service_proto_rawDesc, NumEnums: 0, - NumMessages: 19, + NumMessages: 21, NumExtensions: 0, NumServices: 1, }, diff --git a/walletrpc/service.proto b/walletrpc/service.proto index 8021e3f..efbe345 100644 --- a/walletrpc/service.proto +++ b/walletrpc/service.proto @@ -135,6 +135,26 @@ message GetAddressUtxosReplyList { repeated GetAddressUtxosReply addressUtxos = 1; } +message PriceRequest { + // List of timestamps(in sec) at which the price is being requested + uint64 timestamp = 1; + + // 3 letter currency-code + string currency = 2; +} + +message PriceResponse { + // Timestamp at which this price quote was fetched. Note, this may not be the same + // as the request timestamp, but the server will respond with the closest timestamp that it has/can fetch + int64 timestamp = 1; + + // 3-letter currency code, matching the request + string currency = 2; + + // price of ZEC + double price = 3; +} + service CompactTxStreamer { // Return the height of the tip of the best chain rpc GetLatestBlock(ChainSpec) returns (BlockID) {} @@ -143,6 +163,10 @@ service CompactTxStreamer { // Return a list of consecutive compact blocks rpc GetBlockRange(BlockRange) returns (stream CompactBlock) {} + // Get the historical and current prices + rpc GetZECPrice(PriceRequest) returns (PriceResponse) {} + rpc GetCurrentZECPrice(Empty) returns (PriceResponse) {} + // Return the requested full (not compact) transaction (as from zcashd) rpc GetTransaction(TxFilter) returns (RawTransaction) {} // Submit the given transaction to the Zcash network diff --git a/walletrpc/service_grpc.pb.go b/walletrpc/service_grpc.pb.go index da227dd..222f3cc 100644 --- a/walletrpc/service_grpc.pb.go +++ b/walletrpc/service_grpc.pb.go @@ -24,6 +24,9 @@ type CompactTxStreamerClient interface { GetBlock(ctx context.Context, in *BlockID, opts ...grpc.CallOption) (*CompactBlock, error) // Return a list of consecutive compact blocks GetBlockRange(ctx context.Context, in *BlockRange, opts ...grpc.CallOption) (CompactTxStreamer_GetBlockRangeClient, error) + // Get the historical and current prices + GetZECPrice(ctx context.Context, in *PriceRequest, opts ...grpc.CallOption) (*PriceResponse, error) + GetCurrentZECPrice(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*PriceResponse, error) // Return the requested full (not compact) transaction (as from zcashd) GetTransaction(ctx context.Context, in *TxFilter, opts ...grpc.CallOption) (*RawTransaction, error) // Submit the given transaction to the Zcash network @@ -113,6 +116,24 @@ func (x *compactTxStreamerGetBlockRangeClient) Recv() (*CompactBlock, error) { return m, nil } +func (c *compactTxStreamerClient) GetZECPrice(ctx context.Context, in *PriceRequest, opts ...grpc.CallOption) (*PriceResponse, error) { + out := new(PriceResponse) + err := c.cc.Invoke(ctx, "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetZECPrice", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *compactTxStreamerClient) GetCurrentZECPrice(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*PriceResponse, error) { + out := new(PriceResponse) + err := c.cc.Invoke(ctx, "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetCurrentZECPrice", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *compactTxStreamerClient) GetTransaction(ctx context.Context, in *TxFilter, opts ...grpc.CallOption) (*RawTransaction, error) { out := new(RawTransaction) err := c.cc.Invoke(ctx, "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTransaction", in, out, opts...) @@ -316,6 +337,9 @@ type CompactTxStreamerServer interface { GetBlock(context.Context, *BlockID) (*CompactBlock, error) // Return a list of consecutive compact blocks GetBlockRange(*BlockRange, CompactTxStreamer_GetBlockRangeServer) error + // Get the historical and current prices + GetZECPrice(context.Context, *PriceRequest) (*PriceResponse, error) + GetCurrentZECPrice(context.Context, *Empty) (*PriceResponse, error) // Return the requested full (not compact) transaction (as from zcashd) GetTransaction(context.Context, *TxFilter) (*RawTransaction, error) // Submit the given transaction to the Zcash network @@ -361,6 +385,12 @@ func (UnimplementedCompactTxStreamerServer) GetBlock(context.Context, *BlockID) func (UnimplementedCompactTxStreamerServer) GetBlockRange(*BlockRange, CompactTxStreamer_GetBlockRangeServer) error { return status.Errorf(codes.Unimplemented, "method GetBlockRange not implemented") } +func (UnimplementedCompactTxStreamerServer) GetZECPrice(context.Context, *PriceRequest) (*PriceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetZECPrice not implemented") +} +func (UnimplementedCompactTxStreamerServer) GetCurrentZECPrice(context.Context, *Empty) (*PriceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCurrentZECPrice not implemented") +} func (UnimplementedCompactTxStreamerServer) GetTransaction(context.Context, *TxFilter) (*RawTransaction, error) { return nil, status.Errorf(codes.Unimplemented, "method GetTransaction not implemented") } @@ -464,6 +494,42 @@ func (x *compactTxStreamerGetBlockRangeServer) Send(m *CompactBlock) error { return x.ServerStream.SendMsg(m) } +func _CompactTxStreamer_GetZECPrice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PriceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CompactTxStreamerServer).GetZECPrice(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetZECPrice", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CompactTxStreamerServer).GetZECPrice(ctx, req.(*PriceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CompactTxStreamer_GetCurrentZECPrice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CompactTxStreamerServer).GetCurrentZECPrice(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetCurrentZECPrice", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CompactTxStreamerServer).GetCurrentZECPrice(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + func _CompactTxStreamer_GetTransaction_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(TxFilter) if err := dec(in); err != nil { @@ -694,6 +760,14 @@ var CompactTxStreamer_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetBlock", Handler: _CompactTxStreamer_GetBlock_Handler, }, + { + MethodName: "GetZECPrice", + Handler: _CompactTxStreamer_GetZECPrice_Handler, + }, + { + MethodName: "GetCurrentZECPrice", + Handler: _CompactTxStreamer_GetCurrentZECPrice_Handler, + }, { MethodName: "GetTransaction", Handler: _CompactTxStreamer_GetTransaction_Handler,