From 182ba59a084bc505b2cc5d311e9f859910f03b88 Mon Sep 17 00:00:00 2001 From: Ian Shim <100327837+ian-shim@users.noreply.github.com> Date: Thu, 18 Jul 2024 20:35:13 -0700 Subject: [PATCH 1/4] Minibatch store interface (#635) --- disperser/batcher/inmem/minibatch_store.go | 103 ++++++++++++++++++ .../batcher/inmem/minibatch_store_test.go | 98 +++++++++++++++++ disperser/batcher/minibatch_store.go | 54 +++++++++ 3 files changed, 255 insertions(+) create mode 100644 disperser/batcher/inmem/minibatch_store.go create mode 100644 disperser/batcher/inmem/minibatch_store_test.go create mode 100644 disperser/batcher/minibatch_store.go diff --git a/disperser/batcher/inmem/minibatch_store.go b/disperser/batcher/inmem/minibatch_store.go new file mode 100644 index 0000000000..c11e7b2406 --- /dev/null +++ b/disperser/batcher/inmem/minibatch_store.go @@ -0,0 +1,103 @@ +package inmem + +import ( + "fmt" + + "github.com/Layr-Labs/eigenda/disperser/batcher" + "github.com/Layr-Labs/eigensdk-go/logging" + "github.com/google/uuid" +) + +type minibatchStore struct { + // BatchRecords maps batch IDs to batch records + BatchRecords map[uuid.UUID]*batcher.BatchRecord + // MinibatchRecords maps batch IDs to a map from minibatch indices to minibatch records + MinibatchRecords map[uuid.UUID]map[uint]*batcher.MinibatchRecord + // DispersalRequests maps batch IDs to a map from minibatch indices to dispersal requests + DispersalRequests map[uuid.UUID]map[uint]*batcher.DispersalRequest + // DispersalResponses maps batch IDs to a map from minibatch indices to dispersal responses + DispersalResponses map[uuid.UUID]map[uint]*batcher.DispersalResponse + + logger logging.Logger +} + +var _ batcher.MinibatchStore = (*minibatchStore)(nil) + +func NewMinibatchStore(logger logging.Logger) batcher.MinibatchStore { + return &minibatchStore{ + BatchRecords: make(map[uuid.UUID]*batcher.BatchRecord), + MinibatchRecords: make(map[uuid.UUID]map[uint]*batcher.MinibatchRecord), + DispersalRequests: make(map[uuid.UUID]map[uint]*batcher.DispersalRequest), + DispersalResponses: make(map[uuid.UUID]map[uint]*batcher.DispersalResponse), + + logger: logger, + } +} + +func (m *minibatchStore) PutBatch(batch *batcher.BatchRecord) error { + m.BatchRecords[batch.ID] = batch + + return nil +} + +func (m *minibatchStore) GetBatch(batchID uuid.UUID) (*batcher.BatchRecord, error) { + b, ok := m.BatchRecords[batchID] + if !ok { + return nil, fmt.Errorf("batch not found") + } + return b, nil +} + +func (m *minibatchStore) PutMiniBatch(minibatch *batcher.MinibatchRecord) error { + if _, ok := m.MinibatchRecords[minibatch.BatchID]; !ok { + m.MinibatchRecords[minibatch.BatchID] = make(map[uint]*batcher.MinibatchRecord) + } + m.MinibatchRecords[minibatch.BatchID][minibatch.MinibatchIndex] = minibatch + + return nil +} + +func (m *minibatchStore) GetMiniBatch(batchID uuid.UUID, minibatchIndex uint) (*batcher.MinibatchRecord, error) { + if _, ok := m.MinibatchRecords[batchID]; !ok { + return nil, nil + } + return m.MinibatchRecords[batchID][minibatchIndex], nil +} + +func (m *minibatchStore) PutDispersalRequest(request *batcher.DispersalRequest) error { + if _, ok := m.DispersalRequests[request.BatchID]; !ok { + m.DispersalRequests[request.BatchID] = make(map[uint]*batcher.DispersalRequest) + } + m.DispersalRequests[request.BatchID][request.MinibatchIndex] = request + + return nil +} + +func (m *minibatchStore) GetDispersalRequest(batchID uuid.UUID, minibatchIndex uint) (*batcher.DispersalRequest, error) { + if _, ok := m.DispersalRequests[batchID]; !ok { + return nil, nil + } + + return m.DispersalRequests[batchID][minibatchIndex], nil +} + +func (m *minibatchStore) PutDispersalResponse(response *batcher.DispersalResponse) error { + if _, ok := m.DispersalResponses[response.BatchID]; !ok { + m.DispersalResponses[response.BatchID] = make(map[uint]*batcher.DispersalResponse) + } + m.DispersalResponses[response.BatchID][response.MinibatchIndex] = response + + return nil +} + +func (m *minibatchStore) GetDispersalResponse(batchID uuid.UUID, minibatchIndex uint) (*batcher.DispersalResponse, error) { + if _, ok := m.DispersalResponses[batchID]; !ok { + return nil, nil + } + + return m.DispersalResponses[batchID][minibatchIndex], nil +} + +func (m *minibatchStore) GetPendingBatch() (*batcher.BatchRecord, error) { + return nil, nil +} diff --git a/disperser/batcher/inmem/minibatch_store_test.go b/disperser/batcher/inmem/minibatch_store_test.go new file mode 100644 index 0000000000..dbe0a01054 --- /dev/null +++ b/disperser/batcher/inmem/minibatch_store_test.go @@ -0,0 +1,98 @@ +package inmem_test + +import ( + "testing" + "time" + + "github.com/Layr-Labs/eigenda/core" + "github.com/Layr-Labs/eigenda/disperser/batcher" + "github.com/Layr-Labs/eigenda/disperser/batcher/inmem" + gcommon "github.com/ethereum/go-ethereum/common" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" +) + +func newMinibatchStore() batcher.MinibatchStore { + return inmem.NewMinibatchStore(nil) +} + +func TestPutBatch(t *testing.T) { + s := newMinibatchStore() + id, err := uuid.NewV7() + assert.NoError(t, err) + + batch := &batcher.BatchRecord{ + ID: id, + CreatedAt: time.Now().UTC(), + ReferenceBlockNumber: 1, + HeaderHash: [32]byte{1}, + AggregatePubKey: nil, + AggregateSignature: nil, + } + err = s.PutBatch(batch) + assert.NoError(t, err) + b, err := s.GetBatch(batch.ID) + assert.NoError(t, err) + assert.Equal(t, batch, b) +} + +func TestPutMiniBatch(t *testing.T) { + s := newMinibatchStore() + id, err := uuid.NewV7() + assert.NoError(t, err) + minibatch := &batcher.MinibatchRecord{ + BatchID: id, + MinibatchIndex: 12, + BlobHeaderHashes: [][32]byte{{1}}, + BatchSize: 1, + ReferenceBlockNumber: 1, + } + err = s.PutMiniBatch(minibatch) + assert.NoError(t, err) + m, err := s.GetMiniBatch(minibatch.BatchID, minibatch.MinibatchIndex) + assert.NoError(t, err) + assert.Equal(t, minibatch, m) +} + +func TestPutDispersalRequest(t *testing.T) { + s := newMinibatchStore() + id, err := uuid.NewV7() + assert.NoError(t, err) + request := &batcher.DispersalRequest{ + BatchID: id, + MinibatchIndex: 0, + OperatorID: core.OperatorID([32]byte{1}), + OperatorAddress: gcommon.HexToAddress("0x0"), + NumBlobs: 1, + RequestedAt: time.Now().UTC(), + } + err = s.PutDispersalRequest(request) + assert.NoError(t, err) + r, err := s.GetDispersalRequest(request.BatchID, request.MinibatchIndex) + assert.NoError(t, err) + assert.Equal(t, request, r) +} + +func TestPutDispersalResponse(t *testing.T) { + s := newMinibatchStore() + id, err := uuid.NewV7() + assert.NoError(t, err) + response := &batcher.DispersalResponse{ + DispersalRequest: batcher.DispersalRequest{ + BatchID: id, + MinibatchIndex: 0, + OperatorID: core.OperatorID([32]byte{1}), + OperatorAddress: gcommon.HexToAddress("0x0"), + NumBlobs: 1, + RequestedAt: time.Now().UTC(), + }, + Signatures: nil, + RespondedAt: time.Now().UTC(), + Error: nil, + } + err = s.PutDispersalResponse(response) + assert.NoError(t, err) + r, err := s.GetDispersalResponse(response.BatchID, response.MinibatchIndex) + assert.NoError(t, err) + assert.Equal(t, response, r) +} diff --git a/disperser/batcher/minibatch_store.go b/disperser/batcher/minibatch_store.go new file mode 100644 index 0000000000..a3b45e20f7 --- /dev/null +++ b/disperser/batcher/minibatch_store.go @@ -0,0 +1,54 @@ +package batcher + +import ( + "time" + + "github.com/Layr-Labs/eigenda/core" + gcommon "github.com/ethereum/go-ethereum/common" + "github.com/google/uuid" +) + +type BatchRecord struct { + ID uuid.UUID + CreatedAt time.Time + ReferenceBlockNumber uint + HeaderHash [32]byte + AggregatePubKey *core.G2Point + AggregateSignature *core.Signature +} + +type MinibatchRecord struct { + BatchID uuid.UUID + MinibatchIndex uint + BlobHeaderHashes [][32]byte + BatchSize uint64 // in bytes + ReferenceBlockNumber uint +} + +type DispersalRequest struct { + BatchID uuid.UUID + MinibatchIndex uint + core.OperatorID + OperatorAddress gcommon.Address + NumBlobs uint + RequestedAt time.Time +} + +type DispersalResponse struct { + DispersalRequest + Signatures []*core.Signature + RespondedAt time.Time + Error error +} + +type MinibatchStore interface { + PutBatch(batch *BatchRecord) error + GetBatch(batchID uuid.UUID) (*BatchRecord, error) + PutMiniBatch(minibatch *MinibatchRecord) error + GetMiniBatch(batchID uuid.UUID, minibatchIndex uint) (*MinibatchRecord, error) + PutDispersalRequest(request *DispersalRequest) error + GetDispersalRequest(batchID uuid.UUID, minibatchIndex uint) (*DispersalRequest, error) + PutDispersalResponse(response *DispersalResponse) error + GetDispersalResponse(batchID uuid.UUID, minibatchIndex uint) (*DispersalResponse, error) + GetPendingBatch() (*BatchRecord, error) +} From 59dd16adad6bda0abea3c013fdb9bd5aae927878 Mon Sep 17 00:00:00 2001 From: Ian Shim <100327837+ian-shim@users.noreply.github.com> Date: Thu, 18 Jul 2024 20:42:47 -0700 Subject: [PATCH 2/4] Update node interface to enable minibatching (#646) --- api/grpc/node/node.pb.go | 761 ++++++++++++++++++++++++---------- api/grpc/node/node_grpc.pb.go | 84 ++++ api/proto/node/node.proto | 35 ++ node/grpc/server.go | 8 + node/grpc/server_test.go | 30 ++ 5 files changed, 696 insertions(+), 222 deletions(-) diff --git a/api/grpc/node/node.pb.go b/api/grpc/node/node.pb.go index 5a61d3e797..ab7060c05c 100644 --- a/api/grpc/node/node.pb.go +++ b/api/grpc/node/node.pb.go @@ -10,6 +10,7 @@ import ( common "github.com/Layr-Labs/eigenda/api/grpc/common" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" reflect "reflect" sync "sync" ) @@ -21,7 +22,7 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// This describes the chunks returned in RetrieveChunksReply are encoded. +// This describes how the chunks returned in RetrieveChunksReply are encoded. // Used to facilitate the decoding of chunks. type ChunkEncoding int32 @@ -177,6 +178,217 @@ func (x *StoreChunksReply) GetSignature() []byte { return nil } +type StoreBlobsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Blobs to store + Blobs []*Blob `protobuf:"bytes,1,rep,name=blobs,proto3" json:"blobs,omitempty"` + // The reference block number whose state is used to encode the blobs + ReferenceBlockNumber uint32 `protobuf:"varint,2,opt,name=reference_block_number,json=referenceBlockNumber,proto3" json:"reference_block_number,omitempty"` +} + +func (x *StoreBlobsRequest) Reset() { + *x = StoreBlobsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_node_node_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StoreBlobsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StoreBlobsRequest) ProtoMessage() {} + +func (x *StoreBlobsRequest) ProtoReflect() protoreflect.Message { + mi := &file_node_node_proto_msgTypes[2] + 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 StoreBlobsRequest.ProtoReflect.Descriptor instead. +func (*StoreBlobsRequest) Descriptor() ([]byte, []int) { + return file_node_node_proto_rawDescGZIP(), []int{2} +} + +func (x *StoreBlobsRequest) GetBlobs() []*Blob { + if x != nil { + return x.Blobs + } + return nil +} + +func (x *StoreBlobsRequest) GetReferenceBlockNumber() uint32 { + if x != nil { + return x.ReferenceBlockNumber + } + return 0 +} + +type StoreBlobsReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The operator's BLS sgnature signed on the blob header hashes. + // The ordering of the signatures must match the ordering of the blobs sent + // in the request, with empty signatures in the places for discarded blobs. + Signatures []*wrapperspb.BytesValue `protobuf:"bytes,1,rep,name=signatures,proto3" json:"signatures,omitempty"` +} + +func (x *StoreBlobsReply) Reset() { + *x = StoreBlobsReply{} + if protoimpl.UnsafeEnabled { + mi := &file_node_node_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StoreBlobsReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StoreBlobsReply) ProtoMessage() {} + +func (x *StoreBlobsReply) ProtoReflect() protoreflect.Message { + mi := &file_node_node_proto_msgTypes[3] + 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 StoreBlobsReply.ProtoReflect.Descriptor instead. +func (*StoreBlobsReply) Descriptor() ([]byte, []int) { + return file_node_node_proto_rawDescGZIP(), []int{3} +} + +func (x *StoreBlobsReply) GetSignatures() []*wrapperspb.BytesValue { + if x != nil { + return x.Signatures + } + return nil +} + +type AttestBatchRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // header of the batch + BatchHeader *BatchHeader `protobuf:"bytes,1,opt,name=batch_header,json=batchHeader,proto3" json:"batch_header,omitempty"` + // the header hashes of all blobs in the batch + BlobHeaderHashes [][]byte `protobuf:"bytes,2,rep,name=blob_header_hashes,json=blobHeaderHashes,proto3" json:"blob_header_hashes,omitempty"` +} + +func (x *AttestBatchRequest) Reset() { + *x = AttestBatchRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_node_node_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AttestBatchRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AttestBatchRequest) ProtoMessage() {} + +func (x *AttestBatchRequest) ProtoReflect() protoreflect.Message { + mi := &file_node_node_proto_msgTypes[4] + 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 AttestBatchRequest.ProtoReflect.Descriptor instead. +func (*AttestBatchRequest) Descriptor() ([]byte, []int) { + return file_node_node_proto_rawDescGZIP(), []int{4} +} + +func (x *AttestBatchRequest) GetBatchHeader() *BatchHeader { + if x != nil { + return x.BatchHeader + } + return nil +} + +func (x *AttestBatchRequest) GetBlobHeaderHashes() [][]byte { + if x != nil { + return x.BlobHeaderHashes + } + return nil +} + +type AttestBatchReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (x *AttestBatchReply) Reset() { + *x = AttestBatchReply{} + if protoimpl.UnsafeEnabled { + mi := &file_node_node_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AttestBatchReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AttestBatchReply) ProtoMessage() {} + +func (x *AttestBatchReply) ProtoReflect() protoreflect.Message { + mi := &file_node_node_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AttestBatchReply.ProtoReflect.Descriptor instead. +func (*AttestBatchReply) Descriptor() ([]byte, []int) { + return file_node_node_proto_rawDescGZIP(), []int{5} +} + +func (x *AttestBatchReply) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + type RetrieveChunksRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -198,7 +410,7 @@ type RetrieveChunksRequest struct { func (x *RetrieveChunksRequest) Reset() { *x = RetrieveChunksRequest{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[2] + mi := &file_node_node_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -211,7 +423,7 @@ func (x *RetrieveChunksRequest) String() string { func (*RetrieveChunksRequest) ProtoMessage() {} func (x *RetrieveChunksRequest) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[2] + mi := &file_node_node_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -224,7 +436,7 @@ func (x *RetrieveChunksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RetrieveChunksRequest.ProtoReflect.Descriptor instead. func (*RetrieveChunksRequest) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{2} + return file_node_node_proto_rawDescGZIP(), []int{6} } func (x *RetrieveChunksRequest) GetBatchHeaderHash() []byte { @@ -262,7 +474,7 @@ type RetrieveChunksReply struct { func (x *RetrieveChunksReply) Reset() { *x = RetrieveChunksReply{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[3] + mi := &file_node_node_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -275,7 +487,7 @@ func (x *RetrieveChunksReply) String() string { func (*RetrieveChunksReply) ProtoMessage() {} func (x *RetrieveChunksReply) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[3] + mi := &file_node_node_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -288,7 +500,7 @@ func (x *RetrieveChunksReply) ProtoReflect() protoreflect.Message { // Deprecated: Use RetrieveChunksReply.ProtoReflect.Descriptor instead. func (*RetrieveChunksReply) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{3} + return file_node_node_proto_rawDescGZIP(), []int{7} } func (x *RetrieveChunksReply) GetChunks() [][]byte { @@ -319,7 +531,7 @@ type GetBlobHeaderRequest struct { func (x *GetBlobHeaderRequest) Reset() { *x = GetBlobHeaderRequest{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[4] + mi := &file_node_node_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -332,7 +544,7 @@ func (x *GetBlobHeaderRequest) String() string { func (*GetBlobHeaderRequest) ProtoMessage() {} func (x *GetBlobHeaderRequest) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[4] + mi := &file_node_node_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -345,7 +557,7 @@ func (x *GetBlobHeaderRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetBlobHeaderRequest.ProtoReflect.Descriptor instead. func (*GetBlobHeaderRequest) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{4} + return file_node_node_proto_rawDescGZIP(), []int{8} } func (x *GetBlobHeaderRequest) GetBatchHeaderHash() []byte { @@ -385,7 +597,7 @@ type GetBlobHeaderReply struct { func (x *GetBlobHeaderReply) Reset() { *x = GetBlobHeaderReply{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[5] + mi := &file_node_node_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -398,7 +610,7 @@ func (x *GetBlobHeaderReply) String() string { func (*GetBlobHeaderReply) ProtoMessage() {} func (x *GetBlobHeaderReply) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[5] + mi := &file_node_node_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -411,7 +623,7 @@ func (x *GetBlobHeaderReply) ProtoReflect() protoreflect.Message { // Deprecated: Use GetBlobHeaderReply.ProtoReflect.Descriptor instead. func (*GetBlobHeaderReply) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{5} + return file_node_node_proto_rawDescGZIP(), []int{9} } func (x *GetBlobHeaderReply) GetBlobHeader() *BlobHeader { @@ -442,7 +654,7 @@ type MerkleProof struct { func (x *MerkleProof) Reset() { *x = MerkleProof{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[6] + mi := &file_node_node_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -455,7 +667,7 @@ func (x *MerkleProof) String() string { func (*MerkleProof) ProtoMessage() {} func (x *MerkleProof) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[6] + mi := &file_node_node_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -468,7 +680,7 @@ func (x *MerkleProof) ProtoReflect() protoreflect.Message { // Deprecated: Use MerkleProof.ProtoReflect.Descriptor instead. func (*MerkleProof) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{6} + return file_node_node_proto_rawDescGZIP(), []int{10} } func (x *MerkleProof) GetHashes() [][]byte { @@ -509,7 +721,7 @@ type Blob struct { func (x *Blob) Reset() { *x = Blob{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[7] + mi := &file_node_node_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -522,7 +734,7 @@ func (x *Blob) String() string { func (*Blob) ProtoMessage() {} func (x *Blob) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[7] + mi := &file_node_node_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -535,7 +747,7 @@ func (x *Blob) ProtoReflect() protoreflect.Message { // Deprecated: Use Blob.ProtoReflect.Descriptor instead. func (*Blob) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{7} + return file_node_node_proto_rawDescGZIP(), []int{11} } func (x *Blob) GetHeader() *BlobHeader { @@ -569,7 +781,7 @@ type Bundle struct { func (x *Bundle) Reset() { *x = Bundle{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[8] + mi := &file_node_node_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -582,7 +794,7 @@ func (x *Bundle) String() string { func (*Bundle) ProtoMessage() {} func (x *Bundle) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[8] + mi := &file_node_node_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -595,7 +807,7 @@ func (x *Bundle) ProtoReflect() protoreflect.Message { // Deprecated: Use Bundle.ProtoReflect.Descriptor instead. func (*Bundle) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{8} + return file_node_node_proto_rawDescGZIP(), []int{12} } func (x *Bundle) GetChunks() [][]byte { @@ -630,7 +842,7 @@ type G2Commitment struct { func (x *G2Commitment) Reset() { *x = G2Commitment{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[9] + mi := &file_node_node_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -643,7 +855,7 @@ func (x *G2Commitment) String() string { func (*G2Commitment) ProtoMessage() {} func (x *G2Commitment) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[9] + mi := &file_node_node_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -656,7 +868,7 @@ func (x *G2Commitment) ProtoReflect() protoreflect.Message { // Deprecated: Use G2Commitment.ProtoReflect.Descriptor instead. func (*G2Commitment) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{9} + return file_node_node_proto_rawDescGZIP(), []int{13} } func (x *G2Commitment) GetXA0() []byte { @@ -707,12 +919,14 @@ type BlobHeader struct { QuorumHeaders []*BlobQuorumInfo `protobuf:"bytes,5,rep,name=quorum_headers,json=quorumHeaders,proto3" json:"quorum_headers,omitempty"` // The ID of the user who is dispersing this blob to EigenDA. AccountId string `protobuf:"bytes,6,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` + // The reference block number whose state is used to encode the blob + ReferenceBlockNumber uint32 `protobuf:"varint,7,opt,name=reference_block_number,json=referenceBlockNumber,proto3" json:"reference_block_number,omitempty"` } func (x *BlobHeader) Reset() { *x = BlobHeader{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[10] + mi := &file_node_node_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -725,7 +939,7 @@ func (x *BlobHeader) String() string { func (*BlobHeader) ProtoMessage() {} func (x *BlobHeader) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[10] + mi := &file_node_node_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -738,7 +952,7 @@ func (x *BlobHeader) ProtoReflect() protoreflect.Message { // Deprecated: Use BlobHeader.ProtoReflect.Descriptor instead. func (*BlobHeader) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{10} + return file_node_node_proto_rawDescGZIP(), []int{14} } func (x *BlobHeader) GetCommitment() *common.G1Commitment { @@ -783,6 +997,13 @@ func (x *BlobHeader) GetAccountId() string { return "" } +func (x *BlobHeader) GetReferenceBlockNumber() uint32 { + if x != nil { + return x.ReferenceBlockNumber + } + return 0 +} + // See BlobQuorumParam as defined in // api/proto/disperser/disperser.proto type BlobQuorumInfo struct { @@ -800,7 +1021,7 @@ type BlobQuorumInfo struct { func (x *BlobQuorumInfo) Reset() { *x = BlobQuorumInfo{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[11] + mi := &file_node_node_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -813,7 +1034,7 @@ func (x *BlobQuorumInfo) String() string { func (*BlobQuorumInfo) ProtoMessage() {} func (x *BlobQuorumInfo) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[11] + mi := &file_node_node_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -826,7 +1047,7 @@ func (x *BlobQuorumInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use BlobQuorumInfo.ProtoReflect.Descriptor instead. func (*BlobQuorumInfo) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{11} + return file_node_node_proto_rawDescGZIP(), []int{15} } func (x *BlobQuorumInfo) GetQuorumId() uint32 { @@ -879,7 +1100,7 @@ type BatchHeader struct { func (x *BatchHeader) Reset() { *x = BatchHeader{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[12] + mi := &file_node_node_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -892,7 +1113,7 @@ func (x *BatchHeader) String() string { func (*BatchHeader) ProtoMessage() {} func (x *BatchHeader) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[12] + mi := &file_node_node_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -905,7 +1126,7 @@ func (x *BatchHeader) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchHeader.ProtoReflect.Descriptor instead. func (*BatchHeader) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{12} + return file_node_node_proto_rawDescGZIP(), []int{16} } func (x *BatchHeader) GetBatchRoot() []byte { @@ -932,7 +1153,7 @@ type NodeInfoRequest struct { func (x *NodeInfoRequest) Reset() { *x = NodeInfoRequest{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[13] + mi := &file_node_node_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -945,7 +1166,7 @@ func (x *NodeInfoRequest) String() string { func (*NodeInfoRequest) ProtoMessage() {} func (x *NodeInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[13] + mi := &file_node_node_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -958,7 +1179,7 @@ func (x *NodeInfoRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeInfoRequest.ProtoReflect.Descriptor instead. func (*NodeInfoRequest) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{13} + return file_node_node_proto_rawDescGZIP(), []int{17} } // Node info reply @@ -977,7 +1198,7 @@ type NodeInfoReply struct { func (x *NodeInfoReply) Reset() { *x = NodeInfoReply{} if protoimpl.UnsafeEnabled { - mi := &file_node_node_proto_msgTypes[14] + mi := &file_node_node_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -990,7 +1211,7 @@ func (x *NodeInfoReply) String() string { func (*NodeInfoReply) ProtoMessage() {} func (x *NodeInfoReply) ProtoReflect() protoreflect.Message { - mi := &file_node_node_proto_msgTypes[14] + mi := &file_node_node_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1003,7 +1224,7 @@ func (x *NodeInfoReply) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeInfoReply.ProtoReflect.Descriptor instead. func (*NodeInfoReply) Descriptor() ([]byte, []int) { - return file_node_node_proto_rawDescGZIP(), []int{14} + return file_node_node_proto_rawDescGZIP(), []int{18} } func (x *NodeInfoReply) GetSemver() string { @@ -1045,7 +1266,9 @@ var File_node_node_proto protoreflect.FileDescriptor var file_node_node_proto_rawDesc = []byte{ 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x1a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, + 0x6f, 0x12, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6c, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x0c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x68, 0x65, 0x61, 0x64, @@ -1056,133 +1279,167 @@ var file_node_node_proto_rawDesc = []byte{ 0x6c, 0x6f, 0x62, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x22, 0x30, 0x0a, 0x10, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x7f, 0x0a, 0x15, - 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, - 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x49, 0x64, 0x22, 0x5e, 0x0a, - 0x13, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, - 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x2f, 0x0a, 0x08, - 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, - 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x45, 0x6e, 0x63, 0x6f, 0x64, - 0x69, 0x6e, 0x67, 0x52, 0x08, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x7e, 0x0a, - 0x14, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, - 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x49, 0x64, 0x22, 0x70, 0x0a, - 0x12, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, - 0x70, 0x6c, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, - 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x62, - 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x4d, 0x65, 0x72, - 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x22, - 0x3b, 0x0a, 0x0b, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x16, - 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, - 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x58, 0x0a, 0x04, - 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x28, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x62, - 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x26, - 0x0a, 0x07, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x0c, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x07, 0x62, - 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x22, 0x38, 0x0a, 0x06, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, - 0x52, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x6e, 0x64, - 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x22, 0x5a, 0x0a, 0x0c, 0x47, 0x32, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, - 0x12, 0x11, 0x0a, 0x04, 0x78, 0x5f, 0x61, 0x30, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, - 0x78, 0x41, 0x30, 0x12, 0x11, 0x0a, 0x04, 0x78, 0x5f, 0x61, 0x31, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x03, 0x78, 0x41, 0x31, 0x12, 0x11, 0x0a, 0x04, 0x79, 0x5f, 0x61, 0x30, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x79, 0x41, 0x30, 0x12, 0x11, 0x0a, 0x04, 0x79, 0x5f, 0x61, - 0x31, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x79, 0x41, 0x31, 0x22, 0xae, 0x02, 0x0a, - 0x0a, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x0a, 0x63, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x14, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x31, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, - 0x74, 0x12, 0x3f, 0x0a, 0x11, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, - 0x6f, 0x64, 0x65, 0x2e, 0x47, 0x32, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, - 0x52, 0x10, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, - 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x0c, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, - 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, - 0x47, 0x32, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x6c, 0x65, - 0x6e, 0x67, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, - 0x67, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, - 0x68, 0x12, 0x3b, 0x0a, 0x0e, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6e, 0x6f, 0x64, 0x65, - 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x51, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x0d, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1d, - 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xd6, 0x01, - 0x0a, 0x0e, 0x42, 0x6c, 0x6f, 0x62, 0x51, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x49, 0x64, 0x12, 0x2f, 0x0a, - 0x13, 0x61, 0x64, 0x76, 0x65, 0x72, 0x73, 0x61, 0x72, 0x79, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, - 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x61, 0x64, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x72, 0x79, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x35, - 0x0a, 0x16, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, - 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x68, 0x72, 0x65, - 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x6c, - 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x68, 0x75, - 0x6e, 0x6b, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x61, 0x74, 0x65, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x61, 0x74, - 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x62, 0x0a, 0x0b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x72, - 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, - 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x11, 0x0a, 0x0f, 0x4e, 0x6f, - 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x81, 0x01, - 0x0a, 0x0d, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, - 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x63, 0x68, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x63, 0x68, 0x12, 0x0e, 0x0a, 0x02, 0x6f, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6e, - 0x75, 0x6d, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6e, 0x75, - 0x6d, 0x43, 0x70, 0x75, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x42, 0x79, 0x74, 0x65, - 0x73, 0x2a, 0x30, 0x0a, 0x0d, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, - 0x6e, 0x67, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, - 0x09, 0x0a, 0x05, 0x47, 0x4e, 0x41, 0x52, 0x4b, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x4f, - 0x42, 0x10, 0x02, 0x32, 0x88, 0x01, 0x0a, 0x09, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x61, - 0x6c, 0x12, 0x41, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, - 0x12, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, - 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, - 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x15, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x4e, - 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x32, 0xda, - 0x01, 0x0a, 0x09, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x61, 0x6c, 0x12, 0x4a, 0x0a, 0x0e, - 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x1b, - 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x43, 0x68, - 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6e, 0x6f, - 0x64, 0x65, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, - 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x42, - 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x6e, 0x6f, 0x64, 0x65, - 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x47, 0x65, 0x74, - 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, - 0x00, 0x12, 0x38, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, - 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x2c, 0x5a, 0x2a, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4c, 0x61, 0x79, 0x72, 0x2d, 0x4c, - 0x61, 0x62, 0x73, 0x2f, 0x65, 0x69, 0x67, 0x65, 0x6e, 0x64, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x67, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x6b, 0x0a, 0x11, + 0x53, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x20, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0a, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x05, 0x62, 0x6c, + 0x6f, 0x62, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x14, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x4e, 0x0a, 0x0f, 0x53, 0x74, 0x6f, + 0x72, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3b, 0x0a, 0x0a, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x78, 0x0a, 0x12, 0x41, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x34, 0x0a, 0x0c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0b, 0x62, 0x61, 0x74, 0x63, 0x68, 0x48, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x68, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x10, 0x62, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, + 0x68, 0x65, 0x73, 0x22, 0x30, 0x0a, 0x10, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x7f, 0x0a, 0x15, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, + 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, + 0x0a, 0x11, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, + 0x6f, 0x62, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x62, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, + 0x72, 0x75, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, + 0x6f, 0x72, 0x75, 0x6d, 0x49, 0x64, 0x22, 0x5e, 0x0a, 0x13, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, + 0x76, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, + 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x63, + 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x2f, 0x0a, 0x08, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x43, + 0x68, 0x75, 0x6e, 0x6b, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x65, 0x6e, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x7e, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, + 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, + 0x0a, 0x11, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, + 0x6f, 0x62, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x62, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, + 0x72, 0x75, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, + 0x6f, 0x72, 0x75, 0x6d, 0x49, 0x64, 0x22, 0x70, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, + 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x31, 0x0a, 0x0b, + 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, + 0x27, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6f, + 0x66, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x3b, 0x0a, 0x0b, 0x4d, 0x65, 0x72, 0x6b, + 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, + 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x58, 0x0a, 0x04, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x28, 0x0a, + 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, + 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x07, 0x62, 0x75, 0x6e, 0x64, 0x6c, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, + 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x07, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x22, + 0x38, 0x0a, 0x06, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x75, + 0x6e, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x06, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x22, 0x5a, 0x0a, 0x0c, 0x47, 0x32, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x11, 0x0a, 0x04, 0x78, 0x5f, 0x61, + 0x30, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x78, 0x41, 0x30, 0x12, 0x11, 0x0a, 0x04, + 0x78, 0x5f, 0x61, 0x31, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x78, 0x41, 0x31, 0x12, + 0x11, 0x0a, 0x04, 0x79, 0x5f, 0x61, 0x30, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x79, + 0x41, 0x30, 0x12, 0x11, 0x0a, 0x04, 0x79, 0x5f, 0x61, 0x31, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x03, 0x79, 0x41, 0x31, 0x22, 0xe4, 0x02, 0x0a, 0x0a, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, + 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x47, 0x31, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0a, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3f, 0x0a, 0x11, 0x6c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x47, 0x32, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x0c, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x47, 0x32, 0x43, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x50, 0x72, 0x6f, + 0x6f, 0x66, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x3b, 0x0a, 0x0e, 0x71, 0x75, + 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x51, 0x75, + 0x6f, 0x72, 0x75, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0d, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0xd6, 0x01, 0x0a, + 0x0e, 0x42, 0x6c, 0x6f, 0x62, 0x51, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x13, + 0x61, 0x64, 0x76, 0x65, 0x72, 0x73, 0x61, 0x72, 0x79, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, + 0x6f, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x61, 0x64, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x72, 0x79, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x35, 0x0a, + 0x16, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x68, + 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x68, 0x72, 0x65, 0x73, + 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x6c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x68, 0x75, 0x6e, + 0x6b, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x61, 0x74, 0x65, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x61, 0x74, 0x65, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x62, 0x0a, 0x0b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x72, 0x6f, + 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x52, + 0x6f, 0x6f, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x14, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x11, 0x0a, 0x0f, 0x4e, 0x6f, 0x64, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x81, 0x01, 0x0a, + 0x0d, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x63, 0x68, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x63, 0x68, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x75, + 0x6d, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6e, 0x75, 0x6d, + 0x43, 0x70, 0x75, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x2a, 0x30, 0x0a, 0x0d, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, + 0x67, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, + 0x0a, 0x05, 0x47, 0x4e, 0x41, 0x52, 0x4b, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x4f, 0x42, + 0x10, 0x02, 0x32, 0x8b, 0x02, 0x0a, 0x09, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x61, 0x6c, + 0x12, 0x41, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, + 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, + 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x6c, 0x6f, 0x62, + 0x73, 0x12, 0x17, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x6c, + 0x6f, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6e, 0x6f, 0x64, + 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0b, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x12, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6e, + 0x6f, 0x64, 0x65, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, + 0x32, 0xda, 0x01, 0x0a, 0x09, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x61, 0x6c, 0x12, 0x4a, + 0x0a, 0x0e, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, + 0x12, 0x1b, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, + 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, + 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x43, 0x68, 0x75, + 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0d, 0x47, 0x65, + 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x6e, 0x6f, + 0x64, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x47, + 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x22, 0x00, 0x12, 0x38, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x15, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x2c, 0x5a, + 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4c, 0x61, 0x79, 0x72, + 0x2d, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x69, 0x67, 0x65, 0x6e, 0x64, 0x61, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -1198,53 +1455,65 @@ func file_node_node_proto_rawDescGZIP() []byte { } var file_node_node_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_node_node_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_node_node_proto_msgTypes = make([]protoimpl.MessageInfo, 19) var file_node_node_proto_goTypes = []interface{}{ (ChunkEncoding)(0), // 0: node.ChunkEncoding (*StoreChunksRequest)(nil), // 1: node.StoreChunksRequest (*StoreChunksReply)(nil), // 2: node.StoreChunksReply - (*RetrieveChunksRequest)(nil), // 3: node.RetrieveChunksRequest - (*RetrieveChunksReply)(nil), // 4: node.RetrieveChunksReply - (*GetBlobHeaderRequest)(nil), // 5: node.GetBlobHeaderRequest - (*GetBlobHeaderReply)(nil), // 6: node.GetBlobHeaderReply - (*MerkleProof)(nil), // 7: node.MerkleProof - (*Blob)(nil), // 8: node.Blob - (*Bundle)(nil), // 9: node.Bundle - (*G2Commitment)(nil), // 10: node.G2Commitment - (*BlobHeader)(nil), // 11: node.BlobHeader - (*BlobQuorumInfo)(nil), // 12: node.BlobQuorumInfo - (*BatchHeader)(nil), // 13: node.BatchHeader - (*NodeInfoRequest)(nil), // 14: node.NodeInfoRequest - (*NodeInfoReply)(nil), // 15: node.NodeInfoReply - (*common.G1Commitment)(nil), // 16: common.G1Commitment + (*StoreBlobsRequest)(nil), // 3: node.StoreBlobsRequest + (*StoreBlobsReply)(nil), // 4: node.StoreBlobsReply + (*AttestBatchRequest)(nil), // 5: node.AttestBatchRequest + (*AttestBatchReply)(nil), // 6: node.AttestBatchReply + (*RetrieveChunksRequest)(nil), // 7: node.RetrieveChunksRequest + (*RetrieveChunksReply)(nil), // 8: node.RetrieveChunksReply + (*GetBlobHeaderRequest)(nil), // 9: node.GetBlobHeaderRequest + (*GetBlobHeaderReply)(nil), // 10: node.GetBlobHeaderReply + (*MerkleProof)(nil), // 11: node.MerkleProof + (*Blob)(nil), // 12: node.Blob + (*Bundle)(nil), // 13: node.Bundle + (*G2Commitment)(nil), // 14: node.G2Commitment + (*BlobHeader)(nil), // 15: node.BlobHeader + (*BlobQuorumInfo)(nil), // 16: node.BlobQuorumInfo + (*BatchHeader)(nil), // 17: node.BatchHeader + (*NodeInfoRequest)(nil), // 18: node.NodeInfoRequest + (*NodeInfoReply)(nil), // 19: node.NodeInfoReply + (*wrapperspb.BytesValue)(nil), // 20: google.protobuf.BytesValue + (*common.G1Commitment)(nil), // 21: common.G1Commitment } var file_node_node_proto_depIdxs = []int32{ - 13, // 0: node.StoreChunksRequest.batch_header:type_name -> node.BatchHeader - 8, // 1: node.StoreChunksRequest.blobs:type_name -> node.Blob - 0, // 2: node.RetrieveChunksReply.encoding:type_name -> node.ChunkEncoding - 11, // 3: node.GetBlobHeaderReply.blob_header:type_name -> node.BlobHeader - 7, // 4: node.GetBlobHeaderReply.proof:type_name -> node.MerkleProof - 11, // 5: node.Blob.header:type_name -> node.BlobHeader - 9, // 6: node.Blob.bundles:type_name -> node.Bundle - 16, // 7: node.BlobHeader.commitment:type_name -> common.G1Commitment - 10, // 8: node.BlobHeader.length_commitment:type_name -> node.G2Commitment - 10, // 9: node.BlobHeader.length_proof:type_name -> node.G2Commitment - 12, // 10: node.BlobHeader.quorum_headers:type_name -> node.BlobQuorumInfo - 1, // 11: node.Dispersal.StoreChunks:input_type -> node.StoreChunksRequest - 14, // 12: node.Dispersal.NodeInfo:input_type -> node.NodeInfoRequest - 3, // 13: node.Retrieval.RetrieveChunks:input_type -> node.RetrieveChunksRequest - 5, // 14: node.Retrieval.GetBlobHeader:input_type -> node.GetBlobHeaderRequest - 14, // 15: node.Retrieval.NodeInfo:input_type -> node.NodeInfoRequest - 2, // 16: node.Dispersal.StoreChunks:output_type -> node.StoreChunksReply - 15, // 17: node.Dispersal.NodeInfo:output_type -> node.NodeInfoReply - 4, // 18: node.Retrieval.RetrieveChunks:output_type -> node.RetrieveChunksReply - 6, // 19: node.Retrieval.GetBlobHeader:output_type -> node.GetBlobHeaderReply - 15, // 20: node.Retrieval.NodeInfo:output_type -> node.NodeInfoReply - 16, // [16:21] is the sub-list for method output_type - 11, // [11:16] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 17, // 0: node.StoreChunksRequest.batch_header:type_name -> node.BatchHeader + 12, // 1: node.StoreChunksRequest.blobs:type_name -> node.Blob + 12, // 2: node.StoreBlobsRequest.blobs:type_name -> node.Blob + 20, // 3: node.StoreBlobsReply.signatures:type_name -> google.protobuf.BytesValue + 17, // 4: node.AttestBatchRequest.batch_header:type_name -> node.BatchHeader + 0, // 5: node.RetrieveChunksReply.encoding:type_name -> node.ChunkEncoding + 15, // 6: node.GetBlobHeaderReply.blob_header:type_name -> node.BlobHeader + 11, // 7: node.GetBlobHeaderReply.proof:type_name -> node.MerkleProof + 15, // 8: node.Blob.header:type_name -> node.BlobHeader + 13, // 9: node.Blob.bundles:type_name -> node.Bundle + 21, // 10: node.BlobHeader.commitment:type_name -> common.G1Commitment + 14, // 11: node.BlobHeader.length_commitment:type_name -> node.G2Commitment + 14, // 12: node.BlobHeader.length_proof:type_name -> node.G2Commitment + 16, // 13: node.BlobHeader.quorum_headers:type_name -> node.BlobQuorumInfo + 1, // 14: node.Dispersal.StoreChunks:input_type -> node.StoreChunksRequest + 3, // 15: node.Dispersal.StoreBlobs:input_type -> node.StoreBlobsRequest + 5, // 16: node.Dispersal.AttestBatch:input_type -> node.AttestBatchRequest + 18, // 17: node.Dispersal.NodeInfo:input_type -> node.NodeInfoRequest + 7, // 18: node.Retrieval.RetrieveChunks:input_type -> node.RetrieveChunksRequest + 9, // 19: node.Retrieval.GetBlobHeader:input_type -> node.GetBlobHeaderRequest + 18, // 20: node.Retrieval.NodeInfo:input_type -> node.NodeInfoRequest + 2, // 21: node.Dispersal.StoreChunks:output_type -> node.StoreChunksReply + 4, // 22: node.Dispersal.StoreBlobs:output_type -> node.StoreBlobsReply + 6, // 23: node.Dispersal.AttestBatch:output_type -> node.AttestBatchReply + 19, // 24: node.Dispersal.NodeInfo:output_type -> node.NodeInfoReply + 8, // 25: node.Retrieval.RetrieveChunks:output_type -> node.RetrieveChunksReply + 10, // 26: node.Retrieval.GetBlobHeader:output_type -> node.GetBlobHeaderReply + 19, // 27: node.Retrieval.NodeInfo:output_type -> node.NodeInfoReply + 21, // [21:28] is the sub-list for method output_type + 14, // [14:21] is the sub-list for method input_type + 14, // [14:14] is the sub-list for extension type_name + 14, // [14:14] is the sub-list for extension extendee + 0, // [0:14] is the sub-list for field type_name } func init() { file_node_node_proto_init() } @@ -1278,7 +1547,7 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RetrieveChunksRequest); i { + switch v := v.(*StoreBlobsRequest); i { case 0: return &v.state case 1: @@ -1290,7 +1559,7 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RetrieveChunksReply); i { + switch v := v.(*StoreBlobsReply); i { case 0: return &v.state case 1: @@ -1302,7 +1571,7 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBlobHeaderRequest); i { + switch v := v.(*AttestBatchRequest); i { case 0: return &v.state case 1: @@ -1314,7 +1583,7 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBlobHeaderReply); i { + switch v := v.(*AttestBatchReply); i { case 0: return &v.state case 1: @@ -1326,7 +1595,7 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MerkleProof); i { + switch v := v.(*RetrieveChunksRequest); i { case 0: return &v.state case 1: @@ -1338,7 +1607,7 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Blob); i { + switch v := v.(*RetrieveChunksReply); i { case 0: return &v.state case 1: @@ -1350,7 +1619,7 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Bundle); i { + switch v := v.(*GetBlobHeaderRequest); i { case 0: return &v.state case 1: @@ -1362,7 +1631,7 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*G2Commitment); i { + switch v := v.(*GetBlobHeaderReply); i { case 0: return &v.state case 1: @@ -1374,7 +1643,7 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlobHeader); i { + switch v := v.(*MerkleProof); i { case 0: return &v.state case 1: @@ -1386,7 +1655,7 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlobQuorumInfo); i { + switch v := v.(*Blob); i { case 0: return &v.state case 1: @@ -1398,7 +1667,7 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BatchHeader); i { + switch v := v.(*Bundle); i { case 0: return &v.state case 1: @@ -1410,7 +1679,7 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeInfoRequest); i { + switch v := v.(*G2Commitment); i { case 0: return &v.state case 1: @@ -1422,6 +1691,54 @@ func file_node_node_proto_init() { } } file_node_node_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlobHeader); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_node_node_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlobQuorumInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_node_node_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BatchHeader); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_node_node_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeInfoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_node_node_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NodeInfoReply); i { case 0: return &v.state @@ -1440,7 +1757,7 @@ func file_node_node_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_node_node_proto_rawDesc, NumEnums: 1, - NumMessages: 15, + NumMessages: 19, NumExtensions: 0, NumServices: 2, }, diff --git a/api/grpc/node/node_grpc.pb.go b/api/grpc/node/node_grpc.pb.go index 456dc6218e..28a516cec7 100644 --- a/api/grpc/node/node_grpc.pb.go +++ b/api/grpc/node/node_grpc.pb.go @@ -20,6 +20,8 @@ const _ = grpc.SupportPackageIsVersion7 const ( Dispersal_StoreChunks_FullMethodName = "/node.Dispersal/StoreChunks" + Dispersal_StoreBlobs_FullMethodName = "/node.Dispersal/StoreBlobs" + Dispersal_AttestBatch_FullMethodName = "/node.Dispersal/AttestBatch" Dispersal_NodeInfo_FullMethodName = "/node.Dispersal/NodeInfo" ) @@ -34,6 +36,13 @@ type DispersalClient interface { // for the protocol-defined length of custody. It will return a signature at the // end to attest to the data in this request it has processed. StoreChunks(ctx context.Context, in *StoreChunksRequest, opts ...grpc.CallOption) (*StoreChunksReply, error) + // StoreBlobs is simiar to StoreChunks, but it stores the blobs using a different storage schema + // so that the stored blobs can later be aggregated by AttestBatch method to a bigger batch. + // StoreBlobs + AttestBatch will eventually replace and deprecate StoreChunks method. + StoreBlobs(ctx context.Context, in *StoreBlobsRequest, opts ...grpc.CallOption) (*StoreBlobsReply, error) + // AttestBatch is used to aggregate the batches stored by StoreBlobs method to a bigger batch. + // It will return a signature at the end to attest to the aggregated batch. + AttestBatch(ctx context.Context, in *AttestBatchRequest, opts ...grpc.CallOption) (*AttestBatchReply, error) // Retrieve node info metadata NodeInfo(ctx context.Context, in *NodeInfoRequest, opts ...grpc.CallOption) (*NodeInfoReply, error) } @@ -55,6 +64,24 @@ func (c *dispersalClient) StoreChunks(ctx context.Context, in *StoreChunksReques return out, nil } +func (c *dispersalClient) StoreBlobs(ctx context.Context, in *StoreBlobsRequest, opts ...grpc.CallOption) (*StoreBlobsReply, error) { + out := new(StoreBlobsReply) + err := c.cc.Invoke(ctx, Dispersal_StoreBlobs_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dispersalClient) AttestBatch(ctx context.Context, in *AttestBatchRequest, opts ...grpc.CallOption) (*AttestBatchReply, error) { + out := new(AttestBatchReply) + err := c.cc.Invoke(ctx, Dispersal_AttestBatch_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *dispersalClient) NodeInfo(ctx context.Context, in *NodeInfoRequest, opts ...grpc.CallOption) (*NodeInfoReply, error) { out := new(NodeInfoReply) err := c.cc.Invoke(ctx, Dispersal_NodeInfo_FullMethodName, in, out, opts...) @@ -75,6 +102,13 @@ type DispersalServer interface { // for the protocol-defined length of custody. It will return a signature at the // end to attest to the data in this request it has processed. StoreChunks(context.Context, *StoreChunksRequest) (*StoreChunksReply, error) + // StoreBlobs is simiar to StoreChunks, but it stores the blobs using a different storage schema + // so that the stored blobs can later be aggregated by AttestBatch method to a bigger batch. + // StoreBlobs + AttestBatch will eventually replace and deprecate StoreChunks method. + StoreBlobs(context.Context, *StoreBlobsRequest) (*StoreBlobsReply, error) + // AttestBatch is used to aggregate the batches stored by StoreBlobs method to a bigger batch. + // It will return a signature at the end to attest to the aggregated batch. + AttestBatch(context.Context, *AttestBatchRequest) (*AttestBatchReply, error) // Retrieve node info metadata NodeInfo(context.Context, *NodeInfoRequest) (*NodeInfoReply, error) mustEmbedUnimplementedDispersalServer() @@ -87,6 +121,12 @@ type UnimplementedDispersalServer struct { func (UnimplementedDispersalServer) StoreChunks(context.Context, *StoreChunksRequest) (*StoreChunksReply, error) { return nil, status.Errorf(codes.Unimplemented, "method StoreChunks not implemented") } +func (UnimplementedDispersalServer) StoreBlobs(context.Context, *StoreBlobsRequest) (*StoreBlobsReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method StoreBlobs not implemented") +} +func (UnimplementedDispersalServer) AttestBatch(context.Context, *AttestBatchRequest) (*AttestBatchReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method AttestBatch not implemented") +} func (UnimplementedDispersalServer) NodeInfo(context.Context, *NodeInfoRequest) (*NodeInfoReply, error) { return nil, status.Errorf(codes.Unimplemented, "method NodeInfo not implemented") } @@ -121,6 +161,42 @@ func _Dispersal_StoreChunks_Handler(srv interface{}, ctx context.Context, dec fu return interceptor(ctx, in, info, handler) } +func _Dispersal_StoreBlobs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StoreBlobsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DispersalServer).StoreBlobs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Dispersal_StoreBlobs_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DispersalServer).StoreBlobs(ctx, req.(*StoreBlobsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Dispersal_AttestBatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AttestBatchRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DispersalServer).AttestBatch(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Dispersal_AttestBatch_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DispersalServer).AttestBatch(ctx, req.(*AttestBatchRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Dispersal_NodeInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(NodeInfoRequest) if err := dec(in); err != nil { @@ -150,6 +226,14 @@ var Dispersal_ServiceDesc = grpc.ServiceDesc{ MethodName: "StoreChunks", Handler: _Dispersal_StoreChunks_Handler, }, + { + MethodName: "StoreBlobs", + Handler: _Dispersal_StoreBlobs_Handler, + }, + { + MethodName: "AttestBatch", + Handler: _Dispersal_AttestBatch_Handler, + }, { MethodName: "NodeInfo", Handler: _Dispersal_NodeInfo_Handler, diff --git a/api/proto/node/node.proto b/api/proto/node/node.proto index 932135cafe..a9f4afb4d0 100644 --- a/api/proto/node/node.proto +++ b/api/proto/node/node.proto @@ -1,5 +1,6 @@ syntax = "proto3"; package node; +import "google/protobuf/wrappers.proto"; import "common/common.proto"; option go_package = "github.com/Layr-Labs/eigenda/api/grpc/node"; @@ -14,6 +15,13 @@ service Dispersal { // for the protocol-defined length of custody. It will return a signature at the // end to attest to the data in this request it has processed. rpc StoreChunks(StoreChunksRequest) returns (StoreChunksReply) {} + // StoreBlobs is simiar to StoreChunks, but it stores the blobs using a different storage schema + // so that the stored blobs can later be aggregated by AttestBatch method to a bigger batch. + // StoreBlobs + AttestBatch will eventually replace and deprecate StoreChunks method. + rpc StoreBlobs(StoreBlobsRequest) returns (StoreBlobsReply) {} + // AttestBatch is used to aggregate the batches stored by StoreBlobs method to a bigger batch. + // It will return a signature at the end to attest to the aggregated batch. + rpc AttestBatch(AttestBatchRequest) returns (AttestBatchReply) {} // Retrieve node info metadata rpc NodeInfo(NodeInfoRequest) returns (NodeInfoReply) {} } @@ -41,6 +49,31 @@ message StoreChunksReply { bytes signature = 1; } +message StoreBlobsRequest { + // Blobs to store + repeated Blob blobs = 1; + // The reference block number whose state is used to encode the blobs + uint32 reference_block_number = 2; +} + +message StoreBlobsReply { + // The operator's BLS sgnature signed on the blob header hashes. + // The ordering of the signatures must match the ordering of the blobs sent + // in the request, with empty signatures in the places for discarded blobs. + repeated google.protobuf.BytesValue signatures = 1; +} + +message AttestBatchRequest { + // header of the batch + BatchHeader batch_header = 1; + // the header hashes of all blobs in the batch + repeated bytes blob_header_hashes = 2; +} + +message AttestBatchReply { + bytes signature = 1; +} + message RetrieveChunksRequest { // The hash of the ReducedBatchHeader defined onchain, see: // https://github.com/Layr-Labs/eigenda/blob/master/contracts/src/interfaces/IEigenDAServiceManager.sol#L43 @@ -149,6 +182,8 @@ message BlobHeader { repeated BlobQuorumInfo quorum_headers = 5; // The ID of the user who is dispersing this blob to EigenDA. string account_id = 6; + // The reference block number whose state is used to encode the blob + uint32 reference_block_number = 7; } // See BlobQuorumParam as defined in diff --git a/node/grpc/server.go b/node/grpc/server.go index 598386f9f3..01f82cdea7 100644 --- a/node/grpc/server.go +++ b/node/grpc/server.go @@ -249,6 +249,14 @@ func (s *Server) StoreChunks(ctx context.Context, in *pb.StoreChunksRequest) (*p return reply, err } +func (s *Server) StoreBlobs(ctx context.Context, in *pb.StoreBlobsRequest) (*pb.StoreBlobsReply, error) { + return nil, errors.New("StoreBlobs is not implemented") +} + +func (s *Server) AttestBatch(ctx context.Context, in *pb.AttestBatchRequest) (*pb.AttestBatchReply, error) { + return nil, errors.New("AttestBatch is not implemented") +} + func (s *Server) RetrieveChunks(ctx context.Context, in *pb.RetrieveChunksRequest) (*pb.RetrieveChunksReply, error) { start := time.Now() diff --git a/node/grpc/server_test.go b/node/grpc/server_test.go index 84623ec94c..2285e581d4 100644 --- a/node/grpc/server_test.go +++ b/node/grpc/server_test.go @@ -340,6 +340,36 @@ func TestStoreChunksRequestValidation(t *testing.T) { assert.True(t, strings.Contains(err.Error(), "adversary threshold equals 0")) } +func TestStoreBlobs(t *testing.T) { + server := newTestServer(t, true) + + reqToCopy, _, _, _, _ := makeStoreChunksRequest(t, 66, 33) + reqToCopy.BatchHeader = nil + req := &pb.StoreBlobsRequest{ + Blobs: reqToCopy.Blobs, + ReferenceBlockNumber: 1, + } + reply, err := server.StoreBlobs(context.Background(), req) + assert.Nil(t, reply) + assert.Error(t, err) + assert.Equal(t, strings.Compare(err.Error(), "StoreBlobs is not implemented"), 0) +} + +func TestAttestBatch(t *testing.T) { + server := newTestServer(t, true) + + reqToCopy, _, _, _, _ := makeStoreChunksRequest(t, 66, 33) + reqToCopy.BatchHeader = nil + req := &pb.AttestBatchRequest{ + BatchHeader: reqToCopy.BatchHeader, + BlobHeaderHashes: [][]byte{}, + } + reply, err := server.AttestBatch(context.Background(), req) + assert.Nil(t, reply) + assert.Error(t, err) + assert.Equal(t, strings.Compare(err.Error(), "AttestBatch is not implemented"), 0) +} + func TestRetrieveChunks(t *testing.T) { server := newTestServer(t, true) batchHeaderHash, _, _, _ := storeChunks(t, server) From 52d60a73dc65e4c0a9f26c5dde9b101e50da7d4f Mon Sep 17 00:00:00 2001 From: Ian Shim <100327837+ian-shim@users.noreply.github.com> Date: Thu, 18 Jul 2024 20:46:17 -0700 Subject: [PATCH 3/4] [minibatch] Create minibatch from encoding streamer (#637) --- disperser/batcher/encoded_blob_store.go | 26 +++ disperser/batcher/encoding_streamer.go | 148 ++++++++++++++++- disperser/batcher/encoding_streamer_test.go | 168 ++++++++++++++++++++ 3 files changed, 340 insertions(+), 2 deletions(-) diff --git a/disperser/batcher/encoded_blob_store.go b/disperser/batcher/encoded_blob_store.go index f2e2f8b8a3..5ce98d6d71 100644 --- a/disperser/batcher/encoded_blob_store.go +++ b/disperser/batcher/encoded_blob_store.go @@ -133,6 +133,32 @@ func (e *encodedBlobStore) DeleteEncodingResult(blobKey disperser.BlobKey, quoru e.encodedResultSize -= getChunksSize(encodedResult) } +// PopLatestEncodingResults returns all the encoded results that are pending dispersal and deletes them along with stale results that are older than the given reference block +func (e *encodedBlobStore) PopLatestEncodingResults(refBlockNumber uint) []*EncodingResult { + e.mu.Lock() + defer e.mu.Unlock() + + fetched := make([]*EncodingResult, 0) + staleCount := 0 + for k, encodedResult := range e.encoded { + if encodedResult.ReferenceBlockNumber == refBlockNumber { + fetched = append(fetched, encodedResult) + // this is safe: https://go.dev/doc/effective_go#for + delete(e.encoded, k) + e.encodedResultSize -= getChunksSize(encodedResult) + } else if encodedResult.ReferenceBlockNumber < refBlockNumber { + delete(e.encoded, k) + staleCount++ + e.encodedResultSize -= getChunksSize(encodedResult) + } else { + e.logger.Error("unexpected case", "refBlockNumber", encodedResult.ReferenceBlockNumber, "refBlockNumber", refBlockNumber) + } + } + e.logger.Debug("consumed encoded results", "fetched", len(fetched), "stale", staleCount, "refBlockNumber", refBlockNumber, "encodedSize", e.encodedResultSize) + + return fetched +} + // GetNewAndDeleteStaleEncodingResults returns all the fresh encoded results that are pending dispersal, and deletes all the stale results that are older than the given block number func (e *encodedBlobStore) GetNewAndDeleteStaleEncodingResults(blockNumber uint) []*EncodingResult { e.mu.Lock() diff --git a/disperser/batcher/encoding_streamer.go b/disperser/batcher/encoding_streamer.go index 800e90ed2c..78c62ff0da 100644 --- a/disperser/batcher/encoding_streamer.go +++ b/disperser/batcher/encoding_streamer.go @@ -387,9 +387,7 @@ func (e *EncodingStreamer) RequestEncodingForBlob(ctx context.Context, metadata } }) e.EncodedBlobstore.PutEncodingRequest(blobKey, res.BlobQuorumInfo.QuorumID) - } - } func (e *EncodingStreamer) ProcessEncodedBlobs(ctx context.Context, result EncodingResultOrStatus) error { @@ -420,6 +418,152 @@ func (e *EncodingStreamer) ProcessEncodedBlobs(ctx context.Context, result Encod return nil } +func (e *EncodingStreamer) UpdateReferenecBlock(currentBlockNumber uint) error { + blockNumber := currentBlockNumber + if blockNumber > e.FinalizationBlockDelay { + blockNumber -= e.FinalizationBlockDelay + } + if e.ReferenceBlockNumber > blockNumber { + return fmt.Errorf("reference block number is being updated to a lower value: from %d to %d", e.ReferenceBlockNumber, blockNumber) + } + e.mu.Lock() + defer e.mu.Unlock() + if e.ReferenceBlockNumber < blockNumber { + // Wipe out the encoding results based on previous reference block number + _ = e.EncodedBlobstore.PopLatestEncodingResults(e.ReferenceBlockNumber) + } + e.ReferenceBlockNumber = blockNumber + return nil +} + +func (e *EncodingStreamer) CreateMinibatch(ctx context.Context) (*batch, error) { + e.mu.Lock() + defer e.mu.Unlock() + // Cancel outstanding encoding requests + // Assumption: `CreateMinibatch` will be called at an interval longer than time it takes to encode a single blob + if len(e.encodingCtxCancelFuncs) > 0 { + e.logger.Info("canceling outstanding encoding requests", "count", len(e.encodingCtxCancelFuncs)) + for _, cancel := range e.encodingCtxCancelFuncs { + cancel() + } + e.encodingCtxCancelFuncs = make([]context.CancelFunc, 0) + } + + // Pop the latest encoded blobs and delete any stale results that are not from the current batching iteration (i.e. that has different reference block number) + // If any pending encoded results are discarded here, it will be re-requested in the next iteration + encodedResults := e.EncodedBlobstore.PopLatestEncodingResults(e.ReferenceBlockNumber) + + e.logger.Info("creating a batch...", "numBlobs", len(encodedResults), "refblockNumber", e.ReferenceBlockNumber) + if len(encodedResults) == 0 { + return nil, errNoEncodedResults + } + + encodedBlobByKey := make(map[disperser.BlobKey]core.EncodedBlob) + blobQuorums := make(map[disperser.BlobKey][]*core.BlobQuorumInfo) + blobHeaderByKey := make(map[disperser.BlobKey]*core.BlobHeader) + metadataByKey := make(map[disperser.BlobKey]*disperser.BlobMetadata) + for i := range encodedResults { + // each result represent an encoded result per (blob, quorum param) + // if the same blob has been dispersed multiple time with different security params, + // there will be multiple encoded results for that (blob, quorum) + result := encodedResults[i] + blobKey := result.BlobMetadata.GetBlobKey() + if _, ok := encodedBlobByKey[blobKey]; !ok { + metadataByKey[blobKey] = result.BlobMetadata + blobQuorums[blobKey] = make([]*core.BlobQuorumInfo, 0) + blobHeader := &core.BlobHeader{ + BlobCommitments: *result.Commitment, + } + blobHeaderByKey[blobKey] = blobHeader + encodedBlobByKey[blobKey] = core.EncodedBlob{ + BlobHeader: blobHeader, + BundlesByOperator: make(map[core.OperatorID]core.Bundles), + } + } + + // Populate the assigned bundles + for opID, assignment := range result.Assignments { + bundles, ok := encodedBlobByKey[blobKey].BundlesByOperator[opID] + if !ok { + encodedBlobByKey[blobKey].BundlesByOperator[opID] = make(core.Bundles) + bundles = encodedBlobByKey[blobKey].BundlesByOperator[opID] + } + bundles[result.BlobQuorumInfo.QuorumID] = append(bundles[result.BlobQuorumInfo.QuorumID], result.Chunks[assignment.StartIndex:assignment.StartIndex+assignment.NumChunks]...) + } + + blobQuorums[blobKey] = append(blobQuorums[blobKey], result.BlobQuorumInfo) + } + + // Populate the blob quorum infos + for blobKey, encodedBlob := range encodedBlobByKey { + encodedBlob.BlobHeader.QuorumInfos = blobQuorums[blobKey] + } + + for blobKey, metadata := range metadataByKey { + quorumPresent := make(map[core.QuorumID]bool) + for _, quorum := range blobQuorums[blobKey] { + quorumPresent[quorum.QuorumID] = true + } + // Check if the blob has valid quorums. If any of the quorums are not valid, delete the blobKey + for _, quorum := range metadata.RequestMetadata.SecurityParams { + _, ok := quorumPresent[quorum.QuorumID] + if !ok { + // Delete the blobKey. These encoded blobs will be automatically removed by the next run of + // RequestEncoding + delete(metadataByKey, blobKey) + break + } + } + } + + if len(metadataByKey) == 0 { + return nil, errNoEncodedResults + } + + // Transform maps to slices so orders in different slices match + encodedBlobs := make([]core.EncodedBlob, len(metadataByKey)) + blobHeaders := make([]*core.BlobHeader, len(metadataByKey)) + metadatas := make([]*disperser.BlobMetadata, len(metadataByKey)) + i := 0 + for key := range metadataByKey { + err := e.transitionBlobToDispersing(ctx, metadataByKey[key]) + if err != nil { + continue + } + encodedBlobs[i] = encodedBlobByKey[key] + blobHeaders[i] = blobHeaderByKey[key] + metadatas[i] = metadataByKey[key] + i++ + } + + timeoutCtx, cancel := context.WithTimeout(context.Background(), e.ChainStateTimeout) + defer cancel() + + state, err := e.getOperatorState(timeoutCtx, metadatas, e.ReferenceBlockNumber) + if err != nil { + return nil, err + } + + // Populate the batch header + batchHeader := &core.BatchHeader{ + ReferenceBlockNumber: e.ReferenceBlockNumber, + BatchRoot: [32]byte{}, + } + + _, err = batchHeader.SetBatchRoot(blobHeaders) + if err != nil { + return nil, err + } + + return &batch{ + EncodedBlobs: encodedBlobs, + BatchHeader: batchHeader, + BlobHeaders: blobHeaders, + BlobMetadata: metadatas, + State: state, + }, nil +} + // CreateBatch makes a batch from all blobs in the encoded blob store. // If successful, it returns a batch, and updates the reference block number for next batch to use. // Otherwise, it returns an error and keeps the blobs in the encoded blob store. diff --git a/disperser/batcher/encoding_streamer_test.go b/disperser/batcher/encoding_streamer_test.go index c67a90d11e..8769ebb1b7 100644 --- a/disperser/batcher/encoding_streamer_test.go +++ b/disperser/batcher/encoding_streamer_test.go @@ -749,3 +749,171 @@ func TestGetBatch(t *testing.T) { assert.Contains(t, batch.BlobMetadata, metadata1) assert.Contains(t, batch.BlobMetadata, metadata2) } + +func TestCreateMinibatch(t *testing.T) { + encodingStreamer, c := createEncodingStreamer(t, 10, 1e12, streamerConfig) + ctx := context.Background() + + // put 2 blobs in the blobstore + blob1 := makeTestBlob([]*core.SecurityParam{{ + QuorumID: 0, + AdversaryThreshold: 80, + ConfirmationThreshold: 100, + }, { + QuorumID: 1, + AdversaryThreshold: 70, + ConfirmationThreshold: 95, + }}) + blob2 := makeTestBlob([]*core.SecurityParam{{ + QuorumID: 2, + AdversaryThreshold: 75, + ConfirmationThreshold: 100, + }}) + metadataKey1, err := c.blobStore.StoreBlob(ctx, &blob1, uint64(time.Now().UnixNano())) + assert.Nil(t, err) + metadata1, err := c.blobStore.GetBlobMetadata(ctx, metadataKey1) + assert.Nil(t, err) + assert.Equal(t, disperser.Processing, metadata1.BlobStatus) + metadataKey2, err := c.blobStore.StoreBlob(ctx, &blob2, uint64(time.Now().UnixNano())) + assert.Nil(t, err) + metadata2, err := c.blobStore.GetBlobMetadata(ctx, metadataKey2) + assert.Nil(t, err) + assert.Equal(t, disperser.Processing, metadata2.BlobStatus) + + // request encoding + out := make(chan batcher.EncodingResultOrStatus) + err = encodingStreamer.RequestEncoding(context.Background(), out) + assert.Nil(t, err) + isRequested := encodingStreamer.EncodedBlobstore.HasEncodingRequested(metadataKey1, core.QuorumID(0), 10) + assert.True(t, isRequested) + isRequested = encodingStreamer.EncodedBlobstore.HasEncodingRequested(metadataKey1, core.QuorumID(1), 10) + assert.True(t, isRequested) + isRequested = encodingStreamer.EncodedBlobstore.HasEncodingRequested(metadataKey2, core.QuorumID(2), 10) + assert.True(t, isRequested) + + err = encodingStreamer.ProcessEncodedBlobs(context.Background(), <-out) + assert.Nil(t, err) + err = encodingStreamer.ProcessEncodedBlobs(context.Background(), <-out) + assert.Nil(t, err) + err = encodingStreamer.ProcessEncodedBlobs(context.Background(), <-out) + assert.Nil(t, err) + encodingStreamer.Pool.StopWait() + + isRequested = encodingStreamer.EncodedBlobstore.HasEncodingRequested(metadataKey1, core.QuorumID(0), 10) + assert.True(t, isRequested) + isRequested = encodingStreamer.EncodedBlobstore.HasEncodingRequested(metadataKey1, core.QuorumID(1), 10) + assert.True(t, isRequested) + isRequested = encodingStreamer.EncodedBlobstore.HasEncodingRequested(metadataKey2, core.QuorumID(2), 10) + assert.True(t, isRequested) + + // get batch + assert.Equal(t, encodingStreamer.ReferenceBlockNumber, uint(10)) + batch, err := encodingStreamer.CreateMinibatch(context.Background()) + assert.Nil(t, err) + assert.NotNil(t, batch) + assert.Equal(t, encodingStreamer.ReferenceBlockNumber, uint(10)) + metadata1, err = c.blobStore.GetBlobMetadata(ctx, metadataKey1) + assert.Nil(t, err) + assert.Equal(t, disperser.Dispersing, metadata1.BlobStatus) + metadata2, err = c.blobStore.GetBlobMetadata(ctx, metadataKey2) + assert.Equal(t, disperser.Dispersing, metadata2.BlobStatus) + assert.Nil(t, err) + res, err := encodingStreamer.EncodedBlobstore.GetEncodingResult(metadataKey1, core.QuorumID(0)) + assert.Nil(t, res) + assert.ErrorContains(t, err, "GetEncodedBlob: no such key") + res, err = encodingStreamer.EncodedBlobstore.GetEncodingResult(metadataKey1, core.QuorumID(1)) + assert.Nil(t, res) + assert.ErrorContains(t, err, "GetEncodedBlob: no such key") + res, err = encodingStreamer.EncodedBlobstore.GetEncodingResult(metadataKey2, core.QuorumID(0)) + assert.Nil(t, res) + assert.ErrorContains(t, err, "GetEncodedBlob: no such key") + + err = encodingStreamer.UpdateReferenecBlock(15) + assert.Nil(t, err) + assert.Equal(t, encodingStreamer.ReferenceBlockNumber, uint(15)) + + // Check BatchHeader + assert.NotNil(t, batch.BatchHeader) + assert.Greater(t, len(batch.BatchHeader.BatchRoot), 0) + assert.Equal(t, batch.BatchHeader.ReferenceBlockNumber, uint(10)) + + // Check State + assert.NotNil(t, batch.State) + + // Check EncodedBlobs + assert.Len(t, batch.EncodedBlobs, 2) + assert.Len(t, batch.EncodedBlobs[0].BundlesByOperator, numOperators) + + var encodedBlob1 core.EncodedBlob + var encodedBlob2 core.EncodedBlob + for i := range batch.BlobHeaders { + blobHeader := batch.BlobHeaders[i] + if len(blobHeader.QuorumInfos) > 1 { + encodedBlob1 = batch.EncodedBlobs[i] + // batch.EncodedBlobs and batch.BlobMetadata should be in the same order + assert.ElementsMatch(t, batch.BlobMetadata[i].RequestMetadata.SecurityParams, blob1.RequestHeader.SecurityParams) + } else { + encodedBlob2 = batch.EncodedBlobs[i] + assert.ElementsMatch(t, batch.BlobMetadata[i].RequestMetadata.SecurityParams, blob2.RequestHeader.SecurityParams) + } + } + assert.NotNil(t, encodedBlob1) + assert.NotNil(t, encodedBlob2) + + assert.NotNil(t, encodedBlob1.BlobHeader) + assert.NotNil(t, encodedBlob1.BlobHeader.BlobCommitments) + assert.NotNil(t, encodedBlob1.BlobHeader.BlobCommitments.Commitment) + assert.NotNil(t, encodedBlob1.BlobHeader.BlobCommitments.LengthProof) + assert.Equal(t, encodedBlob1.BlobHeader.BlobCommitments.Length, uint(48)) + assert.Len(t, encodedBlob1.BlobHeader.QuorumInfos, 2) + assert.ElementsMatch(t, encodedBlob1.BlobHeader.QuorumInfos, []*core.BlobQuorumInfo{ + { + SecurityParam: core.SecurityParam{ + QuorumID: 0, + AdversaryThreshold: 80, + ConfirmationThreshold: 100, + }, + ChunkLength: 16, + }, + { + SecurityParam: core.SecurityParam{ + QuorumID: 1, + AdversaryThreshold: 70, + ConfirmationThreshold: 95, + }, + ChunkLength: 8, + }, + }) + + assert.Contains(t, batch.BlobHeaders, encodedBlob1.BlobHeader) + for _, bundles := range encodedBlob1.BundlesByOperator { + assert.Len(t, bundles, 2) + assert.Greater(t, len(bundles[0]), 0) + assert.Greater(t, len(bundles[1]), 0) + break + } + + assert.NotNil(t, encodedBlob2.BlobHeader) + assert.NotNil(t, encodedBlob2.BlobHeader.BlobCommitments) + assert.NotNil(t, encodedBlob2.BlobHeader.BlobCommitments.Commitment) + assert.NotNil(t, encodedBlob2.BlobHeader.BlobCommitments.LengthProof) + assert.Equal(t, encodedBlob2.BlobHeader.BlobCommitments.Length, uint(48)) + assert.Len(t, encodedBlob2.BlobHeader.QuorumInfos, 1) + assert.ElementsMatch(t, encodedBlob2.BlobHeader.QuorumInfos, []*core.BlobQuorumInfo{{ + SecurityParam: core.SecurityParam{ + QuorumID: 2, + AdversaryThreshold: 75, + ConfirmationThreshold: 100, + }, + ChunkLength: 8, + }}) + for _, bundles := range encodedBlob2.BundlesByOperator { + assert.Len(t, bundles, 1) + assert.Greater(t, len(bundles[core.QuorumID(2)]), 0) + break + } + assert.Len(t, batch.BlobHeaders, 2) + assert.Len(t, batch.BlobMetadata, 2) + assert.Contains(t, batch.BlobMetadata, metadata1) + assert.Contains(t, batch.BlobMetadata, metadata2) +} From 5b1fbadc4cadff9ccc597fbb522c267b2eced960 Mon Sep 17 00:00:00 2001 From: Ian Shim <100327837+ian-shim@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:04:07 -0700 Subject: [PATCH 4/4] Dispatcher method to disperse blobs to node via `StoreBlobs` endpoint (#648) --- disperser/batcher/grpc/dispatcher.go | 69 ++++++++++++++++++++++++++++ disperser/disperser.go | 1 + disperser/mock/dispatcher.go | 8 ++++ 3 files changed, 78 insertions(+) diff --git a/disperser/batcher/grpc/dispatcher.go b/disperser/batcher/grpc/dispatcher.go index 5807a6f40d..9c14a433b8 100644 --- a/disperser/batcher/grpc/dispatcher.go +++ b/disperser/batcher/grpc/dispatcher.go @@ -147,6 +147,55 @@ func (c *dispatcher) sendChunks(ctx context.Context, blobs []*core.BlobMessage, return sig, nil } +// SendBlobsToOperator sends blobs to an operator via the node's StoreBlobs endpoint +// It returns the signatures of the blobs sent to the operator in the same order as the blobs +// with nil values for blobs that were not attested by the operator +func (c *dispatcher) SendBlobsToOperator(ctx context.Context, blobs []*core.BlobMessage, batchHeader *core.BatchHeader, op *core.IndexedOperatorInfo) ([]*core.Signature, error) { + // TODO Add secure Grpc + + conn, err := grpc.Dial( + core.OperatorSocket(op.Socket).GetDispersalSocket(), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + c.logger.Warn("Disperser cannot connect to operator dispersal socket", "dispersal_socket", core.OperatorSocket(op.Socket).GetDispersalSocket(), "err", err) + return nil, err + } + defer conn.Close() + + gc := node.NewDispersalClient(conn) + ctx, cancel := context.WithTimeout(ctx, c.Timeout) + defer cancel() + start := time.Now() + request, totalSize, err := GetStoreBlobsRequest(blobs, batchHeader) + if err != nil { + return nil, err + } + c.logger.Debug("sending chunks to operator", "operator", op.Socket, "num blobs", len(blobs), "size", totalSize, "request message size", proto.Size(request), "request serialization time", time.Since(start)) + opt := grpc.MaxCallSendMsgSize(60 * 1024 * 1024 * 1024) + reply, err := gc.StoreBlobs(ctx, request, opt) + + if err != nil { + return nil, err + } + + signaturesInBytes := reply.GetSignatures() + signatures := make([]*core.Signature, len(signaturesInBytes)) + for _, sigBytes := range signaturesInBytes { + sig := sigBytes.GetValue() + if sig != nil { + point, err := new(core.Signature).Deserialize(sig) + if err != nil { + return nil, err + } + signatures = append(signatures, &core.Signature{G1Point: point}) + } else { + signatures = append(signatures, nil) + } + } + return signatures, nil +} + func GetStoreChunksRequest(blobMessages []*core.BlobMessage, batchHeader *core.BatchHeader) (*node.StoreChunksRequest, int64, error) { blobs := make([]*node.Blob, len(blobMessages)) totalSize := int64(0) @@ -167,6 +216,26 @@ func GetStoreChunksRequest(blobMessages []*core.BlobMessage, batchHeader *core.B return request, totalSize, nil } +func GetStoreBlobsRequest(blobMessages []*core.BlobMessage, batchHeader *core.BatchHeader) (*node.StoreBlobsRequest, int64, error) { + blobs := make([]*node.Blob, len(blobMessages)) + totalSize := int64(0) + for i, blob := range blobMessages { + var err error + blobs[i], err = getBlobMessage(blob) + if err != nil { + return nil, 0, err + } + totalSize += int64(blob.Bundles.Size()) + } + + request := &node.StoreBlobsRequest{ + Blobs: blobs, + ReferenceBlockNumber: uint32(batchHeader.ReferenceBlockNumber), + } + + return request, totalSize, nil +} + func getBlobMessage(blob *core.BlobMessage) (*node.Blob, error) { if blob.BlobHeader == nil { return nil, errors.New("blob header is nil") diff --git a/disperser/disperser.go b/disperser/disperser.go index 78d078e594..f9ea58782c 100644 --- a/disperser/disperser.go +++ b/disperser/disperser.go @@ -177,6 +177,7 @@ type BlobStore interface { type Dispatcher interface { DisperseBatch(context.Context, *core.IndexedOperatorState, []core.EncodedBlob, *core.BatchHeader) chan core.SigningMessage + SendBlobsToOperator(ctx context.Context, blobs []*core.BlobMessage, batchHeader *core.BatchHeader, op *core.IndexedOperatorInfo) ([]*core.Signature, error) } // GenerateReverseIndexKey returns the key used to store the blob key in the reverse index diff --git a/disperser/mock/dispatcher.go b/disperser/mock/dispatcher.go index 59d13f686e..8569e9779b 100644 --- a/disperser/mock/dispatcher.go +++ b/disperser/mock/dispatcher.go @@ -64,3 +64,11 @@ func (d *Dispatcher) DisperseBatch(ctx context.Context, state *core.IndexedOpera return update } + +func (d *Dispatcher) SendBlobsToOperator(ctx context.Context, blobs []*core.BlobMessage, batchHeader *core.BatchHeader, op *core.IndexedOperatorInfo) ([]*core.Signature, error) { + args := d.Called(ctx, blobs, batchHeader, op) + if args.Get(0) == nil { + return nil, args.Error(1) + } + return args.Get(0).([]*core.Signature), args.Error(1) +}