diff --git a/api/clients/accountant.go b/api/clients/accountant.go index 122ec3eea..375494166 100644 --- a/api/clients/accountant.go +++ b/api/clients/accountant.go @@ -13,17 +13,17 @@ import ( "github.com/Layr-Labs/eigenda/core/meterer" ) -var minNumBins uint32 = 3 var requiredQuorums = []uint8{0, 1} type Accountant interface { - AccountBlob(ctx context.Context, numSymbols uint64, quorums []uint8) (*commonpb.PaymentHeader, []byte, error) + AccountBlob(ctx context.Context, numSymbols uint64, quorums []uint8) (*commonpb.PaymentHeader, error) } var _ Accountant = &accountant{} type accountant struct { // on-chain states + accountID string reservation *core.ActiveReservation onDemand *core.OnDemandPayment reservationWindow uint32 @@ -36,8 +36,8 @@ type accountant struct { usageLock sync.Mutex cumulativePayment *big.Int - paymentSigner core.PaymentSigner - numBins uint32 + // number of bins in the circular accounting, restricted by minNumBins which is 3 + numBins uint32 } type BinRecord struct { @@ -45,7 +45,7 @@ type BinRecord struct { Usage uint64 } -func NewAccountant(reservation *core.ActiveReservation, onDemand *core.OnDemandPayment, reservationWindow uint32, pricePerSymbol uint32, minNumSymbols uint32, paymentSigner core.PaymentSigner, numBins uint32) *accountant { +func NewAccountant(accountID string, reservation *core.ActiveReservation, onDemand *core.OnDemandPayment, reservationWindow uint32, pricePerSymbol uint32, minNumSymbols uint32, numBins uint32) *accountant { //TODO: client storage; currently every instance starts fresh but on-chain or a small store makes more sense // Also client is currently responsible for supplying network params, we need to add RPC in order to be automatic // There's a subsequent PR that handles populating the accountant with on-chain state from the disperser @@ -54,6 +54,7 @@ func NewAccountant(reservation *core.ActiveReservation, onDemand *core.OnDemandP binRecords[i] = BinRecord{Index: uint32(i), Usage: 0} } a := accountant{ + accountID: accountID, reservation: reservation, onDemand: onDemand, reservationWindow: reservationWindow, @@ -61,8 +62,7 @@ func NewAccountant(reservation *core.ActiveReservation, onDemand *core.OnDemandP minNumSymbols: minNumSymbols, binRecords: binRecords, cumulativePayment: big.NewInt(0), - paymentSigner: paymentSigner, - numBins: max(numBins, minNumBins), + numBins: max(numBins, uint32(meterer.MinNumBins)), } // TODO: add a routine to refresh the on-chain state occasionally? return &a @@ -116,26 +116,20 @@ func (a *accountant) BlobPaymentInfo(ctx context.Context, numSymbols uint64, quo } // AccountBlob accountant provides and records payment information -func (a *accountant) AccountBlob(ctx context.Context, numSymbols uint64, quorums []uint8) (*commonpb.PaymentHeader, []byte, error) { +func (a *accountant) AccountBlob(ctx context.Context, numSymbols uint64, quorums []uint8) (*commonpb.PaymentHeader, error) { binIndex, cumulativePayment, err := a.BlobPaymentInfo(ctx, numSymbols, quorums) if err != nil { - return nil, nil, err + return nil, err } - accountID := a.paymentSigner.GetAccountID() pm := &core.PaymentMetadata{ - AccountID: accountID, + AccountID: a.accountID, BinIndex: binIndex, CumulativePayment: cumulativePayment, } protoPaymentHeader := pm.ConvertToProtoPaymentHeader() - signature, err := a.paymentSigner.SignBlobPayment(pm) - if err != nil { - return nil, nil, err - } - - return protoPaymentHeader, signature, nil + return protoPaymentHeader, nil } // TODO: PaymentCharged and SymbolsCharged copied from meterer, should be refactored diff --git a/api/clients/accountant_test.go b/api/clients/accountant_test.go index d40f75550..979c08792 100644 --- a/api/clients/accountant_test.go +++ b/api/clients/accountant_test.go @@ -9,7 +9,6 @@ import ( "time" "github.com/Layr-Labs/eigenda/core" - "github.com/Layr-Labs/eigenda/core/auth" "github.com/Layr-Labs/eigenda/core/meterer" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" @@ -34,9 +33,8 @@ func TestNewAccountant(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) - paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - assert.NoError(t, err) - accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) + accountId := hex.EncodeToString(privateKey1.D.Bytes()) + accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins) assert.NotNil(t, accountant) assert.Equal(t, reservation, accountant.reservation) @@ -65,15 +63,14 @@ func TestAccountBlob_Reservation(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) - paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - assert.NoError(t, err) - accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) + accountId := hex.EncodeToString(privateKey1.D.Bytes()) + accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins) ctx := context.Background() symbolLength := uint64(500) quorums := []uint8{0, 1} - header, _, err := accountant.AccountBlob(ctx, symbolLength, quorums) + header, err := accountant.AccountBlob(ctx, symbolLength, quorums) metadata := core.ConvertPaymentHeader(header) assert.NoError(t, err) @@ -83,7 +80,7 @@ func TestAccountBlob_Reservation(t *testing.T) { symbolLength = uint64(700) - header, _, err = accountant.AccountBlob(ctx, symbolLength, quorums) + header, err = accountant.AccountBlob(ctx, symbolLength, quorums) metadata = core.ConvertPaymentHeader(header) assert.NoError(t, err) @@ -92,7 +89,7 @@ func TestAccountBlob_Reservation(t *testing.T) { assert.Equal(t, isRotation([]uint64{1200, 0, 200}, mapRecordUsage(accountant.binRecords)), true) // Second call should use on-demand payment - header, _, err = accountant.AccountBlob(ctx, 300, quorums) + header, err = accountant.AccountBlob(ctx, 300, quorums) metadata = core.ConvertPaymentHeader(header) assert.NoError(t, err) @@ -117,15 +114,14 @@ func TestAccountBlob_OnDemand(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) - paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - assert.NoError(t, err) - accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) + accountId := hex.EncodeToString(privateKey1.D.Bytes()) + accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins) ctx := context.Background() numSymbols := uint64(1500) quorums := []uint8{0, 1} - header, _, err := accountant.AccountBlob(ctx, numSymbols, quorums) + header, err := accountant.AccountBlob(ctx, numSymbols, quorums) assert.NoError(t, err) metadata := core.ConvertPaymentHeader(header) @@ -147,15 +143,14 @@ func TestAccountBlob_InsufficientOnDemand(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) - paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - assert.NoError(t, err) - accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) + accountId := hex.EncodeToString(privateKey1.D.Bytes()) + accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins) ctx := context.Background() numSymbols := uint64(2000) quorums := []uint8{0, 1} - _, _, err = accountant.AccountBlob(ctx, numSymbols, quorums) + _, err = accountant.AccountBlob(ctx, numSymbols, quorums) assert.Contains(t, err.Error(), "neither reservation nor on-demand payment is available") } @@ -176,37 +171,36 @@ func TestAccountBlobCallSeries(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) - paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - assert.NoError(t, err) - accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) + accountId := hex.EncodeToString(privateKey1.D.Bytes()) + accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins) ctx := context.Background() quorums := []uint8{0, 1} now := time.Now().Unix() // First call: Use reservation - header, _, err := accountant.AccountBlob(ctx, 800, quorums) + header, err := accountant.AccountBlob(ctx, 800, quorums) metadata := core.ConvertPaymentHeader(header) assert.NoError(t, err) assert.Equal(t, meterer.GetBinIndex(uint64(now), reservationWindow), header.BinIndex) assert.Equal(t, big.NewInt(0), metadata.CumulativePayment) // Second call: Use remaining reservation + overflow - header, _, err = accountant.AccountBlob(ctx, 300, quorums) + header, err = accountant.AccountBlob(ctx, 300, quorums) metadata = core.ConvertPaymentHeader(header) assert.NoError(t, err) assert.Equal(t, meterer.GetBinIndex(uint64(now), reservationWindow), header.BinIndex) assert.Equal(t, big.NewInt(0), metadata.CumulativePayment) // Third call: Use on-demand - header, _, err = accountant.AccountBlob(ctx, 500, quorums) + header, err = accountant.AccountBlob(ctx, 500, quorums) metadata = core.ConvertPaymentHeader(header) assert.NoError(t, err) assert.Equal(t, uint32(0), header.BinIndex) assert.Equal(t, big.NewInt(500), metadata.CumulativePayment) // Fourth call: Insufficient on-demand - _, _, err = accountant.AccountBlob(ctx, 600, quorums) + _, err = accountant.AccountBlob(ctx, 600, quorums) assert.Error(t, err) assert.Contains(t, err.Error(), "neither reservation nor on-demand payment is available") } @@ -225,17 +219,17 @@ func TestAccountBlob_BinRotation(t *testing.T) { reservationWindow := uint32(1) // Set to 1 second for testing pricePerSymbol := uint32(1) minNumSymbols := uint32(100) + privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) - paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - assert.NoError(t, err) - accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) + accountId := hex.EncodeToString(privateKey1.D.Bytes()) + accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins) ctx := context.Background() quorums := []uint8{0, 1} // First call - _, _, err = accountant.AccountBlob(ctx, 800, quorums) + _, err = accountant.AccountBlob(ctx, 800, quorums) assert.NoError(t, err) assert.Equal(t, isRotation([]uint64{800, 0, 0}, mapRecordUsage(accountant.binRecords)), true) @@ -243,12 +237,12 @@ func TestAccountBlob_BinRotation(t *testing.T) { time.Sleep(1000 * time.Millisecond) // Second call - _, _, err = accountant.AccountBlob(ctx, 300, quorums) + _, err = accountant.AccountBlob(ctx, 300, quorums) assert.NoError(t, err) assert.Equal(t, isRotation([]uint64{800, 300, 0}, mapRecordUsage(accountant.binRecords)), true) // Third call - _, _, err = accountant.AccountBlob(ctx, 500, quorums) + _, err = accountant.AccountBlob(ctx, 500, quorums) assert.NoError(t, err) assert.Equal(t, isRotation([]uint64{800, 800, 0}, mapRecordUsage(accountant.binRecords)), true) } @@ -270,9 +264,8 @@ func TestConcurrentBinRotationAndAccountBlob(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) - paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - assert.NoError(t, err) - accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) + accountId := hex.EncodeToString(privateKey1.D.Bytes()) + accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins) ctx := context.Background() quorums := []uint8{0, 1} @@ -285,7 +278,7 @@ func TestConcurrentBinRotationAndAccountBlob(t *testing.T) { defer wg.Done() // for j := 0; j < 5; j++ { // fmt.Println("request ", i) - _, _, err := accountant.AccountBlob(ctx, 100, quorums) + _, err := accountant.AccountBlob(ctx, 100, quorums) assert.NoError(t, err) time.Sleep(500 * time.Millisecond) // } @@ -317,15 +310,15 @@ func TestAccountBlob_ReservationWithOneOverflow(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) - paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - assert.NoError(t, err) - accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) + accountId := hex.EncodeToString(privateKey1.D.Bytes()) + accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins) + ctx := context.Background() quorums := []uint8{0, 1} now := time.Now().Unix() // Okay reservation - header, _, err := accountant.AccountBlob(ctx, 800, quorums) + header, err := accountant.AccountBlob(ctx, 800, quorums) assert.NoError(t, err) assert.Equal(t, meterer.GetBinIndex(uint64(now), reservationWindow), header.BinIndex) metadata := core.ConvertPaymentHeader(header) @@ -333,14 +326,14 @@ func TestAccountBlob_ReservationWithOneOverflow(t *testing.T) { assert.Equal(t, isRotation([]uint64{800, 0, 0}, mapRecordUsage(accountant.binRecords)), true) // Second call: Allow one overflow - header, _, err = accountant.AccountBlob(ctx, 500, quorums) + header, err = accountant.AccountBlob(ctx, 500, quorums) assert.NoError(t, err) metadata = core.ConvertPaymentHeader(header) assert.Equal(t, big.NewInt(0), metadata.CumulativePayment) assert.Equal(t, isRotation([]uint64{1300, 0, 300}, mapRecordUsage(accountant.binRecords)), true) // Third call: Should use on-demand payment - header, _, err = accountant.AccountBlob(ctx, 200, quorums) + header, err = accountant.AccountBlob(ctx, 200, quorums) assert.NoError(t, err) assert.Equal(t, uint32(0), header.BinIndex) metadata = core.ConvertPaymentHeader(header) @@ -365,20 +358,19 @@ func TestAccountBlob_ReservationOverflowReset(t *testing.T) { privateKey1, err := crypto.GenerateKey() assert.NoError(t, err) - paymentSigner, err := auth.NewPaymentSigner(hex.EncodeToString(privateKey1.D.Bytes())) - assert.NoError(t, err) - accountant := NewAccountant(reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, paymentSigner, numBins) + accountId := hex.EncodeToString(privateKey1.D.Bytes()) + accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins) ctx := context.Background() quorums := []uint8{0, 1} // full reservation - _, _, err = accountant.AccountBlob(ctx, 1000, quorums) + _, err = accountant.AccountBlob(ctx, 1000, quorums) assert.NoError(t, err) assert.Equal(t, isRotation([]uint64{1000, 0, 0}, mapRecordUsage(accountant.binRecords)), true) // no overflow - header, _, err := accountant.AccountBlob(ctx, 500, quorums) + header, err := accountant.AccountBlob(ctx, 500, quorums) assert.NoError(t, err) assert.Equal(t, isRotation([]uint64{1000, 0, 0}, mapRecordUsage(accountant.binRecords)), true) metadata := core.ConvertPaymentHeader(header) @@ -388,7 +380,7 @@ func TestAccountBlob_ReservationOverflowReset(t *testing.T) { time.Sleep(time.Duration(reservationWindow) * time.Second) // Third call: Should use new bin and allow overflow again - _, _, err = accountant.AccountBlob(ctx, 500, quorums) + _, err = accountant.AccountBlob(ctx, 500, quorums) assert.NoError(t, err) assert.Equal(t, isRotation([]uint64{1000, 500, 0}, mapRecordUsage(accountant.binRecords)), true) } diff --git a/api/grpc/disperser/v2/disperser_v2.pb.go b/api/grpc/disperser/v2/disperser_v2.pb.go index 3527f50b4..6e1db037b 100644 --- a/api/grpc/disperser/v2/disperser_v2.pb.go +++ b/api/grpc/disperser/v2/disperser_v2.pb.go @@ -423,6 +423,149 @@ func (x *BlobCommitmentReply) GetBlobCommitment() *common.BlobCommitment { return nil } +// GetPaymentStateRequest contains parameters to query the payment state of an account. +type GetPaymentStateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AccountId string `protobuf:"bytes,1,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` + // Signature over the account ID + // TODO: sign over a bin index or a nonce to mitigate signature replay attacks + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (x *GetPaymentStateRequest) Reset() { + *x = GetPaymentStateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPaymentStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPaymentStateRequest) ProtoMessage() {} + +func (x *GetPaymentStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[6] + 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 GetPaymentStateRequest.ProtoReflect.Descriptor instead. +func (*GetPaymentStateRequest) Descriptor() ([]byte, []int) { + return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{6} +} + +func (x *GetPaymentStateRequest) GetAccountId() string { + if x != nil { + return x.AccountId + } + return "" +} + +func (x *GetPaymentStateRequest) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +// GetPaymentStateReply contains the payment state of an account. +type GetPaymentStateReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // global payment vault parameters + PaymentGlobalParams *PaymentGlobalParams `protobuf:"bytes,1,opt,name=payment_global_params,json=paymentGlobalParams,proto3" json:"payment_global_params,omitempty"` + // off-chain account reservation usage records + BinRecords []*BinRecord `protobuf:"bytes,2,rep,name=bin_records,json=binRecords,proto3" json:"bin_records,omitempty"` + // on-chain account reservation setting + Reservation *Reservation `protobuf:"bytes,3,opt,name=reservation,proto3" json:"reservation,omitempty"` + // off-chain on-demand payment usage + CumulativePayment []byte `protobuf:"bytes,4,opt,name=cumulative_payment,json=cumulativePayment,proto3" json:"cumulative_payment,omitempty"` + // on-chain on-demand payment deposited + OnchainCumulativePayment []byte `protobuf:"bytes,5,opt,name=onchain_cumulative_payment,json=onchainCumulativePayment,proto3" json:"onchain_cumulative_payment,omitempty"` +} + +func (x *GetPaymentStateReply) Reset() { + *x = GetPaymentStateReply{} + if protoimpl.UnsafeEnabled { + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPaymentStateReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPaymentStateReply) ProtoMessage() {} + +func (x *GetPaymentStateReply) ProtoReflect() protoreflect.Message { + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[7] + 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 GetPaymentStateReply.ProtoReflect.Descriptor instead. +func (*GetPaymentStateReply) Descriptor() ([]byte, []int) { + return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{7} +} + +func (x *GetPaymentStateReply) GetPaymentGlobalParams() *PaymentGlobalParams { + if x != nil { + return x.PaymentGlobalParams + } + return nil +} + +func (x *GetPaymentStateReply) GetBinRecords() []*BinRecord { + if x != nil { + return x.BinRecords + } + return nil +} + +func (x *GetPaymentStateReply) GetReservation() *Reservation { + if x != nil { + return x.Reservation + } + return nil +} + +func (x *GetPaymentStateReply) GetCumulativePayment() []byte { + if x != nil { + return x.CumulativePayment + } + return nil +} + +func (x *GetPaymentStateReply) GetOnchainCumulativePayment() []byte { + if x != nil { + return x.OnchainCumulativePayment + } + return nil +} + // SignedBatch is a batch of blobs with a signature. type SignedBatch struct { state protoimpl.MessageState @@ -438,7 +581,7 @@ type SignedBatch struct { func (x *SignedBatch) Reset() { *x = SignedBatch{} if protoimpl.UnsafeEnabled { - mi := &file_disperser_v2_disperser_v2_proto_msgTypes[6] + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -451,7 +594,7 @@ func (x *SignedBatch) String() string { func (*SignedBatch) ProtoMessage() {} func (x *SignedBatch) ProtoReflect() protoreflect.Message { - mi := &file_disperser_v2_disperser_v2_proto_msgTypes[6] + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -464,7 +607,7 @@ func (x *SignedBatch) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedBatch.ProtoReflect.Descriptor instead. func (*SignedBatch) Descriptor() ([]byte, []int) { - return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{6} + return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{8} } func (x *SignedBatch) GetHeader() *v2.BatchHeader { @@ -497,7 +640,7 @@ type BlobVerificationInfo struct { func (x *BlobVerificationInfo) Reset() { *x = BlobVerificationInfo{} if protoimpl.UnsafeEnabled { - mi := &file_disperser_v2_disperser_v2_proto_msgTypes[7] + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -510,7 +653,7 @@ func (x *BlobVerificationInfo) String() string { func (*BlobVerificationInfo) ProtoMessage() {} func (x *BlobVerificationInfo) ProtoReflect() protoreflect.Message { - mi := &file_disperser_v2_disperser_v2_proto_msgTypes[7] + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -523,7 +666,7 @@ func (x *BlobVerificationInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use BlobVerificationInfo.ProtoReflect.Descriptor instead. func (*BlobVerificationInfo) Descriptor() ([]byte, []int) { - return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{7} + return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{9} } func (x *BlobVerificationInfo) GetBlobCertificate() *v2.BlobCertificate { @@ -567,7 +710,7 @@ type Attestation struct { func (x *Attestation) Reset() { *x = Attestation{} if protoimpl.UnsafeEnabled { - mi := &file_disperser_v2_disperser_v2_proto_msgTypes[8] + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -580,7 +723,7 @@ func (x *Attestation) String() string { func (*Attestation) ProtoMessage() {} func (x *Attestation) ProtoReflect() protoreflect.Message { - mi := &file_disperser_v2_disperser_v2_proto_msgTypes[8] + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -593,7 +736,7 @@ func (x *Attestation) ProtoReflect() protoreflect.Message { // Deprecated: Use Attestation.ProtoReflect.Descriptor instead. func (*Attestation) Descriptor() ([]byte, []int) { - return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{8} + return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{10} } func (x *Attestation) GetNonSignerPubkeys() [][]byte { @@ -631,6 +774,221 @@ func (x *Attestation) GetQuorumNumbers() []uint32 { return nil } +type PaymentGlobalParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GlobalSymbolsPerSecond uint64 `protobuf:"varint,1,opt,name=global_symbols_per_second,json=globalSymbolsPerSecond,proto3" json:"global_symbols_per_second,omitempty"` + MinNumSymbols uint32 `protobuf:"varint,2,opt,name=min_num_symbols,json=minNumSymbols,proto3" json:"min_num_symbols,omitempty"` + PricePerSymbol uint32 `protobuf:"varint,3,opt,name=price_per_symbol,json=pricePerSymbol,proto3" json:"price_per_symbol,omitempty"` + ReservationWindow uint32 `protobuf:"varint,4,opt,name=reservation_window,json=reservationWindow,proto3" json:"reservation_window,omitempty"` + OnDemandQuorumNumbers []uint32 `protobuf:"varint,5,rep,packed,name=on_demand_quorum_numbers,json=onDemandQuorumNumbers,proto3" json:"on_demand_quorum_numbers,omitempty"` +} + +func (x *PaymentGlobalParams) Reset() { + *x = PaymentGlobalParams{} + if protoimpl.UnsafeEnabled { + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PaymentGlobalParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PaymentGlobalParams) ProtoMessage() {} + +func (x *PaymentGlobalParams) ProtoReflect() protoreflect.Message { + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PaymentGlobalParams.ProtoReflect.Descriptor instead. +func (*PaymentGlobalParams) Descriptor() ([]byte, []int) { + return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{11} +} + +func (x *PaymentGlobalParams) GetGlobalSymbolsPerSecond() uint64 { + if x != nil { + return x.GlobalSymbolsPerSecond + } + return 0 +} + +func (x *PaymentGlobalParams) GetMinNumSymbols() uint32 { + if x != nil { + return x.MinNumSymbols + } + return 0 +} + +func (x *PaymentGlobalParams) GetPricePerSymbol() uint32 { + if x != nil { + return x.PricePerSymbol + } + return 0 +} + +func (x *PaymentGlobalParams) GetReservationWindow() uint32 { + if x != nil { + return x.ReservationWindow + } + return 0 +} + +func (x *PaymentGlobalParams) GetOnDemandQuorumNumbers() []uint32 { + if x != nil { + return x.OnDemandQuorumNumbers + } + return nil +} + +type Reservation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SymbolsPerSecond uint64 `protobuf:"varint,1,opt,name=symbols_per_second,json=symbolsPerSecond,proto3" json:"symbols_per_second,omitempty"` + StartTimestamp uint32 `protobuf:"varint,2,opt,name=start_timestamp,json=startTimestamp,proto3" json:"start_timestamp,omitempty"` + EndTimestamp uint32 `protobuf:"varint,3,opt,name=end_timestamp,json=endTimestamp,proto3" json:"end_timestamp,omitempty"` + QuorumNumbers []uint32 `protobuf:"varint,4,rep,packed,name=quorum_numbers,json=quorumNumbers,proto3" json:"quorum_numbers,omitempty"` + QuorumSplit []uint32 `protobuf:"varint,5,rep,packed,name=quorum_split,json=quorumSplit,proto3" json:"quorum_split,omitempty"` +} + +func (x *Reservation) Reset() { + *x = Reservation{} + if protoimpl.UnsafeEnabled { + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Reservation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Reservation) ProtoMessage() {} + +func (x *Reservation) ProtoReflect() protoreflect.Message { + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[12] + 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 Reservation.ProtoReflect.Descriptor instead. +func (*Reservation) Descriptor() ([]byte, []int) { + return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{12} +} + +func (x *Reservation) GetSymbolsPerSecond() uint64 { + if x != nil { + return x.SymbolsPerSecond + } + return 0 +} + +func (x *Reservation) GetStartTimestamp() uint32 { + if x != nil { + return x.StartTimestamp + } + return 0 +} + +func (x *Reservation) GetEndTimestamp() uint32 { + if x != nil { + return x.EndTimestamp + } + return 0 +} + +func (x *Reservation) GetQuorumNumbers() []uint32 { + if x != nil { + return x.QuorumNumbers + } + return nil +} + +func (x *Reservation) GetQuorumSplit() []uint32 { + if x != nil { + return x.QuorumSplit + } + return nil +} + +// BinRecord is the usage record of an account in a bin. The API should return the active bin +// record and the subsequent two records that contains potential overflows. +type BinRecord struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Index uint32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + Usage uint64 `protobuf:"varint,2,opt,name=usage,proto3" json:"usage,omitempty"` +} + +func (x *BinRecord) Reset() { + *x = BinRecord{} + if protoimpl.UnsafeEnabled { + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BinRecord) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BinRecord) ProtoMessage() {} + +func (x *BinRecord) ProtoReflect() protoreflect.Message { + mi := &file_disperser_v2_disperser_v2_proto_msgTypes[13] + 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 BinRecord.ProtoReflect.Descriptor instead. +func (*BinRecord) Descriptor() ([]byte, []int) { + return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{13} +} + +func (x *BinRecord) GetIndex() uint32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *BinRecord) GetUsage() uint64 { + if x != nil { + return x.Usage + } + return 0 +} + var File_disperser_v2_disperser_v2_proto protoreflect.FileDescriptor var file_disperser_v2_disperser_v2_proto_rawDesc = []byte{ @@ -677,64 +1035,131 @@ var file_disperser_v2_disperser_v2_proto_rawDesc = []byte{ 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x62, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x7a, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, - 0x74, 0x63, 0x68, 0x12, 0x2e, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, - 0x42, 0x61, 0x74, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, - 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0xa5, 0x01, 0x0a, 0x14, 0x42, 0x6c, 0x6f, 0x62, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x45, 0x0a, 0x10, 0x62, 0x6c, 0x6f, - 0x62, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, - 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, - 0x0f, 0x62, 0x6c, 0x6f, 0x62, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, - 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, - 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, - 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, - 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0xb0, 0x01, 0x0a, 0x0b, 0x41, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x6f, 0x6e, 0x5f, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0c, 0x52, 0x10, 0x6e, 0x6f, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x50, - 0x75, 0x62, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x70, 0x6b, 0x5f, 0x67, 0x32, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x61, 0x70, 0x6b, 0x47, 0x32, 0x12, 0x1f, 0x0a, - 0x0b, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x61, 0x70, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0c, 0x52, 0x0a, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x41, 0x70, 0x6b, 0x73, 0x12, 0x14, - 0x0a, 0x05, 0x73, 0x69, 0x67, 0x6d, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, - 0x69, 0x67, 0x6d, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x6e, - 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x71, 0x75, - 0x6f, 0x72, 0x75, 0x6d, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2a, 0x6a, 0x0a, 0x0a, 0x42, - 0x6c, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, - 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x51, 0x55, 0x45, 0x55, 0x45, 0x44, - 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x45, 0x44, 0x10, 0x02, 0x12, - 0x0d, 0x0a, 0x09, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0a, - 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x1b, 0x0a, 0x17, 0x49, 0x4e, - 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x49, 0x47, 0x4e, 0x41, - 0x54, 0x55, 0x52, 0x45, 0x53, 0x10, 0x05, 0x32, 0x93, 0x02, 0x0a, 0x09, 0x44, 0x69, 0x73, 0x70, - 0x65, 0x72, 0x73, 0x65, 0x72, 0x12, 0x54, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, - 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x21, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, - 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, - 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, - 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, - 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0d, 0x47, - 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x2e, 0x64, - 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, - 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, - 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x5d, - 0x0a, 0x11, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, - 0x65, 0x6e, 0x74, 0x12, 0x23, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, - 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, - 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x34, 0x5a, - 0x32, 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, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, - 0x2f, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x55, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1c, 0x0a, + 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xd1, 0x02, 0x0a, 0x14, + 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x12, 0x55, 0x0a, 0x15, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, + 0x76, 0x32, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x13, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x62, + 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, + 0x42, 0x69, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x0a, 0x62, 0x69, 0x6e, 0x52, 0x65, + 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, + 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, + 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x3c, 0x0a, 0x1a, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x63, 0x75, 0x6d, + 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x18, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x75, + 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x22, + 0x7a, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x2e, + 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x3b, + 0x0a, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, + 0x76, 0x32, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, + 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa5, 0x01, 0x0a, 0x14, + 0x42, 0x6c, 0x6f, 0x62, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x45, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x63, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x43, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x62, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 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, 0x27, 0x0a, 0x0f, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x22, 0xb0, 0x01, 0x0a, 0x0b, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x6f, 0x6e, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x65, + 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, + 0x10, 0x6e, 0x6f, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, + 0x73, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x70, 0x6b, 0x5f, 0x67, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x05, 0x61, 0x70, 0x6b, 0x47, 0x32, 0x12, 0x1f, 0x0a, 0x0b, 0x71, 0x75, 0x6f, 0x72, + 0x75, 0x6d, 0x5f, 0x61, 0x70, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x71, + 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x41, 0x70, 0x6b, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x69, 0x67, + 0x6d, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x69, 0x67, 0x6d, 0x61, 0x12, + 0x25, 0x0a, 0x0e, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0x8a, 0x02, 0x0a, 0x13, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x39, + 0x0a, 0x19, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, + 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x16, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, + 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, + 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x4e, 0x75, 0x6d, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, + 0x73, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x73, + 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x70, 0x72, 0x69, + 0x63, 0x65, 0x50, 0x65, 0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x2d, 0x0a, 0x12, 0x72, + 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x37, 0x0a, 0x18, 0x6f, 0x6e, + 0x5f, 0x64, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x15, 0x6f, 0x6e, + 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x51, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x73, 0x22, 0xd3, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x5f, 0x70, + 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x10, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, + 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0c, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x25, 0x0a, 0x0e, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, + 0x5f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0b, 0x71, 0x75, + 0x6f, 0x72, 0x75, 0x6d, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x22, 0x37, 0x0a, 0x09, 0x42, 0x69, 0x6e, + 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x14, 0x0a, 0x05, + 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x75, 0x73, 0x61, + 0x67, 0x65, 0x2a, 0x6a, 0x0a, 0x0a, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, + 0x06, 0x51, 0x55, 0x45, 0x55, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x4e, 0x43, + 0x4f, 0x44, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, + 0x04, 0x12, 0x1b, 0x0a, 0x17, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, + 0x54, 0x5f, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, 0x45, 0x53, 0x10, 0x05, 0x32, 0xf2, + 0x02, 0x0a, 0x09, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x12, 0x54, 0x0a, 0x0c, + 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x21, 0x2e, 0x64, + 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x69, 0x73, 0x70, + 0x65, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x44, + 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x1f, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, + 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, + 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, + 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x2e, 0x64, 0x69, 0x73, + 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x21, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x42, + 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, + 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, + 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x22, 0x00, 0x42, 0x34, 0x5a, 0x32, 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, 0x64, 0x69, 0x73, + 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -750,44 +1175,54 @@ func file_disperser_v2_disperser_v2_proto_rawDescGZIP() []byte { } var file_disperser_v2_disperser_v2_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_disperser_v2_disperser_v2_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_disperser_v2_disperser_v2_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_disperser_v2_disperser_v2_proto_goTypes = []interface{}{ - (BlobStatus)(0), // 0: disperser.v2.BlobStatus - (*DisperseBlobRequest)(nil), // 1: disperser.v2.DisperseBlobRequest - (*DisperseBlobReply)(nil), // 2: disperser.v2.DisperseBlobReply - (*BlobStatusRequest)(nil), // 3: disperser.v2.BlobStatusRequest - (*BlobStatusReply)(nil), // 4: disperser.v2.BlobStatusReply - (*BlobCommitmentRequest)(nil), // 5: disperser.v2.BlobCommitmentRequest - (*BlobCommitmentReply)(nil), // 6: disperser.v2.BlobCommitmentReply - (*SignedBatch)(nil), // 7: disperser.v2.SignedBatch - (*BlobVerificationInfo)(nil), // 8: disperser.v2.BlobVerificationInfo - (*Attestation)(nil), // 9: disperser.v2.Attestation - (*v2.BlobHeader)(nil), // 10: common.v2.BlobHeader - (*common.BlobCommitment)(nil), // 11: common.BlobCommitment - (*v2.BatchHeader)(nil), // 12: common.v2.BatchHeader - (*v2.BlobCertificate)(nil), // 13: common.v2.BlobCertificate + (BlobStatus)(0), // 0: disperser.v2.BlobStatus + (*DisperseBlobRequest)(nil), // 1: disperser.v2.DisperseBlobRequest + (*DisperseBlobReply)(nil), // 2: disperser.v2.DisperseBlobReply + (*BlobStatusRequest)(nil), // 3: disperser.v2.BlobStatusRequest + (*BlobStatusReply)(nil), // 4: disperser.v2.BlobStatusReply + (*BlobCommitmentRequest)(nil), // 5: disperser.v2.BlobCommitmentRequest + (*BlobCommitmentReply)(nil), // 6: disperser.v2.BlobCommitmentReply + (*GetPaymentStateRequest)(nil), // 7: disperser.v2.GetPaymentStateRequest + (*GetPaymentStateReply)(nil), // 8: disperser.v2.GetPaymentStateReply + (*SignedBatch)(nil), // 9: disperser.v2.SignedBatch + (*BlobVerificationInfo)(nil), // 10: disperser.v2.BlobVerificationInfo + (*Attestation)(nil), // 11: disperser.v2.Attestation + (*PaymentGlobalParams)(nil), // 12: disperser.v2.PaymentGlobalParams + (*Reservation)(nil), // 13: disperser.v2.Reservation + (*BinRecord)(nil), // 14: disperser.v2.BinRecord + (*v2.BlobHeader)(nil), // 15: common.v2.BlobHeader + (*common.BlobCommitment)(nil), // 16: common.BlobCommitment + (*v2.BatchHeader)(nil), // 17: common.v2.BatchHeader + (*v2.BlobCertificate)(nil), // 18: common.v2.BlobCertificate } var file_disperser_v2_disperser_v2_proto_depIdxs = []int32{ - 10, // 0: disperser.v2.DisperseBlobRequest.blob_header:type_name -> common.v2.BlobHeader + 15, // 0: disperser.v2.DisperseBlobRequest.blob_header:type_name -> common.v2.BlobHeader 0, // 1: disperser.v2.DisperseBlobReply.result:type_name -> disperser.v2.BlobStatus 0, // 2: disperser.v2.BlobStatusReply.status:type_name -> disperser.v2.BlobStatus - 7, // 3: disperser.v2.BlobStatusReply.signed_batch:type_name -> disperser.v2.SignedBatch - 8, // 4: disperser.v2.BlobStatusReply.blob_verification_info:type_name -> disperser.v2.BlobVerificationInfo - 11, // 5: disperser.v2.BlobCommitmentReply.blob_commitment:type_name -> common.BlobCommitment - 12, // 6: disperser.v2.SignedBatch.header:type_name -> common.v2.BatchHeader - 9, // 7: disperser.v2.SignedBatch.attestation:type_name -> disperser.v2.Attestation - 13, // 8: disperser.v2.BlobVerificationInfo.blob_certificate:type_name -> common.v2.BlobCertificate - 1, // 9: disperser.v2.Disperser.DisperseBlob:input_type -> disperser.v2.DisperseBlobRequest - 3, // 10: disperser.v2.Disperser.GetBlobStatus:input_type -> disperser.v2.BlobStatusRequest - 5, // 11: disperser.v2.Disperser.GetBlobCommitment:input_type -> disperser.v2.BlobCommitmentRequest - 2, // 12: disperser.v2.Disperser.DisperseBlob:output_type -> disperser.v2.DisperseBlobReply - 4, // 13: disperser.v2.Disperser.GetBlobStatus:output_type -> disperser.v2.BlobStatusReply - 6, // 14: disperser.v2.Disperser.GetBlobCommitment:output_type -> disperser.v2.BlobCommitmentReply - 12, // [12:15] is the sub-list for method output_type - 9, // [9:12] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 9, // 3: disperser.v2.BlobStatusReply.signed_batch:type_name -> disperser.v2.SignedBatch + 10, // 4: disperser.v2.BlobStatusReply.blob_verification_info:type_name -> disperser.v2.BlobVerificationInfo + 16, // 5: disperser.v2.BlobCommitmentReply.blob_commitment:type_name -> common.BlobCommitment + 12, // 6: disperser.v2.GetPaymentStateReply.payment_global_params:type_name -> disperser.v2.PaymentGlobalParams + 14, // 7: disperser.v2.GetPaymentStateReply.bin_records:type_name -> disperser.v2.BinRecord + 13, // 8: disperser.v2.GetPaymentStateReply.reservation:type_name -> disperser.v2.Reservation + 17, // 9: disperser.v2.SignedBatch.header:type_name -> common.v2.BatchHeader + 11, // 10: disperser.v2.SignedBatch.attestation:type_name -> disperser.v2.Attestation + 18, // 11: disperser.v2.BlobVerificationInfo.blob_certificate:type_name -> common.v2.BlobCertificate + 1, // 12: disperser.v2.Disperser.DisperseBlob:input_type -> disperser.v2.DisperseBlobRequest + 3, // 13: disperser.v2.Disperser.GetBlobStatus:input_type -> disperser.v2.BlobStatusRequest + 5, // 14: disperser.v2.Disperser.GetBlobCommitment:input_type -> disperser.v2.BlobCommitmentRequest + 7, // 15: disperser.v2.Disperser.GetPaymentState:input_type -> disperser.v2.GetPaymentStateRequest + 2, // 16: disperser.v2.Disperser.DisperseBlob:output_type -> disperser.v2.DisperseBlobReply + 4, // 17: disperser.v2.Disperser.GetBlobStatus:output_type -> disperser.v2.BlobStatusReply + 6, // 18: disperser.v2.Disperser.GetBlobCommitment:output_type -> disperser.v2.BlobCommitmentReply + 8, // 19: disperser.v2.Disperser.GetPaymentState:output_type -> disperser.v2.GetPaymentStateReply + 16, // [16:20] is the sub-list for method output_type + 12, // [12:16] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_disperser_v2_disperser_v2_proto_init() } @@ -869,7 +1304,7 @@ func file_disperser_v2_disperser_v2_proto_init() { } } file_disperser_v2_disperser_v2_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignedBatch); i { + switch v := v.(*GetPaymentStateRequest); i { case 0: return &v.state case 1: @@ -881,7 +1316,7 @@ func file_disperser_v2_disperser_v2_proto_init() { } } file_disperser_v2_disperser_v2_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlobVerificationInfo); i { + switch v := v.(*GetPaymentStateReply); i { case 0: return &v.state case 1: @@ -893,6 +1328,30 @@ func file_disperser_v2_disperser_v2_proto_init() { } } file_disperser_v2_disperser_v2_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SignedBatch); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_disperser_v2_disperser_v2_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlobVerificationInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_disperser_v2_disperser_v2_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Attestation); i { case 0: return &v.state @@ -904,6 +1363,42 @@ func file_disperser_v2_disperser_v2_proto_init() { return nil } } + file_disperser_v2_disperser_v2_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PaymentGlobalParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_disperser_v2_disperser_v2_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Reservation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_disperser_v2_disperser_v2_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BinRecord); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -911,7 +1406,7 @@ func file_disperser_v2_disperser_v2_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_disperser_v2_disperser_v2_proto_rawDesc, NumEnums: 1, - NumMessages: 9, + NumMessages: 14, NumExtensions: 0, NumServices: 1, }, diff --git a/api/grpc/disperser/v2/disperser_v2_grpc.pb.go b/api/grpc/disperser/v2/disperser_v2_grpc.pb.go index d84637329..bd93c2ea2 100644 --- a/api/grpc/disperser/v2/disperser_v2_grpc.pb.go +++ b/api/grpc/disperser/v2/disperser_v2_grpc.pb.go @@ -22,6 +22,7 @@ const ( Disperser_DisperseBlob_FullMethodName = "/disperser.v2.Disperser/DisperseBlob" Disperser_GetBlobStatus_FullMethodName = "/disperser.v2.Disperser/GetBlobStatus" Disperser_GetBlobCommitment_FullMethodName = "/disperser.v2.Disperser/GetBlobCommitment" + Disperser_GetPaymentState_FullMethodName = "/disperser.v2.Disperser/GetPaymentState" ) // DisperserClient is the client API for Disperser service. @@ -37,6 +38,8 @@ type DisperserClient interface { GetBlobStatus(ctx context.Context, in *BlobStatusRequest, opts ...grpc.CallOption) (*BlobStatusReply, error) // GetBlobCommitment is a utility method that calculates commitment for a blob payload. GetBlobCommitment(ctx context.Context, in *BlobCommitmentRequest, opts ...grpc.CallOption) (*BlobCommitmentReply, error) + // GetPaymentState is a utility method to get the payment state of a given account. + GetPaymentState(ctx context.Context, in *GetPaymentStateRequest, opts ...grpc.CallOption) (*GetPaymentStateReply, error) } type disperserClient struct { @@ -74,6 +77,15 @@ func (c *disperserClient) GetBlobCommitment(ctx context.Context, in *BlobCommitm return out, nil } +func (c *disperserClient) GetPaymentState(ctx context.Context, in *GetPaymentStateRequest, opts ...grpc.CallOption) (*GetPaymentStateReply, error) { + out := new(GetPaymentStateReply) + err := c.cc.Invoke(ctx, Disperser_GetPaymentState_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // DisperserServer is the server API for Disperser service. // All implementations must embed UnimplementedDisperserServer // for forward compatibility @@ -87,6 +99,8 @@ type DisperserServer interface { GetBlobStatus(context.Context, *BlobStatusRequest) (*BlobStatusReply, error) // GetBlobCommitment is a utility method that calculates commitment for a blob payload. GetBlobCommitment(context.Context, *BlobCommitmentRequest) (*BlobCommitmentReply, error) + // GetPaymentState is a utility method to get the payment state of a given account. + GetPaymentState(context.Context, *GetPaymentStateRequest) (*GetPaymentStateReply, error) mustEmbedUnimplementedDisperserServer() } @@ -103,6 +117,9 @@ func (UnimplementedDisperserServer) GetBlobStatus(context.Context, *BlobStatusRe func (UnimplementedDisperserServer) GetBlobCommitment(context.Context, *BlobCommitmentRequest) (*BlobCommitmentReply, error) { return nil, status.Errorf(codes.Unimplemented, "method GetBlobCommitment not implemented") } +func (UnimplementedDisperserServer) GetPaymentState(context.Context, *GetPaymentStateRequest) (*GetPaymentStateReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentState not implemented") +} func (UnimplementedDisperserServer) mustEmbedUnimplementedDisperserServer() {} // UnsafeDisperserServer may be embedded to opt out of forward compatibility for this service. @@ -170,6 +187,24 @@ func _Disperser_GetBlobCommitment_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _Disperser_GetPaymentState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPaymentStateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DisperserServer).GetPaymentState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Disperser_GetPaymentState_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DisperserServer).GetPaymentState(ctx, req.(*GetPaymentStateRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Disperser_ServiceDesc is the grpc.ServiceDesc for Disperser service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -189,6 +224,10 @@ var Disperser_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetBlobCommitment", Handler: _Disperser_GetBlobCommitment_Handler, }, + { + MethodName: "GetPaymentState", + Handler: _Disperser_GetPaymentState_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "disperser/v2/disperser_v2.proto", diff --git a/api/proto/disperser/v2/disperser_v2.proto b/api/proto/disperser/v2/disperser_v2.proto index c6e4388e1..4107023bc 100644 --- a/api/proto/disperser/v2/disperser_v2.proto +++ b/api/proto/disperser/v2/disperser_v2.proto @@ -19,6 +19,9 @@ service Disperser { // GetBlobCommitment is a utility method that calculates commitment for a blob payload. rpc GetBlobCommitment(BlobCommitmentRequest) returns (BlobCommitmentReply) {} + + // GetPaymentState is a utility method to get the payment state of a given account. + rpc GetPaymentState(GetPaymentStateRequest) returns (GetPaymentStateReply) {} } // Requests and Replys @@ -63,6 +66,28 @@ message BlobCommitmentReply { common.BlobCommitment blob_commitment = 1; } +// GetPaymentStateRequest contains parameters to query the payment state of an account. +message GetPaymentStateRequest { + string account_id = 1; + // Signature over the account ID + // TODO: sign over a bin index or a nonce to mitigate signature replay attacks + bytes signature = 2; +} + +// GetPaymentStateReply contains the payment state of an account. +message GetPaymentStateReply { + // global payment vault parameters + PaymentGlobalParams payment_global_params = 1; + // off-chain account reservation usage records + repeated BinRecord bin_records = 2; + // on-chain account reservation setting + Reservation reservation = 3; + // off-chain on-demand payment usage + bytes cumulative_payment = 4; + // on-chain on-demand payment deposited + bytes onchain_cumulative_payment = 5; +} + // Data Types // BlobStatus represents the status of a blob. @@ -125,3 +150,26 @@ message Attestation { // Relevant quorum numbers for the attestation repeated uint32 quorum_numbers = 5; } + +message PaymentGlobalParams { + uint64 global_symbols_per_second = 1; + uint32 min_num_symbols = 2; + uint32 price_per_symbol = 3; + uint32 reservation_window = 4; + repeated uint32 on_demand_quorum_numbers = 5; +} + +message Reservation { + uint64 symbols_per_second = 1; + uint32 start_timestamp = 2; + uint32 end_timestamp = 3; + repeated uint32 quorum_numbers = 4; + repeated uint32 quorum_split = 5; +} + +// BinRecord is the usage record of an account in a bin. The API should return the active bin +// record and the subsequent two records that contains potential overflows. +message BinRecord { + uint32 index = 1; + uint64 usage = 2; +} diff --git a/core/auth/payment_signer.go b/core/auth/payment_signer.go deleted file mode 100644 index b22283040..000000000 --- a/core/auth/payment_signer.go +++ /dev/null @@ -1,99 +0,0 @@ -package auth - -import ( - "crypto/ecdsa" - "fmt" - - "github.com/Layr-Labs/eigenda/core" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -type paymentSigner struct { - PrivateKey *ecdsa.PrivateKey -} - -var _ core.PaymentSigner = &paymentSigner{} - -func NewPaymentSigner(privateKeyHex string) (*paymentSigner, error) { - if len(privateKeyHex) == 0 { - return nil, fmt.Errorf("private key cannot be empty") - } - privateKeyBytes := common.FromHex(privateKeyHex) - privateKey, err := crypto.ToECDSA(privateKeyBytes) - if err != nil { - return nil, fmt.Errorf("failed to convert hex to ECDSA private key: %w", err) - } - - return &paymentSigner{ - PrivateKey: privateKey, - }, nil -} - -// SignBlobPayment signs the payment header and returns the signature -func (s *paymentSigner) SignBlobPayment(pm *core.PaymentMetadata) ([]byte, error) { - hash, err := pm.Hash() - if err != nil { - return nil, fmt.Errorf("failed to hash payment header: %w", err) - } - - sig, err := crypto.Sign(hash[:], s.PrivateKey) - if err != nil { - return nil, fmt.Errorf("failed to sign hash: %w", err) - } - - return sig, nil -} - -type NoopPaymentSigner struct{} - -func NewNoopPaymentSigner() *NoopPaymentSigner { - return &NoopPaymentSigner{} -} - -func (s *NoopPaymentSigner) SignBlobPayment(header *core.PaymentMetadata) ([]byte, error) { - return nil, fmt.Errorf("noop signer cannot sign blob payment header") -} - -func (s *NoopPaymentSigner) GetAccountID() string { - return "" -} - -// VerifyPaymentSignature verifies the signature against the payment metadata -func VerifyPaymentSignature(paymentHeader *core.PaymentMetadata, paymentSignature []byte) error { - hash, err := paymentHeader.Hash() - if err != nil { - return fmt.Errorf("failed to hash payment header: %w", err) - } - - recoveredPubKey, err := crypto.SigToPub(hash[:], paymentSignature) - if err != nil { - return fmt.Errorf("failed to recover public key from signature: %w", err) - } - - recoveredAddress := crypto.PubkeyToAddress(*recoveredPubKey) - accountId := common.HexToAddress(paymentHeader.AccountID) - if recoveredAddress != accountId { - return fmt.Errorf("signature address %s does not match account id %s", recoveredAddress.Hex(), accountId.Hex()) - } - - ok := crypto.VerifySignature( - crypto.FromECDSAPub(recoveredPubKey), - hash[:], - paymentSignature[:len(paymentSignature)-1], // Remove recovery ID - ) - - if !ok { - return fmt.Errorf("invalid signature") - } - - return nil -} - -// GetAccountID returns the Ethereum address of the signer -func (s *paymentSigner) GetAccountID() string { - publicKey := crypto.FromECDSAPub(&s.PrivateKey.PublicKey) - hash := crypto.Keccak256(publicKey[1:]) - - return common.BytesToAddress(hash[12:]).Hex() -} diff --git a/core/auth/payment_signer_test.go b/core/auth/payment_signer_test.go deleted file mode 100644 index e2c1a172a..000000000 --- a/core/auth/payment_signer_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package auth_test - -import ( - "encoding/hex" - "math/big" - "testing" - - "github.com/Layr-Labs/eigenda/core" - "github.com/Layr-Labs/eigenda/core/auth" - "github.com/ethereum/go-ethereum/crypto" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPaymentSigner(t *testing.T) { - privateKey, err := crypto.GenerateKey() - require.NoError(t, err) - - privateKeyHex := hex.EncodeToString(crypto.FromECDSA(privateKey)) - signer, err := auth.NewPaymentSigner(privateKeyHex) - require.NoError(t, err) - - t.Run("SignBlobPayment", func(t *testing.T) { - header := &core.PaymentMetadata{ - AccountID: signer.GetAccountID(), - BinIndex: 1, - CumulativePayment: big.NewInt(1), - } - - signature, err := signer.SignBlobPayment(header) - require.NoError(t, err) - assert.NotEmpty(t, signature) - - // Verify the signature - err = auth.VerifyPaymentSignature(header, signature) - assert.NoError(t, err) - }) - - t.Run("VerifyPaymentSignature_InvalidSignature", func(t *testing.T) { - header := &core.PaymentMetadata{ - BinIndex: 1, - CumulativePayment: big.NewInt(1), - AccountID: signer.GetAccountID(), - } - - // Create an invalid signature - invalidSignature := make([]byte, 65) - err = auth.VerifyPaymentSignature(header, invalidSignature) - assert.Error(t, err) - }) - - t.Run("VerifyPaymentSignature_ModifiedHeader", func(t *testing.T) { - header := &core.PaymentMetadata{ - BinIndex: 1, - CumulativePayment: big.NewInt(1), - AccountID: signer.GetAccountID(), - } - - signature, err := signer.SignBlobPayment(header) - require.NoError(t, err) - - // Modify the header after signing - header.BinIndex = 2 - - err = auth.VerifyPaymentSignature(header, signature) - assert.Error(t, err) - }) -} - -func TestNoopPaymentSigner(t *testing.T) { - signer := auth.NewNoopPaymentSigner() - - t.Run("SignBlobRequest", func(t *testing.T) { - _, err := signer.SignBlobPayment(nil) - assert.Error(t, err) - assert.Contains(t, err.Error(), "noop signer cannot sign blob payment header") - }) -} diff --git a/core/auth/v2/auth_test.go b/core/auth/v2/auth_test.go index cf5a474c8..e700e3b9e 100644 --- a/core/auth/v2/auth_test.go +++ b/core/auth/v2/auth_test.go @@ -1,6 +1,7 @@ package v2_test import ( + "crypto/sha256" "math/big" "testing" @@ -9,6 +10,7 @@ import ( corev2 "github.com/Layr-Labs/eigenda/core/v2" "github.com/Layr-Labs/eigenda/encoding" "github.com/consensys/gnark-crypto/ecc/bn254/fp" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/assert" ) @@ -114,3 +116,70 @@ func testHeader(t *testing.T, accountID string) *corev2.BlobHeader { Signature: []byte{}, } } + +func TestAuthenticatePaymentStateRequestValid(t *testing.T) { + signer := auth.NewLocalBlobRequestSigner(privateKeyHex) + authenticator := auth.NewAuthenticator() + + signature, err := signer.SignPaymentStateRequest() + assert.NoError(t, err) + + accountId, err := signer.GetAccountID() + assert.NoError(t, err) + + err = authenticator.AuthenticatePaymentStateRequest(signature, accountId) + assert.NoError(t, err) +} + +func TestAuthenticatePaymentStateRequestInvalidSignatureLength(t *testing.T) { + authenticator := auth.NewAuthenticator() + + err := authenticator.AuthenticatePaymentStateRequest([]byte{1, 2, 3}, "0x123") + assert.Error(t, err) + assert.Contains(t, err.Error(), "signature length is unexpected") +} + +func TestAuthenticatePaymentStateRequestInvalidPublicKey(t *testing.T) { + authenticator := auth.NewAuthenticator() + + err := authenticator.AuthenticatePaymentStateRequest(make([]byte, 65), "not-hex-encoded") + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to decode public key") +} + +func TestAuthenticatePaymentStateRequestSignatureMismatch(t *testing.T) { + signer := auth.NewLocalBlobRequestSigner(privateKeyHex) + authenticator := auth.NewAuthenticator() + + // Create a different signer with wrong private key + wrongSigner := auth.NewLocalBlobRequestSigner("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcded") + + // Sign with wrong key + accountId, err := signer.GetAccountID() + assert.NoError(t, err) + + signature, err := wrongSigner.SignPaymentStateRequest() + assert.NoError(t, err) + + err = authenticator.AuthenticatePaymentStateRequest(signature, accountId) + assert.Error(t, err) + assert.Contains(t, err.Error(), "signature doesn't match with provided public key") +} + +func TestAuthenticatePaymentStateRequestCorruptedSignature(t *testing.T) { + signer := auth.NewLocalBlobRequestSigner(privateKeyHex) + authenticator := auth.NewAuthenticator() + + accountId, err := signer.GetAccountID() + assert.NoError(t, err) + + hash := sha256.Sum256([]byte(accountId)) + signature, err := crypto.Sign(hash[:], signer.PrivateKey) + assert.NoError(t, err) + + // Corrupt the signature + signature[0] ^= 0x01 + + err = authenticator.AuthenticatePaymentStateRequest(signature, accountId) + assert.Error(t, err) +} diff --git a/core/auth/v2/authenticator.go b/core/auth/v2/authenticator.go index 4d8c9615a..055a0edfb 100644 --- a/core/auth/v2/authenticator.go +++ b/core/auth/v2/authenticator.go @@ -2,11 +2,11 @@ package v2 import ( "bytes" + "crypto/sha256" "errors" "fmt" core "github.com/Layr-Labs/eigenda/core/v2" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" ) @@ -40,7 +40,7 @@ func (*authenticator) AuthenticateBlobRequest(header *core.BlobHeader) error { // Decode public key pubKey, err := crypto.UnmarshalPubkey(publicKeyBytes) if err != nil { - return fmt.Errorf("failed to decode public key (%v): %v", header.PaymentMetadata.AccountID, err) + return fmt.Errorf("failed to convert bytes to public key (%v): %v", header.PaymentMetadata.AccountID, err) } // Verify the signature @@ -55,3 +55,35 @@ func (*authenticator) AuthenticateBlobRequest(header *core.BlobHeader) error { return nil } + +func (*authenticator) AuthenticatePaymentStateRequest(sig []byte, accountId string) error { + // Ensure the signature is 65 bytes (Recovery ID is the last byte) + if len(sig) != 65 { + return fmt.Errorf("signature length is unexpected: %d", len(sig)) + } + + // Decode public key + publicKeyBytes, err := hexutil.Decode(accountId) + if err != nil { + return fmt.Errorf("failed to decode public key (%v): %v", accountId, err) + } + + // Convert bytes to public key + pubKey, err := crypto.UnmarshalPubkey(publicKeyBytes) + if err != nil { + return fmt.Errorf("failed to convert bytes to public key (%v): %v", accountId, err) + } + + // Verify the signature + hash := sha256.Sum256([]byte(accountId)) + sigPublicKeyECDSA, err := crypto.SigToPub(hash[:], sig) + if err != nil { + return fmt.Errorf("failed to recover public key from signature: %v", err) + } + + if !bytes.Equal(pubKey.X.Bytes(), sigPublicKeyECDSA.X.Bytes()) || !bytes.Equal(pubKey.Y.Bytes(), sigPublicKeyECDSA.Y.Bytes()) { + return errors.New("signature doesn't match with provided public key") + } + + return nil +} diff --git a/core/auth/v2/signer.go b/core/auth/v2/signer.go index bac6f3684..7ceefbe00 100644 --- a/core/auth/v2/signer.go +++ b/core/auth/v2/signer.go @@ -2,6 +2,7 @@ package v2 import ( "crypto/ecdsa" + "crypto/sha256" "fmt" "log" @@ -44,6 +45,22 @@ func (s *LocalBlobRequestSigner) SignBlobRequest(header *core.BlobHeader) ([]byt return sig, nil } +func (s *LocalBlobRequestSigner) SignPaymentStateRequest() ([]byte, error) { + accountId, err := s.GetAccountID() + if err != nil { + return nil, fmt.Errorf("failed to get account ID: %v", err) + } + + hash := sha256.Sum256([]byte(accountId)) + // Sign the account ID using the private key + sig, err := crypto.Sign(hash[:], s.PrivateKey) + if err != nil { + return nil, fmt.Errorf("failed to sign hash: %v", err) + } + + return sig, nil +} + func (s *LocalBlobRequestSigner) GetAccountID() (string, error) { publicKeyBytes := crypto.FromECDSAPub(&s.PrivateKey.PublicKey) @@ -63,6 +80,10 @@ func (s *LocalNoopSigner) SignBlobRequest(header *core.BlobHeader) ([]byte, erro return nil, fmt.Errorf("noop signer cannot sign blob request") } +func (s *LocalNoopSigner) SignPaymentStateRequest() ([]byte, error) { + return nil, fmt.Errorf("noop signer cannot sign payment state request") +} + func (s *LocalNoopSigner) GetAccountID() (string, error) { return "", fmt.Errorf("noop signer cannot get accountID") } diff --git a/core/data.go b/core/data.go index 430305690..1a3b5218a 100644 --- a/core/data.go +++ b/core/data.go @@ -601,7 +601,8 @@ func ConvertToPaymentMetadata(ph *commonpb.PaymentHeader) *PaymentMetadata { // OperatorInfo contains information about an operator which is stored on the blockchain state, // corresponding to a particular quorum type ActiveReservation struct { - SymbolsPerSec uint64 // reserve number of symbols per second + SymbolsPerSec uint64 // reserve number of symbols per second + //TODO: we are not using start and end timestamp, add check or remove StartTimestamp uint64 // Unix timestamp that's valid for basically eternity EndTimestamp uint64 diff --git a/core/meterer/offchain_store.go b/core/meterer/offchain_store.go index e07ca0668..bb5f8e185 100644 --- a/core/meterer/offchain_store.go +++ b/core/meterer/offchain_store.go @@ -8,6 +8,7 @@ import ( "strconv" "time" + pb "github.com/Layr-Labs/eigenda/api/grpc/disperser/v2" commonaws "github.com/Layr-Labs/eigenda/common/aws" commondynamodb "github.com/Layr-Labs/eigenda/common/aws/dynamodb" "github.com/Layr-Labs/eigenda/core" @@ -17,6 +18,8 @@ import ( "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" ) +const MinNumBins int32 = 3 + type OffchainStore struct { dynamoClient commondynamodb.Client reservationTableName string @@ -237,3 +240,97 @@ func (s *OffchainStore) GetRelevantOnDemandRecords(ctx context.Context, accountI return prevPayment, nextPayment, nextDataLength, nil } + +func (s *OffchainStore) GetBinRecords(ctx context.Context, accountID string, binIndex uint32) ([MinNumBins]*pb.BinRecord, error) { + // Fetch the 3 bins start from the current bin + queryInput := &dynamodb.QueryInput{ + TableName: aws.String(s.reservationTableName), + KeyConditionExpression: aws.String("AccountID = :account AND BinIndex > :binIndex"), + ExpressionAttributeValues: commondynamodb.ExpressionValues{ + ":account": &types.AttributeValueMemberS{Value: accountID}, + ":binIndex": &types.AttributeValueMemberN{Value: strconv.FormatUint(uint64(binIndex), 10)}, + }, + ScanIndexForward: aws.Bool(true), + Limit: aws.Int32(MinNumBins), + } + bins, err := s.dynamoClient.QueryWithInput(ctx, queryInput) + if err != nil { + return [MinNumBins]*pb.BinRecord{}, fmt.Errorf("failed to query payments for account: %w", err) + } + + records := [MinNumBins]*pb.BinRecord{} + for i := 0; i < len(bins) && i < int(MinNumBins); i++ { + binRecord, err := parseBinRecord(bins[i]) + if err != nil { + return [MinNumBins]*pb.BinRecord{}, fmt.Errorf("failed to parse bin %d record: %w", i, err) + } + records[i] = binRecord + } + + return records, nil +} + +func (s *OffchainStore) GetLargestCumulativePayment(ctx context.Context, accountID string) (*big.Int, error) { + // Fetch the largest cumulative payment + queryInput := &dynamodb.QueryInput{ + TableName: aws.String(s.onDemandTableName), + KeyConditionExpression: aws.String("AccountID = :account"), + ExpressionAttributeValues: commondynamodb.ExpressionValues{ + ":account": &types.AttributeValueMemberS{Value: accountID}, + }, + ScanIndexForward: aws.Bool(false), + Limit: aws.Int32(1), + } + payments, err := s.dynamoClient.QueryWithInput(ctx, queryInput) + if err != nil { + return nil, fmt.Errorf("failed to query payments for account: %w", err) + } + + if len(payments) == 0 { + return nil, nil + } + + payment, err := strconv.ParseUint(payments[0]["CumulativePayments"].(*types.AttributeValueMemberN).Value, 10, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse payment: %w", err) + } + + return new(big.Int).SetUint64(payment), nil +} + +func parseBinRecord(bin map[string]types.AttributeValue) (*pb.BinRecord, error) { + binIndex, ok := bin["BinIndex"] + if !ok { + return nil, errors.New("BinIndex is not present in the response") + } + + binIndexAttr, ok := binIndex.(*types.AttributeValueMemberN) + if !ok { + return nil, fmt.Errorf("unexpected type for BinIndex: %T", binIndex) + } + + binIndexValue, err := strconv.ParseUint(binIndexAttr.Value, 10, 32) + if err != nil { + return nil, fmt.Errorf("failed to parse BinIndex: %w", err) + } + + binUsage, ok := bin["BinUsage"] + if !ok { + return nil, errors.New("BinUsage is not present in the response") + } + + binUsageAttr, ok := binUsage.(*types.AttributeValueMemberN) + if !ok { + return nil, fmt.Errorf("unexpected type for BinUsage: %T", binUsage) + } + + binUsageValue, err := strconv.ParseUint(binUsageAttr.Value, 10, 32) + if err != nil { + return nil, fmt.Errorf("failed to parse BinUsage: %w", err) + } + + return &pb.BinRecord{ + Index: uint32(binIndexValue), + Usage: uint64(binUsageValue), + }, nil +} diff --git a/core/v2/auth.go b/core/v2/auth.go index 7ad714bf8..555825f5e 100644 --- a/core/v2/auth.go +++ b/core/v2/auth.go @@ -2,9 +2,11 @@ package v2 type BlobRequestAuthenticator interface { AuthenticateBlobRequest(header *BlobHeader) error + AuthenticatePaymentStateRequest(signature []byte, accountId string) error } type BlobRequestSigner interface { SignBlobRequest(header *BlobHeader) ([]byte, error) + SignPaymentStateRequest() ([]byte, error) GetAccountID() (string, error) } diff --git a/disperser/apiserver/disperse_blob_v2.go b/disperser/apiserver/disperse_blob_v2.go index 05034122c..01626ffb8 100644 --- a/disperser/apiserver/disperse_blob_v2.go +++ b/disperser/apiserver/disperse_blob_v2.go @@ -3,10 +3,12 @@ package apiserver import ( "context" "fmt" + "math/big" "time" "github.com/Layr-Labs/eigenda/api" pb "github.com/Layr-Labs/eigenda/api/grpc/disperser/v2" + "github.com/Layr-Labs/eigenda/core" corev2 "github.com/Layr-Labs/eigenda/core/v2" dispv2 "github.com/Layr-Labs/eigenda/disperser/common/v2" "github.com/Layr-Labs/eigenda/encoding" @@ -19,7 +21,7 @@ func (s *DispersalServerV2) DisperseBlob(ctx context.Context, req *pb.DisperseBl return nil, api.NewErrorInternal("onchain state is nil") } - if err := s.validateDispersalRequest(req, onchainState); err != nil { + if err := s.validateDispersalRequest(ctx, req, onchainState); err != nil { return nil, err } @@ -30,8 +32,6 @@ func (s *DispersalServerV2) DisperseBlob(ctx context.Context, req *pb.DisperseBl } s.logger.Debug("received a new blob dispersal request", "blobSizeBytes", len(data), "quorums", req.GetBlobHeader().GetQuorumNumbers()) - // TODO(ian-shim): handle payments and check rate limits - blobKey, err := s.StoreBlob(ctx, data, blobHeader, time.Now(), onchainState.TTL) if err != nil { return nil, err @@ -66,7 +66,7 @@ func (s *DispersalServerV2) StoreBlob(ctx context.Context, data []byte, blobHead return blobKey, err } -func (s *DispersalServerV2) validateDispersalRequest(req *pb.DisperseBlobRequest, onchainState *OnchainState) error { +func (s *DispersalServerV2) validateDispersalRequest(ctx context.Context, req *pb.DisperseBlobRequest, onchainState *OnchainState) error { data := req.GetData() blobSize := len(data) if blobSize == 0 { @@ -119,6 +119,26 @@ func (s *DispersalServerV2) validateDispersalRequest(req *pb.DisperseBlobRequest return api.NewErrorInvalidArg("invalid payment metadata") } + // handle payments and check rate limits + if blobHeaderProto.GetPaymentHeader() != nil { + binIndex := blobHeaderProto.GetPaymentHeader().GetBinIndex() + cumulativePayment := new(big.Int).SetBytes(blobHeaderProto.GetPaymentHeader().GetCumulativePayment()) + accountID := blobHeaderProto.GetPaymentHeader().GetAccountId() + + paymentHeader := core.PaymentMetadata{ + AccountID: accountID, + BinIndex: binIndex, + CumulativePayment: cumulativePayment, + } + + err := s.meterer.MeterRequest(ctx, paymentHeader, blobLength, blobHeader.QuorumNumbers) + if err != nil { + return api.NewErrorResourceExhausted(err.Error()) + } + } else { + return api.NewErrorInvalidArg("payment header is required") + } + commitments, err := s.prover.GetCommitmentsForPaddedLength(data) if err != nil { return api.NewErrorInternal(fmt.Sprintf("failed to get commitments: %v", err)) diff --git a/disperser/apiserver/server_v2.go b/disperser/apiserver/server_v2.go index acf85b054..114f24409 100644 --- a/disperser/apiserver/server_v2.go +++ b/disperser/apiserver/server_v2.go @@ -14,6 +14,7 @@ import ( "github.com/Layr-Labs/eigenda/common" healthcheck "github.com/Layr-Labs/eigenda/common/healthcheck" "github.com/Layr-Labs/eigenda/core" + "github.com/Layr-Labs/eigenda/core/meterer" corev2 "github.com/Layr-Labs/eigenda/core/v2" v2 "github.com/Layr-Labs/eigenda/core/v2" "github.com/Layr-Labs/eigenda/disperser" @@ -37,6 +38,7 @@ type DispersalServerV2 struct { serverConfig disperser.ServerConfig blobStore *blobstore.BlobStore blobMetadataStore *blobstore.BlobMetadataStore + meterer *meterer.Meterer chainReader core.Reader authenticator corev2.BlobRequestAuthenticator @@ -57,6 +59,7 @@ func NewDispersalServerV2( blobMetadataStore *blobstore.BlobMetadataStore, chainReader core.Reader, ratelimiter common.RateLimiter, + meterer *meterer.Meterer, authenticator corev2.BlobRequestAuthenticator, prover encoding.Prover, maxNumSymbolsPerBlob uint64, @@ -72,6 +75,7 @@ func NewDispersalServerV2( chainReader: chainReader, authenticator: authenticator, + meterer: meterer, prover: prover, logger: logger, @@ -207,3 +211,62 @@ func (s *DispersalServerV2) RefreshOnchainState(ctx context.Context) error { return nil } + +func (s *DispersalServerV2) GetPaymentState(ctx context.Context, req *pb.GetPaymentStateRequest) (*pb.GetPaymentStateReply, error) { + // validate the signature + if err := s.authenticator.AuthenticatePaymentStateRequest(req.GetSignature(), req.GetAccountId()); err != nil { + return nil, api.NewErrorInvalidArg(fmt.Sprintf("authentication failed: %s", err.Error())) + } + // on-chain global payment parameters + globalSymbolsPerSecond := s.meterer.ChainPaymentState.GetGlobalSymbolsPerSecond() + minNumSymbols := s.meterer.ChainPaymentState.GetMinNumSymbols() + pricePerSymbol := s.meterer.ChainPaymentState.GetPricePerSymbol() + reservationWindow := s.meterer.ChainPaymentState.GetReservationWindow() + + // off-chain account specific payment state + now := uint64(time.Now().Unix()) + currentBinIndex := meterer.GetBinIndex(now, reservationWindow) + binRecords, err := s.meterer.OffchainStore.GetBinRecords(ctx, req.AccountId, currentBinIndex) + if err != nil { + return nil, api.NewErrorNotFound("failed to get active reservation") + } + largestCumulativePayment, err := s.meterer.OffchainStore.GetLargestCumulativePayment(ctx, req.AccountId) + if err != nil { + return nil, api.NewErrorNotFound("failed to get largest cumulative payment") + } + // on-Chain account state + reservation, err := s.meterer.ChainPaymentState.GetActiveReservationByAccount(ctx, req.AccountId) + if err != nil { + return nil, api.NewErrorNotFound("failed to get active reservation") + } + onDemandPayment, err := s.meterer.ChainPaymentState.GetOnDemandPaymentByAccount(ctx, req.AccountId) + if err != nil { + return nil, api.NewErrorNotFound("failed to get on-demand payment") + } + + paymentGlobalParams := pb.PaymentGlobalParams{ + GlobalSymbolsPerSecond: globalSymbolsPerSecond, + MinNumSymbols: minNumSymbols, + PricePerSymbol: pricePerSymbol, + ReservationWindow: reservationWindow, + } + + quorumNumbers := make([]uint32, len(reservation.QuorumNumbers)) + for i, v := range reservation.QuorumNumbers { + quorumNumbers[i] = uint32(v) + } + // build reply + reply := &pb.GetPaymentStateReply{ + PaymentGlobalParams: &paymentGlobalParams, + BinRecords: binRecords[:], + Reservation: &pb.Reservation{ + SymbolsPerSecond: reservation.SymbolsPerSec, + StartTimestamp: uint32(reservation.StartTimestamp), + EndTimestamp: uint32(reservation.EndTimestamp), + QuorumNumbers: quorumNumbers, + }, + CumulativePayment: largestCumulativePayment.Bytes(), + OnchainCumulativePayment: onDemandPayment.CumulativePayment.Bytes(), + } + return reply, nil +} diff --git a/disperser/apiserver/server_v2_test.go b/disperser/apiserver/server_v2_test.go index 77afef008..17f84f510 100644 --- a/disperser/apiserver/server_v2_test.go +++ b/disperser/apiserver/server_v2_test.go @@ -14,6 +14,7 @@ import ( "github.com/Layr-Labs/eigenda/common/aws/s3" "github.com/Layr-Labs/eigenda/core" auth "github.com/Layr-Labs/eigenda/core/auth/v2" + "github.com/Layr-Labs/eigenda/core/meterer" "github.com/Layr-Labs/eigenda/core/mock" corev2 "github.com/Layr-Labs/eigenda/core/v2" v2 "github.com/Layr-Labs/eigenda/core/v2" @@ -431,6 +432,52 @@ func newTestServerV2(t *testing.T) *testComponents { chainReader := &mock.MockWriter{} rateConfig := apiserver.RateConfig{} + // append test name to each table name for an unique store + mockState := &mock.MockOnchainPaymentState{} + mockState.On("RefreshOnchainPaymentState", tmock.Anything).Return(nil).Maybe() + mockState.On("GetReservationWindow", tmock.Anything).Return(uint32(1), nil) + mockState.On("GetPricePerSymbol", tmock.Anything).Return(uint32(2), nil) + mockState.On("GetGlobalSymbolsPerSecond", tmock.Anything).Return(uint64(1009), nil) + mockState.On("GetMinNumSymbols", tmock.Anything).Return(uint32(3), nil) + + now := uint64(time.Now().Unix()) + mockState.On("GetActiveReservationByAccount", tmock.Anything, tmock.Anything).Return(core.ActiveReservation{SymbolsPerSec: 100, StartTimestamp: now + 1200, EndTimestamp: now + 1800, QuorumSplit: []byte{50, 50}, QuorumNumbers: []uint8{0, 1}}, nil) + mockState.On("GetOnDemandPaymentByAccount", tmock.Anything, tmock.Anything).Return(core.OnDemandPayment{CumulativePayment: big.NewInt(3864)}, nil) + mockState.On("GetOnDemandQuorumNumbers", tmock.Anything).Return([]uint8{0, 1}, nil) + + if err := mockState.RefreshOnchainPaymentState(context.Background(), nil); err != nil { + panic("failed to make initial query to the on-chain state") + } + table_names := []string{"reservations_server_" + t.Name(), "ondemand_server_" + t.Name(), "global_server_" + t.Name()} + err = meterer.CreateReservationTable(awsConfig, table_names[0]) + if err != nil { + teardown() + panic("failed to create reservation table") + } + err = meterer.CreateOnDemandTable(awsConfig, table_names[1]) + if err != nil { + teardown() + panic("failed to create ondemand table") + } + err = meterer.CreateGlobalReservationTable(awsConfig, table_names[2]) + if err != nil { + teardown() + panic("failed to create global reservation table") + } + + store, err := meterer.NewOffchainStore( + awsConfig, + table_names[0], + table_names[1], + table_names[2], + logger, + ) + if err != nil { + teardown() + panic("failed to create offchain store") + } + meterer := meterer.NewMeterer(meterer.Config{}, mockState, store, logger) + chainReader.On("GetCurrentBlockNumber").Return(uint32(100), nil) chainReader.On("GetQuorumCount").Return(uint8(2), nil) chainReader.On("GetRequiredQuorumNumbers", tmock.Anything).Return([]uint8{0, 1}, nil) @@ -447,7 +494,7 @@ func newTestServerV2(t *testing.T) *testComponents { s := apiserver.NewDispersalServerV2(disperser.ServerConfig{ GrpcPort: "51002", GrpcTimeout: 1 * time.Second, - }, rateConfig, blobStore, blobMetadataStore, chainReader, nil, auth.NewAuthenticator(), prover, 10, time.Hour, logger) + }, rateConfig, blobStore, blobMetadataStore, chainReader, nil, meterer, auth.NewAuthenticator(), prover, 10, time.Hour, logger) err = s.RefreshOnchainState(context.Background()) assert.NoError(t, err) diff --git a/disperser/cmd/apiserver/main.go b/disperser/cmd/apiserver/main.go index 0be93b7ab..07ff7936c 100644 --- a/disperser/cmd/apiserver/main.go +++ b/disperser/cmd/apiserver/main.go @@ -176,6 +176,7 @@ func RunDisperserServer(ctx *cli.Context) error { blobMetadataStore, transactor, ratelimiter, + meterer, authv2.NewAuthenticator(), prover, uint64(config.MaxNumSymbolsPerBlob), diff --git a/tools/traffic/generator_v2.go b/tools/traffic/generator_v2.go index 61575b9c0..dc39b4af8 100644 --- a/tools/traffic/generator_v2.go +++ b/tools/traffic/generator_v2.go @@ -88,6 +88,7 @@ func NewTrafficGeneratorV2(config *config.Config) (*Generator, error) { unconfirmedKeyChannel := make(chan *workers.UnconfirmedKey, 100) + // TODO: create a dedicated reservation for traffic generator disperserClient, err := clients.NewDisperserClient(config.DisperserClientConfig, signer) if err != nil { cancel()