From dc1130cf17d9aaba41ab6ae7c832d6b6c72d4954 Mon Sep 17 00:00:00 2001
From: Emma Haruka Iwao <yuryu@google.com>
Date: Sat, 23 Jul 2022 13:22:01 -0700
Subject: [PATCH] feat: chunk count checking

This patch supports check count checking when committing.
The client can optionally set chunk_count when uploading and if set,
the server will check if the uploaded chunk count matches
the value. It returns FailedPrecondition if it doesn't.
---
 api/open_saves.pb.go                        | 528 +++++++++++---------
 api/open_saves.proto                        |  15 +-
 docs/reference.md                           |   5 +-
 internal/app/collector/collector_test.go    |  18 +-
 internal/app/server/open_saves.go           |   4 +-
 internal/app/server/open_saves_test.go      |  89 ++++
 internal/pkg/metadb/blobref/blobref.go      |  25 +-
 internal/pkg/metadb/blobref/blobref_test.go |  62 ++-
 internal/pkg/metadb/metadb.go               |   4 +
 internal/pkg/metadb/metadb_test.go          |  21 +-
 10 files changed, 480 insertions(+), 291 deletions(-)

diff --git a/api/open_saves.pb.go b/api/open_saves.pb.go
index 6a0c42de..d73c7c9f 100644
--- a/api/open_saves.pb.go
+++ b/api/open_saves.pb.go
@@ -1625,6 +1625,10 @@ type BlobMetadata struct {
 	Crc32C uint32 `protobuf:"varint,6,opt,name=crc32c,proto3" json:"crc32c,omitempty"`
 	// has_crc32c indicates if crc32c is present.
 	HasCrc32C bool `protobuf:"varint,7,opt,name=has_crc32c,json=hasCrc32c,proto3" json:"has_crc32c,omitempty"`
+	// chunked is set true if the attached blob is chunked, otherwise false (read only).
+	Chunked bool `protobuf:"varint,8,opt,name=chunked,proto3" json:"chunked,omitempty"`
+	// Number of chunks (read only).
+	ChunkCount int64 `protobuf:"varint,9,opt,name=chunk_count,json=chunkCount,proto3" json:"chunk_count,omitempty"`
 }
 
 func (x *BlobMetadata) Reset() {
@@ -1708,6 +1712,20 @@ func (x *BlobMetadata) GetHasCrc32C() bool {
 	return false
 }
 
+func (x *BlobMetadata) GetChunked() bool {
+	if x != nil {
+		return x.Chunked
+	}
+	return false
+}
+
+func (x *BlobMetadata) GetChunkCount() int64 {
+	if x != nil {
+		return x.ChunkCount
+	}
+	return 0
+}
+
 type CreateChunkedBlobRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1719,6 +1737,10 @@ type CreateChunkedBlobRequest struct {
 	RecordKey string `protobuf:"bytes,2,opt,name=record_key,json=recordKey,proto3" json:"record_key,omitempty"`
 	// Size of each chunk
 	ChunkSize int64 `protobuf:"varint,3,opt,name=chunk_size,json=chunkSize,proto3" json:"chunk_size,omitempty"`
+	// Expected number of chunks.
+	// When set to non-zero, the server checks if it has received the exact number of
+	// chunks when CommitChunkedUpload is called.
+	ChunkCount int64 `protobuf:"varint,4,opt,name=chunk_count,json=chunkCount,proto3" json:"chunk_count,omitempty"`
 }
 
 func (x *CreateChunkedBlobRequest) Reset() {
@@ -1774,6 +1796,13 @@ func (x *CreateChunkedBlobRequest) GetChunkSize() int64 {
 	return 0
 }
 
+func (x *CreateChunkedBlobRequest) GetChunkCount() int64 {
+	if x != nil {
+		return x.ChunkCount
+	}
+	return 0
+}
+
 type CreateChunkedBlobResponse struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1914,7 +1943,7 @@ type ChunkMetadata struct {
 
 	// session_id is the ID of a chunk upload session. Not used for downloads.
 	SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"`
-	// number is the number
+	// number is the number of the chunk.
 	Number int64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"`
 	// size is a byte size of the chunk.
 	Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
@@ -3167,7 +3196,7 @@ var file_open_saves_proto_rawDesc = []byte{
 	0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
 	0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
 	0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x09, 0x0a, 0x07,
-	0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xcc, 0x01, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x62,
+	0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x87, 0x02, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x62,
 	0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72,
 	0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f,
 	0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f,
@@ -3180,272 +3209,277 @@ var file_open_saves_proto_rawDesc = []byte{
 	0x16, 0x0a, 0x06, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52,
 	0x06, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x5f, 0x63,
 	0x72, 0x63, 0x33, 0x32, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x68, 0x61, 0x73,
-	0x43, 0x72, 0x63, 0x33, 0x32, 0x63, 0x22, 0x75, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
-	0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12,
-	0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1d,
-	0x0a, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01,
-	0x28, 0x03, 0x52, 0x09, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x3a, 0x0a,
-	0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c,
-	0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65,
-	0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
-	0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x73, 0x0a, 0x12, 0x55, 0x70, 0x6c,
-	0x6f, 0x61, 0x64, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
-	0x36, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x68,
-	0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x08, 0x6d,
-	0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65,
-	0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74,
-	0x65, 0x6e, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xc8,
-	0x01, 0x0a, 0x0d, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
-	0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12,
-	0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
-	0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18,
-	0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68,
-	0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
-	0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74,
-	0x12, 0x10, 0x0a, 0x03, 0x6d, 0x64, 0x35, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d,
-	0x64, 0x35, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x18, 0x06, 0x20, 0x01,
-	0x28, 0x0d, 0x52, 0x06, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x61,
-	0x73, 0x5f, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09,
-	0x68, 0x61, 0x73, 0x43, 0x72, 0x63, 0x33, 0x32, 0x63, 0x22, 0x60, 0x0a, 0x1a, 0x43, 0x6f, 0x6d,
-	0x6d, 0x69, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69,
+	0x43, 0x72, 0x63, 0x33, 0x32, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x65,
+	0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64,
+	0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
+	0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e,
+	0x74, 0x22, 0x96, 0x01, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x68, 0x75, 0x6e,
+	0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b,
+	0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72,
+	0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68,
+	0x75, 0x6e, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09,
+	0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x75,
+	0x6e, 0x6b, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a,
+	0x63, 0x68, 0x75, 0x6e, 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x0a, 0x19, 0x43, 0x72,
+	0x65, 0x61, 0x74, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69,
 	0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73,
-	0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73,
-	0x2e, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x3a, 0x0a, 0x19, 0x41,
-	0x62, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61,
-	0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73,
-	0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65,
-	0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x71, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x42, 0x6c,
-	0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f,
-	0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74,
-	0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64,
-	0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f,
-	0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e,
-	0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x70, 0x0a, 0x0f, 0x47, 0x65,
-	0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a,
-	0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
-	0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x62,
+	0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x73, 0x0a, 0x12, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64,
+	0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x08,
+	0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18,
+	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b,
 	0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61,
 	0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18,
 	0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
-	0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x99, 0x01, 0x0a,
-	0x13, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65,
-	0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65,
-	0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79,
-	0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
-	0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x4e, 0x75, 0x6d,
-	0x62, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28,
+	0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xc8, 0x01, 0x0a, 0x0d,
+	0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a,
+	0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06,
+	0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6e, 0x75,
+	0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76,
+	0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x12, 0x10, 0x0a,
+	0x03, 0x6d, 0x64, 0x35, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6d, 0x64, 0x35, 0x12,
+	0x16, 0x0a, 0x06, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52,
+	0x06, 0x63, 0x72, 0x63, 0x33, 0x32, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x5f, 0x63,
+	0x72, 0x63, 0x33, 0x32, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x68, 0x61, 0x73,
+	0x43, 0x72, 0x63, 0x33, 0x32, 0x63, 0x22, 0x60, 0x0a, 0x1a, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+	0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
+	0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f,
+	0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
 	0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69,
-	0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x76, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x42,
-	0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x12, 0x36, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43,
-	0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x08,
-	0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74,
-	0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e,
-	0x74, 0x65, 0x6e, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x22, 0x74, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b,
-	0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b,
-	0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65,
-	0x79, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
-	0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, 0x74,
-	0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x21, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x22, 0x22, 0x0a, 0x0c, 0x50, 0x69, 0x6e,
-	0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6e,
-	0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x6e, 0x67, 0x22, 0xfa, 0x01,
-	0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65,
-	0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72,
-	0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b,
-	0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64,
-	0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f,
-	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70,
-	0x65, 0x72, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
-	0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61,
-	0x76, 0x65, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52, 0x05, 0x76, 0x61,
-	0x6c, 0x75, 0x65, 0x12, 0x30, 0x0a, 0x09, 0x6f, 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65,
-	0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76,
-	0x65, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52, 0x08, 0x6f, 0x6c, 0x64,
-	0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e,
-	0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x0a, 0x16, 0x43, 0x6f,
-	0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x29,
-	0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e,
-	0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72,
-	0x74, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xae, 0x01, 0x0a, 0x10, 0x41, 0x74,
-	0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b,
-	0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72,
-	0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72,
-	0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12,
-	0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05,
-	0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e,
-	0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x43, 0x0a, 0x11, 0x41, 0x74,
-	0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
-	0x18, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
-	0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
-	0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22,
-	0xda, 0x01, 0x0a, 0x10, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x63, 0x52, 0x65, 0x71,
+	0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x3a, 0x0a, 0x19, 0x41, 0x62, 0x6f, 0x72,
+	0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+	0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69,
+	0x6f, 0x6e, 0x49, 0x64, 0x22, 0x71, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f,
+	0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65,
+	0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65,
+	0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b,
+	0x65, 0x79, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e,
+	0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x70, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c,
+	0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x6d, 0x65,
+	0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f,
+	0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74,
+	0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
+	0x61, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x0a, 0x0a,
+	0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x13, 0x47, 0x65,
+	0x74, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d,
+	0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a,
+	0x0c, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72,
+	0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f,
+	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, 0x74, 0x52,
+	0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x76, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62,
+	0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a,
+	0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x68, 0x75, 0x6e,
+	0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74,
+	0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+	0x74, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x74, 0x0a,
+	0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12,
+	0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x23,
+	0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f,
+	0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68,
+	0x69, 0x6e, 0x74, 0x22, 0x21, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x22, 0x22, 0x0a, 0x0c, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x6e, 0x67, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x6e, 0x67, 0x22, 0xfa, 0x01, 0x0a, 0x15, 0x43,
+	0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71,
 	0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65,
 	0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65,
 	0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18,
 	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79,
 	0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d,
 	0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74,
-	0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x62,
-	0x6f, 0x75, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6c, 0x6f, 0x77, 0x65,
-	0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f,
-	0x62, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x75, 0x70, 0x70,
-	0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18,
-	0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65,
-	0x73, 0x2e, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x2a, 0x5b, 0x0a, 0x0e,
-	0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x09,
-	0x0a, 0x05, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x47, 0x52, 0x45,
-	0x41, 0x54, 0x45, 0x52, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x45, 0x53, 0x53, 0x10, 0x02,
-	0x12, 0x14, 0x0a, 0x10, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x4f, 0x52, 0x5f, 0x45,
-	0x51, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x4f,
-	0x52, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x04, 0x32, 0xfc, 0x0e, 0x0a, 0x09, 0x4f, 0x70,
-	0x65, 0x6e, 0x53, 0x61, 0x76, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74,
-	0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76,
-	0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65,
-	0x73, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x08, 0x47, 0x65, 0x74,
-	0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65,
-	0x73, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x1a, 0x10, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x74,
-	0x6f, 0x72, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x6f,
-	0x72, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e,
-	0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x1a, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x4c, 0x69,
-	0x73, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x22, 0x00, 0x12, 0x46, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72,
-	0x65, 0x12, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x44, 0x65,
-	0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
-	0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0c, 0x43, 0x72,
-	0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x6f, 0x70, 0x65,
-	0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63,
-	0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x70, 0x65,
-	0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x00, 0x12,
-	0x3d, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x2e, 0x6f,
-	0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f,
-	0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
-	0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x00, 0x12, 0x51,
-	0x0a, 0x0c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x1e,
-	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79,
-	0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f,
-	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79,
-	0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
-	0x00, 0x12, 0x43, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72,
-	0x64, 0x12, 0x1e, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x55, 0x70,
-	0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x52, 0x65,
-	0x63, 0x6f, 0x72, 0x64, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
-	0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76,
-	0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00,
-	0x12, 0x47, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x1c,
-	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
-	0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f,
-	0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74,
-	0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x00, 0x28, 0x01, 0x12, 0x60, 0x0a, 0x11, 0x43, 0x72, 0x65,
-	0x61, 0x74, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x23,
-	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
-	0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e,
-	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f,
-	0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0b, 0x55,
-	0x70, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x1d, 0x2e, 0x6f, 0x70, 0x65,
-	0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x68, 0x75,
-	0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
-	0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64,
-	0x61, 0x74, 0x61, 0x22, 0x00, 0x28, 0x01, 0x12, 0x57, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69,
-	0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x25,
-	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69,
-	0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65,
-	0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x00,
-	0x12, 0x54, 0x0a, 0x12, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64,
-	0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76,
-	0x65, 0x73, 0x2e, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55,
-	0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67,
-	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
-	0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f,
-	0x62, 0x12, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65,
-	0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6f,
-	0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62,
-	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x0c,
-	0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x1e, 0x2e, 0x6f,
-	0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62,
-	0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6f,
-	0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62,
-	0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30,
-	0x01, 0x12, 0x44, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12,
-	0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65,
-	0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e,
+	0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73,
+	0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+	0x12, 0x30, 0x0a, 0x09, 0x6f, 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e,
+	0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52, 0x08, 0x6f, 0x6c, 0x64, 0x56, 0x61, 0x6c,
+	0x75, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e,
+	0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x5d, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x70, 0x61,
+	0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x08, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x76,
+	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65,
+	0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52,
+	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xae, 0x01, 0x0a, 0x10, 0x41, 0x74, 0x6f, 0x6d, 0x69,
+	0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73,
+	0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
+	0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f,
+	0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65,
+	0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65,
+	0x72, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
+	0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05,
+	0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48, 0x69, 0x6e,
+	0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x22, 0x43, 0x0a, 0x11, 0x41, 0x74, 0x6f, 0x6d, 0x69,
+	0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07,
+	0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x75,
+	0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xda, 0x01, 0x0a,
+	0x10, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d,
+	0x0a, 0x0a, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a,
+	0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x4e, 0x61,
+	0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x75, 0x6e,
+	0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x42, 0x6f,
+	0x75, 0x6e, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x70, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x75,
+	0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x75, 0x70, 0x70, 0x65, 0x72, 0x42,
+	0x6f, 0x75, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x48,
+	0x69, 0x6e, 0x74, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x2a, 0x5b, 0x0a, 0x0e, 0x46, 0x69, 0x6c,
+	0x74, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x09, 0x0a, 0x05, 0x45,
+	0x51, 0x55, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45,
+	0x52, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x14, 0x0a,
+	0x10, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x4f, 0x52, 0x5f, 0x45, 0x51, 0x55, 0x41,
+	0x4c, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x4f, 0x52, 0x5f, 0x45,
+	0x51, 0x55, 0x41, 0x4c, 0x10, 0x04, 0x32, 0xfc, 0x0e, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x6e, 0x53,
+	0x61, 0x76, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74,
+	0x6f, 0x72, 0x65, 0x12, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e,
+	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x53,
+	0x74, 0x6f, 0x72, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f,
+	0x72, 0x65, 0x12, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47,
+	0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10,
+	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65,
+	0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73,
+	0x12, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73,
+	0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d,
+	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53,
+	0x74, 0x6f, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
+	0x46, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x1d,
+	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
+	0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e,
 	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
-	0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12,
-	0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x50, 0x69, 0x6e, 0x67,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61,
-	0x76, 0x65, 0x73, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x22, 0x00, 0x12, 0x57, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64,
-	0x53, 0x77, 0x61, 0x70, 0x12, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73,
-	0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76,
-	0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61,
-	0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x18, 0x43,
-	0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x47, 0x72, 0x65,
-	0x61, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61,
-	0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73,
-	0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41,
-	0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x4c, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e,
-	0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63,
-	0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65,
-	0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74,
-	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x41, 0x74,
-	0x6f, 0x6d, 0x69, 0x63, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65,
+	0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74,
+	0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61,
+	0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61,
+	0x76, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x09,
+	0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
+	0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76,
+	0x65, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0c, 0x51,
+	0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x1e, 0x2e, 0x6f, 0x70,
+	0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x63,
+	0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6f, 0x70,
+	0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x63,
+	0x6f, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x43,
+	0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1e,
+	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74,
+	0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11,
+	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72,
+	0x64, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63,
+	0x6f, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e,
+	0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a,
+	0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x1c, 0x2e, 0x6f, 0x70,
+	0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x6c,
+	0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
+	0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
+	0x74, 0x61, 0x22, 0x00, 0x28, 0x01, 0x12, 0x60, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
+	0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x6f, 0x70,
+	0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x68,
+	0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65,
+	0x61, 0x74, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x0b, 0x55, 0x70, 0x6c, 0x6f,
+	0x61, 0x64, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61,
+	0x76, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76,
+	0x65, 0x73, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+	0x22, 0x00, 0x28, 0x01, 0x12, 0x57, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x43, 0x68,
+	0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x25, 0x2e, 0x6f, 0x70,
+	0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x43, 0x68,
+	0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x42,
+	0x6c, 0x6f, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x00, 0x12, 0x54, 0x0a,
+	0x12, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c,
+	0x6f, 0x61, 0x64, 0x12, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e,
+	0x41, 0x62, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x55, 0x70, 0x6c, 0x6f,
+	0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
+	0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x19,
+	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c,
+	0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
+	0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x0c, 0x47, 0x65, 0x74,
+	0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x1e, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
+	0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75,
+	0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
+	0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x68, 0x75,
+	0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x44,
+	0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x1c, 0x2e, 0x6f,
+	0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42,
+	0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
+	0x74, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x16, 0x2e, 0x6f,
+	0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73,
+	0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
+	0x57, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61,
+	0x70, 0x12, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x6f,
+	0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e,
+	0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x18, 0x43, 0x6f, 0x6d, 0x70,
+	0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53, 0x77, 0x61, 0x70, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65,
+	0x72, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73,
+	0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74,
+	0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
+	0x00, 0x12, 0x54, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x41, 0x6e, 0x64, 0x53,
+	0x77, 0x61, 0x70, 0x4c, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65,
 	0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74,
 	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61,
 	0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73,
 	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x41, 0x74, 0x6f, 0x6d, 0x69,
-	0x63, 0x53, 0x75, 0x62, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61,
+	0x63, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61,
 	0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71,
 	0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73,
 	0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x09, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e,
-	0x63, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74,
-	0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c,
+	0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x53, 0x75,
+	0x62, 0x49, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73,
+	0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74,
+	0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
+	0x00, 0x12, 0x48, 0x0a, 0x09, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x63, 0x12, 0x1b,
 	0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69,
-	0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48,
-	0x0a, 0x09, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x44, 0x65, 0x63, 0x12, 0x1b, 0x2e, 0x6f, 0x70,
+	0x63, 0x49, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70,
 	0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e,
-	0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73,
-	0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68,
-	0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x66, 0x6f, 0x72,
-	0x67, 0x61, 0x6d, 0x65, 0x73, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x2d, 0x73, 0x61, 0x76, 0x65, 0x73,
-	0x3b, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x33,
+	0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x09, 0x41,
+	0x74, 0x6f, 0x6d, 0x69, 0x63, 0x44, 0x65, 0x63, 0x12, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73,
+	0x61, 0x76, 0x65, 0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x63, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x76, 0x65,
+	0x73, 0x2e, 0x41, 0x74, 0x6f, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
+	0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x66, 0x6f, 0x72, 0x67, 0x61, 0x6d,
+	0x65, 0x73, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x2d, 0x73, 0x61, 0x76, 0x65, 0x73, 0x3b, 0x6f, 0x70,
+	0x65, 0x6e, 0x73, 0x61, 0x76, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/api/open_saves.proto b/api/open_saves.proto
index 647016c0..3034e9c1 100644
--- a/api/open_saves.proto
+++ b/api/open_saves.proto
@@ -317,7 +317,6 @@ message DeleteStoreRequest {
   string key = 1;
 }
 
-
 message CreateRecordRequest {
   // The key of the store to create the record into.
   string store_key = 1;
@@ -343,7 +342,6 @@ message GetRecordRequest {
 // QueryRecordsRequest contains conditions to search particular records.
 // Multiple conditions are AND'ed together.
 message QueryRecordsRequest {
-
   // store_key is the primary key of the store.
   // Optional and the method queries all stores when omitted.
   string store_key = 1;
@@ -498,6 +496,12 @@ message BlobMetadata {
 
   // has_crc32c indicates if crc32c is present.
   bool has_crc32c = 7;
+
+  // chunked is set true if the attached blob is chunked, otherwise false (read only).
+  bool chunked = 8;
+
+  // Number of chunks (read only).
+  int64 chunk_count = 9;
 }
 
 message CreateChunkedBlobRequest {
@@ -509,6 +513,11 @@ message CreateChunkedBlobRequest {
 
   // Size of each chunk
   int64 chunk_size = 3;
+  
+  // Expected number of chunks.
+  // When set to non-zero, the server checks if it has received the exact number of
+  // chunks when CommitChunkedUpload is called.
+  int64 chunk_count = 4;
 }
 
 message CreateChunkedBlobResponse {
@@ -533,7 +542,7 @@ message ChunkMetadata {
   // session_id is the ID of a chunk upload session. Not used for downloads.
   string session_id = 1;
 
-  // number is the number
+  // number is the number of the chunk.
   int64 number = 2;
 
   // size is a byte size of the chunk.
diff --git a/docs/reference.md b/docs/reference.md
index ee5e01f0..db3e0999 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -149,6 +149,8 @@ when creating (uploading) a new blob object.
 | md5 | [bytes](#bytes) |  | md5 is the MD5 hash of the blob content. If supplied for uploads, the server validates the content using the hash value. For downloads, the server returns the stored hash value of the blob content. The length of the hash value is 0 (not present) or 16 (present) bytes. |
 | crc32c | [uint32](#uint32) |  | crc32c is the CRC32C checksum of the blob content. Specifically, it uses the Castagnoli polynomial. https://pkg.go.dev/hash/crc32#pkg-constants If supplied for uploads, the server validates the content using the checksum. For downloads, the server returns the checksum of the blob content. Open Saves provides both MD5 and CRC32C because CRC32C is often used by Cloud object storage services. |
 | has_crc32c | [bool](#bool) |  | has_crc32c indicates if crc32c is present. |
+| chunked | [bool](#bool) |  | chunked is set true if the attached blob is chunked, otherwise false (read only). |
+| chunk_count | [int64](#int64) |  | Number of chunks (read only). |
 
 
 
@@ -164,7 +166,7 @@ when creating (uploading) a new blob object.
 | Field | Type | Label | Description |
 | ----- | ---- | ----- | ----------- |
 | session_id | [string](#string) |  | session_id is the ID of a chunk upload session. Not used for downloads. |
-| number | [int64](#int64) |  | number is the number |
+| number | [int64](#int64) |  | number is the number of the chunk. |
 | size | [int64](#int64) |  | size is a byte size of the chunk. |
 | hint | [Hint](#opensaves-Hint) |  | Performance hints (write only). |
 | md5 | [bytes](#bytes) |  | md5 is the MD5 hash of the chunk content. If supplied for uploads, the server validates the content using the hash value. For downloads, the server returns the stored hash value of the chunk content. The length of the hash value is 0 (not present) or 16 (present) bytes. |
@@ -258,6 +260,7 @@ should contain content.
 | store_key | [string](#string) |  | store_key is the key of the store that the record belongs to. |
 | record_key | [string](#string) |  | record_key is the key of the record the new blob object belongs to. |
 | chunk_size | [int64](#int64) |  | Size of each chunk |
+| chunk_count | [int64](#int64) |  | Expected number of chunks. When set to non-zero, the server checks if it has received the exact number of chunks when CommitChunkedUpload is called. |
 
 
 
diff --git a/internal/app/collector/collector_test.go b/internal/app/collector/collector_test.go
index b1d7da85..95305939 100644
--- a/internal/app/collector/collector_test.go
+++ b/internal/app/collector/collector_test.go
@@ -147,13 +147,13 @@ func TestCollector_DeletesBlobs(t *testing.T) {
 	collector := newTestCollector(ctx, t)
 	store := setupTestStore(ctx, t, collector)
 	record := setupTestRecord(ctx, t, collector, store.Key)
-	const numBlobRefs = 5
-	blobRefs := make([]*blobref.BlobRef, 0, numBlobRefs)
+	const blobRefCount = 5
+	blobRefs := make([]*blobref.BlobRef, 0, blobRefCount)
 
 	// 0 and 2 are old, to be deleted
 	// 1 and 3 have the applicable statuses but new
 	// 4 is still initializing
-	for i := 0; i < numBlobRefs; i++ {
+	for i := 0; i < blobRefCount; i++ {
 		blobRef := blobref.NewBlobRef(0, store.Key, record.Key)
 		blobRef.Timestamps.CreatedAt = collector.cfg.Before
 		blobRef.Timestamps.UpdatedAt = collector.cfg.Before
@@ -197,10 +197,10 @@ func TestCollector_DeletesUnlinkedBlobRefs(t *testing.T) {
 	collector := newTestCollector(ctx, t)
 	store := setupTestStore(ctx, t, collector)
 	record := setupTestRecord(ctx, t, collector, store.Key)
-	const numBlobRefs = 3
-	blobRefs := make([]*blobref.BlobRef, 0, numBlobRefs)
+	const blobRefCount = 3
+	blobRefs := make([]*blobref.BlobRef, 0, blobRefCount)
 	ds := newDatastoreClient(ctx, t)
-	for i := 0; i < numBlobRefs; i++ {
+	for i := 0; i < blobRefCount; i++ {
 		blobRef := blobref.NewBlobRef(0, store.Key, record.Key)
 		blobRef.Fail() // Fail() updates Timestamps so needs to come here.
 		blobRef.Timestamps.CreatedAt = collector.cfg.Before.Add(-1 * time.Second)
@@ -226,15 +226,15 @@ func TestCollector_DeletesUnlinkedBlobRefs(t *testing.T) {
 }
 
 func TestCollector_DeletesChunkedBlobs(t *testing.T) {
+	const chunkRefCount = 5
+
 	ctx := context.Background()
 	collector := newTestCollector(ctx, t)
 	store := setupTestStore(ctx, t, collector)
 	record := setupTestRecord(ctx, t, collector, store.Key)
-	blob := blobref.NewChunkedBlobRef(store.Key, record.Key)
+	blob := blobref.NewChunkedBlobRef(store.Key, record.Key, chunkRefCount)
 	ds := newDatastoreClient(ctx, t)
 	setupTestBlobRef(ctx, t, ds, blob)
-
-	const chunkRefCount = 5
 	chunks := make([]*chunkref.ChunkRef, 0, chunkRefCount)
 
 	// 0 and 2 are old, to be deleted
diff --git a/internal/app/server/open_saves.go b/internal/app/server/open_saves.go
index ff0de59d..5fbc5472 100644
--- a/internal/app/server/open_saves.go
+++ b/internal/app/server/open_saves.go
@@ -490,7 +490,7 @@ func (s *openSavesServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.Pi
 }
 
 func (s *openSavesServer) CreateChunkedBlob(ctx context.Context, req *pb.CreateChunkedBlobRequest) (*pb.CreateChunkedBlobResponse, error) {
-	b := blobref.NewChunkedBlobRef(req.GetStoreKey(), req.GetRecordKey())
+	b := blobref.NewChunkedBlobRef(req.GetStoreKey(), req.GetRecordKey(), req.GetChunkCount())
 	b, err := s.metaDB.InsertBlobRef(ctx, b)
 	if err != nil {
 		log.Errorf("CreateChunkedBlob failed for store (%v), record (%v): %v", req.GetStoreKey(), req.GetRecordKey(), err)
@@ -609,7 +609,7 @@ func (s *openSavesServer) CommitChunkedUpload(ctx context.Context, req *pb.Commi
 		log.Errorf("Cannot retrieve chunked blob metadata for session (%v): %v", blobKey, err)
 		return nil, err
 	}
-	record, blob, err := s.metaDB.PromoteBlobRefToCurrent(ctx, blob)
+	record, _, err := s.metaDB.PromoteBlobRefToCurrent(ctx, blob)
 	if err != nil {
 		log.Errorf("PromoteBlobRefToCurrent failed for object %v: %v", blob.ObjectPath(), err)
 		// Do not delete the blob object here. Leave it to the garbage collector.
diff --git a/internal/app/server/open_saves_test.go b/internal/app/server/open_saves_test.go
index 271f3c85..cdc13421 100644
--- a/internal/app/server/open_saves_test.go
+++ b/internal/app/server/open_saves_test.go
@@ -1557,6 +1557,95 @@ func TestOpenSaves_UploadChunkedBlob(t *testing.T) {
 		SessionId: sessionId,
 	}); assert.NoError(t, err) {
 		assert.Equal(t, int64(len(testChunk)*chunkCount), meta.Size)
+		assert.True(t, meta.Chunked)
+		assert.Equal(t, int64(chunkCount), meta.ChunkCount)
+		assert.False(t, meta.HasCrc32C)
+		assert.Empty(t, meta.Md5)
+		assert.Equal(t, store.Key, meta.StoreKey)
+		assert.Equal(t, record.Key, meta.RecordKey)
+	}
+
+	// Check if the metadata is reflected to the record as well.
+	if updatedRecord, err := client.GetRecord(ctx, &pb.GetRecordRequest{
+		StoreKey: store.Key, Key: record.Key,
+	}); assert.NoError(t, err) {
+		if assert.NotNil(t, updatedRecord) {
+			assert.Equal(t, int64(len(testChunk)*chunkCount), updatedRecord.BlobSize)
+			assert.Equal(t, int64(chunkCount), updatedRecord.ChunkCount)
+			assert.True(t, updatedRecord.Chunked)
+			assert.True(t, record.GetCreatedAt().AsTime().Equal(updatedRecord.GetCreatedAt().AsTime()))
+			assert.True(t, beforeCreateChunk.Before(updatedRecord.GetUpdatedAt().AsTime()))
+			assert.NotEqual(t, record.Signature, updatedRecord.Signature)
+		}
+	}
+
+	for i := 0; i < chunkCount; i++ {
+		verifyChunk(ctx, t, client, store.Key, record.Key, sessionId, int64(i), testChunk)
+	}
+
+	// Deletion test
+	if _, err := client.DeleteBlob(ctx, &pb.DeleteBlobRequest{
+		StoreKey:  store.Key,
+		RecordKey: record.Key,
+	}); err != nil {
+		t.Errorf("DeleteBlob failed: %v", err)
+	}
+	verifyBlob(ctx, t, client, store.Key, record.Key, make([]byte, 0))
+}
+
+func TestOpenSaves_UploadChunkedBlobWithChunkCount(t *testing.T) {
+	ctx := context.Background()
+	_, listener := getOpenSavesServer(ctx, t, "gcp")
+	_, client := getTestClient(ctx, t, listener)
+	store := &pb.Store{Key: uuid.NewString()}
+	setupTestStore(ctx, t, client, store)
+	record := &pb.Record{Key: uuid.NewString()}
+	record = setupTestRecord(ctx, t, client, store.Key, record)
+
+	const chunkSize = 1025
+	const chunkCount = 4
+	testChunk := make([]byte, chunkSize)
+	for i := 0; i < chunkSize; i++ {
+		testChunk[i] = byte(i % 256)
+	}
+
+	beforeCreateChunk := time.Now()
+	var sessionId string
+
+	if res, err := client.CreateChunkedBlob(ctx, &pb.CreateChunkedBlobRequest{
+		StoreKey:   store.Key,
+		RecordKey:  record.Key,
+		ChunkSize:  chunkSize,
+		ChunkCount: chunkCount,
+	}); assert.NoError(t, err) {
+		if assert.NotNil(t, res) {
+			_, err := uuid.Parse(res.SessionId)
+			assert.NoError(t, err)
+			sessionId = res.SessionId
+		}
+	}
+	t.Cleanup(func() {
+		client.DeleteBlob(ctx, &pb.DeleteBlobRequest{StoreKey: store.Key, RecordKey: record.Key})
+	})
+
+	for i := 0; i < chunkCount-1; i++ {
+		uploadChunk(ctx, t, client, sessionId, int64(i), testChunk)
+		// UploadChunk shouldn't update Signature.
+		if actual, err := client.GetRecord(ctx, &pb.GetRecordRequest{StoreKey: store.Key, Key: record.Key}); assert.NoError(t, err) {
+			assert.Equal(t, record.Signature, actual.Signature)
+		}
+		b, err := client.CommitChunkedUpload(ctx, &pb.CommitChunkedUploadRequest{SessionId: sessionId})
+		assert.Nil(t, b)
+		assert.Equal(t, codes.FailedPrecondition, status.Code(err))
+	}
+	uploadChunk(ctx, t, client, sessionId, chunkCount-1, testChunk)
+
+	if meta, err := client.CommitChunkedUpload(ctx, &pb.CommitChunkedUploadRequest{
+		SessionId: sessionId,
+	}); assert.NoError(t, err) {
+		assert.Equal(t, int64(len(testChunk)*chunkCount), meta.Size)
+		assert.True(t, meta.Chunked)
+		assert.Equal(t, int64(chunkCount), meta.ChunkCount)
 		assert.False(t, meta.HasCrc32C)
 		assert.Empty(t, meta.Md5)
 		assert.Equal(t, store.Key, meta.StoreKey)
diff --git a/internal/pkg/metadb/blobref/blobref.go b/internal/pkg/metadb/blobref/blobref.go
index 1f38f89b..67d11424 100644
--- a/internal/pkg/metadb/blobref/blobref.go
+++ b/internal/pkg/metadb/blobref/blobref.go
@@ -38,6 +38,10 @@ type BlobRef struct {
 	RecordKey string
 	// Chunked is whether the BlobRef is chunked or not.
 	Chunked bool
+	// ChunkCount is the number of chunks that should be associated to the BlobRef.
+	// It is set by either the client when starting a chunk upload or
+	// the server when committing a chunked upload.
+	ChunkCount int64
 
 	// Checksums have checksums for each blob object associated with the BlobRef entity.
 	// Record.{MD5,CRC32C} must be used for inline blobs, and
@@ -95,12 +99,13 @@ func NewBlobRef(size int64, storeKey, recordKey string) *BlobRef {
 	}
 }
 
-// NewChunkedBlobRef creates a new BlobRef object with Size and Chunked
-// set 0 and true, respectively.
+// NewChunkedBlobRef creates a new BlobRef object with Size, Chunked, and
+// ChunkCount set to 0, true, and chunkCount respectively.
 // Other behaviors are the same as NewBlobRef
-func NewChunkedBlobRef(storeKey, recordKey string) *BlobRef {
+func NewChunkedBlobRef(storeKey, recordKey string, chunkCount int64) *BlobRef {
 	b := NewBlobRef(0, storeKey, recordKey)
 	b.Chunked = true
+	b.ChunkCount = chunkCount
 	return b
 }
 
@@ -112,11 +117,13 @@ func (b *BlobRef) ObjectPath() string {
 // ToProto returns a BlobMetadata representation of the object.
 func (b *BlobRef) ToProto() *pb.BlobMetadata {
 	return &pb.BlobMetadata{
-		StoreKey:  b.StoreKey,
-		RecordKey: b.RecordKey,
-		Size:      b.Size,
-		Md5:       b.MD5,
-		Crc32C:    b.GetCRC32C(),
-		HasCrc32C: b.HasCRC32C,
+		StoreKey:   b.StoreKey,
+		RecordKey:  b.RecordKey,
+		Size:       b.Size,
+		Md5:        b.MD5,
+		Chunked:    b.Chunked,
+		ChunkCount: b.ChunkCount,
+		Crc32C:     b.GetCRC32C(),
+		HasCrc32C:  b.HasCRC32C,
 	}
 }
diff --git a/internal/pkg/metadb/blobref/blobref_test.go b/internal/pkg/metadb/blobref/blobref_test.go
index 4d161b2c..ea876e85 100644
--- a/internal/pkg/metadb/blobref/blobref_test.go
+++ b/internal/pkg/metadb/blobref/blobref_test.go
@@ -24,6 +24,22 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+func TestBlobRef_New(t *testing.T) {
+	const (
+		store      = "store"
+		record     = "record"
+		chunkCount = int64(42)
+	)
+
+	b := NewChunkedBlobRef(store, record, chunkCount)
+	if assert.NotNil(t, b) {
+		assert.Equal(t, store, b.StoreKey)
+		assert.Equal(t, record, b.RecordKey)
+		assert.NotEqual(t, uuid.Nil, b.Key)
+		assert.Equal(t, chunkCount, b.ChunkCount)
+	}
+}
+
 func TestBlobRef_LoadKey(t *testing.T) {
 	key := uuid.MustParse("d13c289c-8845-485f-b582-c87342d5dade")
 	blob := new(BlobRef)
@@ -37,6 +53,7 @@ func TestBlobRef_Save(t *testing.T) {
 		objectName = "object name"
 		store      = "store"
 		record     = "record"
+		chunkCount = int64(42)
 	)
 
 	blob := BlobRef{
@@ -44,6 +61,7 @@ func TestBlobRef_Save(t *testing.T) {
 		Status:     StatusInitializing,
 		StoreKey:   store,
 		RecordKey:  record,
+		ChunkCount: chunkCount,
 		Checksums:  checksumstest.RandomChecksums(t),
 		Timestamps: timestamps.New(),
 	}
@@ -69,14 +87,18 @@ func TestBlobRef_Save(t *testing.T) {
 			Name:  "Chunked",
 			Value: false,
 		},
+		{
+			Name:  "ChunkCount",
+			Value: chunkCount,
+		},
 	}
 	actual, err := blob.Save()
 	assert.NoError(t, err)
 	if assert.NotNil(t, actual) {
 		assert.Equal(t, expected, actual[:len(expected)])
-		if assert.Equal(t, 9, len(actual)) {
-			checksumstest.AssertPropertyListMatch(t, blob.Checksums, actual[5:8])
-			assert.Equal(t, "Timestamps", actual[8].Name)
+		if assert.Equal(t, len(expected)+3+1, len(actual)) {
+			checksumstest.AssertPropertyListMatch(t, blob.Checksums, actual[len(expected):len(expected)+3])
+			assert.Equal(t, "Timestamps", actual[len(expected)+3].Name)
 		}
 	}
 }
@@ -87,6 +109,7 @@ func TestBlobRef_Load(t *testing.T) {
 		objectName = "object name"
 		store      = "store"
 		record     = "record"
+		chunkCount = int64(551)
 	)
 	properties := []datastore.Property{
 		{
@@ -105,13 +128,23 @@ func TestBlobRef_Load(t *testing.T) {
 			Name:  "RecordKey",
 			Value: record,
 		},
+		{
+			Name:  "Chunked",
+			Value: true,
+		},
+		{
+			Name:  "ChunkCount",
+			Value: chunkCount,
+		},
 	}
 	expected := &BlobRef{
-		Size:      123,
-		Status:    StatusReady,
-		StoreKey:  store,
-		RecordKey: record,
-		Checksums: checksumstest.RandomChecksums(t),
+		Size:       size,
+		Status:     StatusReady,
+		StoreKey:   store,
+		RecordKey:  record,
+		Chunked:    true,
+		ChunkCount: chunkCount,
+		Checksums:  checksumstest.RandomChecksums(t),
 	}
 	properties = append(properties, checksumstest.ChecksumsToProperties(t, expected.Checksums)...)
 	actual := new(BlobRef)
@@ -129,18 +162,23 @@ func TestBlobRef_GetObjectPath(t *testing.T) {
 
 func TestBlobRef_ToProto(t *testing.T) {
 	const (
-		size   = int64(123)
-		store  = "store"
-		record = "record"
+		size       = int64(123)
+		store      = "store"
+		record     = "record"
+		chunkCount = int64(42)
 	)
-	b := NewBlobRef(size, store, record)
+	b := NewChunkedBlobRef(store, record, chunkCount)
 	b.Checksums = checksumstest.RandomChecksums(t)
+	b.Size = size
+	b.ChunkCount = chunkCount
 
 	proto := b.ToProto()
 	if assert.NotNil(t, proto) {
 		assert.Equal(t, b.StoreKey, proto.GetStoreKey())
 		assert.Equal(t, b.RecordKey, proto.GetRecordKey())
 		assert.Equal(t, b.Size, proto.GetSize())
+		assert.True(t, b.Chunked)
+		assert.Equal(t, b.ChunkCount, chunkCount)
 		checksumstest.AssertProtoEqual(t, b.Checksums, proto)
 	}
 }
diff --git a/internal/pkg/metadb/metadb.go b/internal/pkg/metadb/metadb.go
index 3ed652d8..c3fa4004 100644
--- a/internal/pkg/metadb/metadb.go
+++ b/internal/pkg/metadb/metadb.go
@@ -510,6 +510,10 @@ func (m *MetaDB) PromoteBlobRefToCurrent(ctx context.Context, blob *blobref.Blob
 			if err != nil {
 				return err
 			}
+			if blob.ChunkCount != 0 && blob.ChunkCount != count {
+				return status.Errorf(codes.FailedPrecondition, "expected chunk count doesn't match: expected (%v), actual (%v)", blob.ChunkCount, count)
+			}
+			blob.ChunkCount = count
 			record.ChunkCount = count
 			blob.Size = size
 		} else {
diff --git a/internal/pkg/metadb/metadb_test.go b/internal/pkg/metadb/metadb_test.go
index 7bd59758..6dfed063 100644
--- a/internal/pkg/metadb/metadb_test.go
+++ b/internal/pkg/metadb/metadb_test.go
@@ -513,7 +513,7 @@ func TestMetaDB_SimpleCreateGetDeleteBlobRef(t *testing.T) {
 			assert.Nil(t, promoRecord.Blob)
 			assert.Equal(t, blobKey, promoRecord.ExternalBlob)
 			assert.Equal(t, blob.Size, promoRecord.BlobSize)
-			assert.Zero(t, promoRecord.Chunked)
+			assert.False(t, promoRecord.Chunked)
 			assert.Zero(t, promoRecord.ChunkCount)
 			assert.True(t, beforePromo.Before(promoRecord.Timestamps.UpdatedAt))
 			assert.NotEqual(t, record.Timestamps.Signature, promoRecord.Timestamps.Signature)
@@ -521,6 +521,8 @@ func TestMetaDB_SimpleCreateGetDeleteBlobRef(t *testing.T) {
 		if assert.NotNil(t, promoBlob) {
 			assert.Equal(t, blobKey, promoBlob.Key)
 			assert.Equal(t, blobref.StatusReady, promoBlob.Status)
+			assert.False(t, promoBlob.Chunked)
+			assert.Zero(t, promoBlob.ChunkCount)
 			assert.True(t, beforePromo.Before(promoBlob.Timestamps.UpdatedAt))
 			assert.NotEqual(t, origSig, promoBlob.Timestamps.Signature)
 		}
@@ -547,7 +549,7 @@ func TestMetaDB_SimpleCreateGetDeleteBlobRef(t *testing.T) {
 			assert.Equal(t, uuid.Nil, delPendRecord.ExternalBlob)
 			assert.Empty(t, delPendRecord.Blob)
 			assert.Zero(t, delPendRecord.BlobSize)
-			assert.Zero(t, delPendRecord.Chunked)
+			assert.False(t, delPendRecord.Chunked)
 			assert.Zero(t, delPendRecord.ChunkCount)
 		}
 		if assert.NotNil(t, delPendBlob) {
@@ -932,7 +934,7 @@ func TestMetaDB_DeleteRecordWithNonExistentBlobRef(t *testing.T) {
 func TestMetaDB_SimpleCreateGetDeleteChunkedBlob(t *testing.T) {
 	const (
 		testChunkCount = 3
-		testChunKSize  = int32(42)
+		testChunkSize  = int32(42)
 	)
 
 	ctx := context.Background()
@@ -951,7 +953,7 @@ func TestMetaDB_SimpleCreateGetDeleteChunkedBlob(t *testing.T) {
 
 	// Mark the chunks ready
 	for _, chunk := range chunks {
-		chunk.Size = testChunKSize
+		chunk.Size = testChunkSize
 		now := time.Now()
 		if err := metaDB.MarkChunkRefReady(ctx, chunk); err != nil {
 			t.Fatalf("MarkChunkRefReady failed for chunk (%v): %v", chunk.Key, err)
@@ -970,21 +972,24 @@ func TestMetaDB_SimpleCreateGetDeleteChunkedBlob(t *testing.T) {
 		t.Fatalf("PromoteBlobRefToCurrent failed: %v", err)
 	}
 	if assert.NotNil(t, record) {
-		assert.EqualValues(t, testChunKSize*testChunkCount, record.BlobSize)
+		assert.EqualValues(t, testChunkSize*testChunkCount, record.BlobSize)
 		assert.EqualValues(t, testChunkCount, record.ChunkCount)
 		assert.True(t, record.Chunked)
 	}
 	if assert.NotNil(t, blobRetrieved) {
-		assert.EqualValues(t, testChunKSize*testChunkCount, blobRetrieved.Size)
+		assert.EqualValues(t, testChunkSize*testChunkCount, blobRetrieved.Size)
+		assert.True(t, blobRetrieved.Chunked)
+		assert.Equal(t, int64(testChunkCount), blobRetrieved.ChunkCount)
 	}
 
 	// Verify blob and chunk metadata
 	if blobRetrieved, err := metaDB.GetCurrentBlobRef(ctx, store.Key, record.Key); err != nil {
 		t.Errorf("GetCurrentBlobRef failed: %v", err)
 	} else {
-		assert.EqualValues(t, testChunKSize*testChunkCount, blobRetrieved.Size)
+		assert.EqualValues(t, testChunkSize*testChunkCount, blobRetrieved.Size)
 		assert.Equal(t, blob.Key, blobRetrieved.Key)
 		assert.True(t, blobRetrieved.Chunked)
+		assert.Equal(t, int64(testChunkCount), blobRetrieved.ChunkCount)
 	}
 	for i := 0; i < testChunkCount; i++ {
 		got, err := metaDB.FindChunkRefByNumber(ctx, store.Key, record.Key, int32(i))
@@ -1041,7 +1046,7 @@ func TestMetaDB_MarkUncommittedBlobForDeletion(t *testing.T) {
 	assert.NoError(t, metaDB.MarkUncommittedBlobForDeletion(ctx, blob.Key))
 
 	// Should fail if blob is live
-	blob = blobref.NewChunkedBlobRef(store.Key, record.Key)
+	blob = blobref.NewChunkedBlobRef(store.Key, record.Key, 0)
 	setupTestBlobRef(ctx, t, metaDB, blob)
 	_, _, err := metaDB.PromoteBlobRefToCurrent(ctx, blob)
 	if assert.NoError(t, err) {