From 980a8660503f9d3638c38af7fd48dc1a9d45ad44 Mon Sep 17 00:00:00 2001 From: hopeyen <60078528+hopeyen@users.noreply.github.com> Date: Thu, 12 Dec 2024 04:28:39 +0700 Subject: [PATCH] payment struct and names ReservationPeriod and Salt (#965) --- api/clients/accountant.go | 17 ++--- api/clients/accountant_test.go | 64 +++++++++--------- api/clients/disperser_client_v2.go | 5 +- api/docs/disperser.html | 2 +- api/docs/disperser.md | 2 +- api/docs/eigenda-protos.html | 18 +++-- api/docs/eigenda-protos.md | 9 +-- api/grpc/common/common.pb.go | 47 ++++++++----- api/grpc/disperser/disperser.pb.go | 2 +- api/proto/common/common.proto | 8 ++- api/proto/disperser/disperser.proto | 2 +- core/auth/v2/auth_test.go | 2 +- core/data.go | 52 +++++++-------- core/meterer/meterer.go | 32 ++++----- core/meterer/meterer_test.go | 54 +++++++-------- core/meterer/offchain_store.go | 46 ++++++------- core/meterer/offchain_store_test.go | 66 +++++++++---------- core/meterer/util.go | 12 ++-- core/v2/serialization_test.go | 21 +++--- core/v2/types.go | 9 +-- core/v2/types_test.go | 8 +-- disperser/apiserver/disperse_blob_v2.go | 6 +- disperser/apiserver/server_v2.go | 7 +- disperser/apiserver/server_v2_test.go | 20 +++--- .../blobstore/dynamo_metadata_store_test.go | 14 ++-- disperser/controller/controller_test.go | 4 +- disperser/controller/dispatcher_test.go | 4 +- disperser/dataapi/server_v2_test.go | 9 ++- disperser/encoder/server_v2_test.go | 2 +- node/mock/testdata.go | 6 +- relay/relay_test_utils.go | 17 ++--- 31 files changed, 297 insertions(+), 270 deletions(-) diff --git a/api/clients/accountant.go b/api/clients/accountant.go index ef76eef1d..045877bd7 100644 --- a/api/clients/accountant.go +++ b/api/clients/accountant.go @@ -69,11 +69,11 @@ func NewAccountant(accountID string, reservation *core.ActiveReservation, onDema // and both fields are used to create the payment header and signature func (a *Accountant) BlobPaymentInfo(ctx context.Context, numSymbols uint64, quorumNumbers []uint8) (uint32, *big.Int, error) { now := time.Now().Unix() - currentBinIndex := meterer.GetBinIndex(uint64(now), a.reservationWindow) + currentReservationPeriod := meterer.GetReservationPeriod(uint64(now), a.reservationWindow) a.usageLock.Lock() defer a.usageLock.Unlock() - relativeBinRecord := a.GetRelativeBinRecord(currentBinIndex) + relativeBinRecord := a.GetRelativeBinRecord(currentReservationPeriod) relativeBinRecord.Usage += numSymbols // first attempt to use the active reservation @@ -82,17 +82,17 @@ func (a *Accountant) BlobPaymentInfo(ctx context.Context, numSymbols uint64, quo if err := QuorumCheck(quorumNumbers, a.reservation.QuorumNumbers); err != nil { return 0, big.NewInt(0), err } - return currentBinIndex, big.NewInt(0), nil + return currentReservationPeriod, big.NewInt(0), nil } - overflowBinRecord := a.GetRelativeBinRecord(currentBinIndex + 2) + overflowBinRecord := a.GetRelativeBinRecord(currentReservationPeriod + 2) // Allow one overflow when the overflow bin is empty, the current usage and new length are both less than the limit if overflowBinRecord.Usage == 0 && relativeBinRecord.Usage-numSymbols < binLimit && numSymbols <= binLimit { overflowBinRecord.Usage += relativeBinRecord.Usage - binLimit if err := QuorumCheck(quorumNumbers, a.reservation.QuorumNumbers); err != nil { return 0, big.NewInt(0), err } - return currentBinIndex, big.NewInt(0), nil + return currentReservationPeriod, big.NewInt(0), nil } // reservation not available, attempt on-demand @@ -110,16 +110,17 @@ 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) (*core.PaymentMetadata, error) { - binIndex, cumulativePayment, err := a.BlobPaymentInfo(ctx, numSymbols, quorums) +func (a *Accountant) AccountBlob(ctx context.Context, numSymbols uint64, quorums []uint8, salt uint32) (*core.PaymentMetadata, error) { + reservationPeriod, cumulativePayment, err := a.BlobPaymentInfo(ctx, numSymbols, quorums) if err != nil { return nil, err } pm := &core.PaymentMetadata{ AccountID: a.accountID, - BinIndex: binIndex, + ReservationPeriod: reservationPeriod, CumulativePayment: cumulativePayment, + Salt: salt, } return pm, nil diff --git a/api/clients/accountant_test.go b/api/clients/accountant_test.go index 5c19e1592..c6dc3fa69 100644 --- a/api/clients/accountant_test.go +++ b/api/clients/accountant_test.go @@ -15,6 +15,7 @@ import ( ) const numBins = uint32(3) +const salt = uint32(0) func TestNewAccountant(t *testing.T) { reservation := &core.ActiveReservation{ @@ -70,27 +71,27 @@ func TestAccountBlob_Reservation(t *testing.T) { symbolLength := uint64(500) quorums := []uint8{0, 1} - header, err := accountant.AccountBlob(ctx, symbolLength, quorums) + header, err := accountant.AccountBlob(ctx, symbolLength, quorums, salt) assert.NoError(t, err) - assert.Equal(t, meterer.GetBinIndex(uint64(time.Now().Unix()), reservationWindow), header.BinIndex) + assert.Equal(t, meterer.GetReservationPeriod(uint64(time.Now().Unix()), reservationWindow), header.ReservationPeriod) assert.Equal(t, big.NewInt(0), header.CumulativePayment) assert.Equal(t, isRotation([]uint64{500, 0, 0}, mapRecordUsage(accountant.binRecords)), true) symbolLength = uint64(700) - header, err = accountant.AccountBlob(ctx, symbolLength, quorums) + header, err = accountant.AccountBlob(ctx, symbolLength, quorums, salt) assert.NoError(t, err) - assert.NotEqual(t, 0, header.BinIndex) + assert.NotEqual(t, 0, header.ReservationPeriod) assert.Equal(t, big.NewInt(0), header.CumulativePayment) 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, salt) assert.NoError(t, err) - assert.Equal(t, uint32(0), header.BinIndex) + assert.Equal(t, uint32(0), header.ReservationPeriod) assert.Equal(t, big.NewInt(300), header.CumulativePayment) } @@ -118,11 +119,11 @@ func TestAccountBlob_OnDemand(t *testing.T) { numSymbols := uint64(1500) quorums := []uint8{0, 1} - header, err := accountant.AccountBlob(ctx, numSymbols, quorums) + header, err := accountant.AccountBlob(ctx, numSymbols, quorums, salt) assert.NoError(t, err) expectedPayment := big.NewInt(int64(numSymbols * uint64(pricePerSymbol))) - assert.Equal(t, uint32(0), header.BinIndex) + assert.Equal(t, uint32(0), header.ReservationPeriod) assert.Equal(t, expectedPayment, header.CumulativePayment) assert.Equal(t, isRotation([]uint64{0, 0, 0}, mapRecordUsage(accountant.binRecords)), true) assert.Equal(t, expectedPayment, accountant.cumulativePayment) @@ -146,7 +147,7 @@ func TestAccountBlob_InsufficientOnDemand(t *testing.T) { numSymbols := uint64(2000) quorums := []uint8{0, 1} - _, err = accountant.AccountBlob(ctx, numSymbols, quorums) + _, err = accountant.AccountBlob(ctx, numSymbols, quorums, salt) assert.Contains(t, err.Error(), "neither reservation nor on-demand payment is available") } @@ -175,25 +176,25 @@ func TestAccountBlobCallSeries(t *testing.T) { now := time.Now().Unix() // First call: Use reservation - header, err := accountant.AccountBlob(ctx, 800, quorums) + header, err := accountant.AccountBlob(ctx, 800, quorums, salt) assert.NoError(t, err) - assert.Equal(t, meterer.GetBinIndex(uint64(now), reservationWindow), header.BinIndex) + assert.Equal(t, meterer.GetReservationPeriod(uint64(now), reservationWindow), header.ReservationPeriod) assert.Equal(t, big.NewInt(0), header.CumulativePayment) // Second call: Use remaining reservation + overflow - header, err = accountant.AccountBlob(ctx, 300, quorums) + header, err = accountant.AccountBlob(ctx, 300, quorums, salt) assert.NoError(t, err) - assert.Equal(t, meterer.GetBinIndex(uint64(now), reservationWindow), header.BinIndex) + assert.Equal(t, meterer.GetReservationPeriod(uint64(now), reservationWindow), header.ReservationPeriod) assert.Equal(t, big.NewInt(0), header.CumulativePayment) // Third call: Use on-demand - header, err = accountant.AccountBlob(ctx, 500, quorums) + header, err = accountant.AccountBlob(ctx, 500, quorums, salt) assert.NoError(t, err) - assert.Equal(t, uint32(0), header.BinIndex) + assert.Equal(t, uint32(0), header.ReservationPeriod) assert.Equal(t, big.NewInt(500), header.CumulativePayment) // Fourth call: Insufficient on-demand - _, err = accountant.AccountBlob(ctx, 600, quorums) + _, err = accountant.AccountBlob(ctx, 600, quorums, salt) assert.Error(t, err) assert.Contains(t, err.Error(), "neither reservation nor on-demand payment is available") } @@ -222,7 +223,7 @@ func TestAccountBlob_BinRotation(t *testing.T) { quorums := []uint8{0, 1} // First call - _, err = accountant.AccountBlob(ctx, 800, quorums) + _, err = accountant.AccountBlob(ctx, 800, quorums, salt) assert.NoError(t, err) assert.Equal(t, isRotation([]uint64{800, 0, 0}, mapRecordUsage(accountant.binRecords)), true) @@ -230,12 +231,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, salt) 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, salt) assert.NoError(t, err) assert.Equal(t, isRotation([]uint64{800, 800, 0}, mapRecordUsage(accountant.binRecords)), true) } @@ -269,12 +270,8 @@ func TestConcurrentBinRotationAndAccountBlob(t *testing.T) { wg.Add(1) go func() { 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, salt) assert.NoError(t, err) - time.Sleep(500 * time.Millisecond) - // } }() } @@ -311,22 +308,25 @@ func TestAccountBlob_ReservationWithOneOverflow(t *testing.T) { now := time.Now().Unix() // Okay reservation - header, err := accountant.AccountBlob(ctx, 800, quorums) + header, err := accountant.AccountBlob(ctx, 800, quorums, salt) assert.NoError(t, err) - assert.Equal(t, meterer.GetBinIndex(uint64(now), reservationWindow), header.BinIndex) + assert.Equal(t, salt, header.Salt) + assert.Equal(t, meterer.GetReservationPeriod(uint64(now), reservationWindow), header.ReservationPeriod) assert.Equal(t, big.NewInt(0), header.CumulativePayment) 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, salt+1) assert.NoError(t, err) + assert.Equal(t, salt+1, header.Salt) assert.Equal(t, big.NewInt(0), header.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, salt+2) assert.NoError(t, err) - assert.Equal(t, uint32(0), header.BinIndex) + assert.Equal(t, salt+2, header.Salt) + assert.Equal(t, uint32(0), header.ReservationPeriod) assert.Equal(t, big.NewInt(200), header.CumulativePayment) assert.Equal(t, isRotation([]uint64{1300, 0, 300}, mapRecordUsage(accountant.binRecords)), true) } @@ -355,12 +355,12 @@ func TestAccountBlob_ReservationOverflowReset(t *testing.T) { quorums := []uint8{0, 1} // full reservation - _, err = accountant.AccountBlob(ctx, 1000, quorums) + _, err = accountant.AccountBlob(ctx, 1000, quorums, salt) 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, salt) assert.NoError(t, err) assert.Equal(t, isRotation([]uint64{1000, 0, 0}, mapRecordUsage(accountant.binRecords)), true) assert.Equal(t, big.NewInt(500), header.CumulativePayment) @@ -369,7 +369,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, salt) assert.NoError(t, err) assert.Equal(t, isRotation([]uint64{1000, 500, 0}, mapRecordUsage(accountant.binRecords)), true) } diff --git a/api/clients/disperser_client_v2.go b/api/clients/disperser_client_v2.go index ec3407192..635a72bc6 100644 --- a/api/clients/disperser_client_v2.go +++ b/api/clients/disperser_client_v2.go @@ -23,7 +23,7 @@ type DisperserClientV2Config struct { type DisperserClientV2 interface { Close() error - DisperseBlob(ctx context.Context, data []byte, blobVersion corev2.BlobVersion, quorums []core.QuorumID) (*dispv2.BlobStatus, corev2.BlobKey, error) + DisperseBlob(ctx context.Context, data []byte, blobVersion corev2.BlobVersion, quorums []core.QuorumID, salt uint32) (*dispv2.BlobStatus, corev2.BlobKey, error) GetBlobStatus(ctx context.Context, blobKey corev2.BlobKey) (*disperser_rpc.BlobStatusReply, error) GetBlobCommitment(ctx context.Context, data []byte) (*disperser_rpc.BlobCommitmentReply, error) } @@ -114,6 +114,7 @@ func (c *disperserClientV2) DisperseBlob( data []byte, blobVersion corev2.BlobVersion, quorums []core.QuorumID, + salt uint32, ) (*dispv2.BlobStatus, corev2.BlobKey, error) { err := c.initOnceGrpcConnection() if err != nil { @@ -128,7 +129,7 @@ func (c *disperserClientV2) DisperseBlob( } symbolLength := encoding.GetBlobLengthPowerOf2(uint(len(data))) - payment, err := c.accountant.AccountBlob(ctx, uint64(symbolLength), quorums) + payment, err := c.accountant.AccountBlob(ctx, uint64(symbolLength), quorums, salt) if err != nil { return nil, [32]byte{}, fmt.Errorf("error accounting blob: %w", err) } diff --git a/api/docs/disperser.html b/api/docs/disperser.html index f3f51e9f5..fba274aa7 100644 --- a/api/docs/disperser.html +++ b/api/docs/disperser.html @@ -839,7 +839,7 @@

DispersePaidBlobRequest

payment_header common.PaymentHeader -

Payment header contains AccountID, BinIndex, and CumulativePayment

+

Payment header contains account_id, reservation_period, cumulative_payment, and salt

diff --git a/api/docs/disperser.md b/api/docs/disperser.md index c900aef33..39f01e439 100644 --- a/api/docs/disperser.md +++ b/api/docs/disperser.md @@ -287,7 +287,7 @@ BlobStatusRequest is used to query the status of a blob. | ----- | ---- | ----- | ----------- | | data | [bytes](#bytes) | | The data to be dispersed. Same requirements as DisperseBlobRequest. | | quorum_numbers | [uint32](#uint32) | repeated | The quorums to which the blob to be sent | -| payment_header | [common.PaymentHeader](#common-PaymentHeader) | | Payment header contains AccountID, BinIndex, and CumulativePayment | +| payment_header | [common.PaymentHeader](#common-PaymentHeader) | | Payment header contains account_id, reservation_period, cumulative_payment, and salt | | payment_signature | [bytes](#bytes) | | signature of payment_header | diff --git a/api/docs/eigenda-protos.html b/api/docs/eigenda-protos.html index 168b8699d..987f3250d 100644 --- a/api/docs/eigenda-protos.html +++ b/api/docs/eigenda-protos.html @@ -949,21 +949,29 @@

PaymentHeader

account_id string -

+

The account ID of the disperser client. This should be a hex-encoded string of the ECSDA public key +corresponding to the key used by the client to sign the BlobHeader.

- bin_index + reservation_period uint32 -

+

The reservation period of the dispersal request.

cumulative_payment bytes -

+

The cumulative payment of the dispersal request.

+ + + + salt + uint32 + +

The salt of the disperser request. This is used to ensure that the payment header is intentionally unique.

@@ -1713,7 +1721,7 @@

DispersePaidBlobRequest

payment_header common.PaymentHeader -

Payment header contains AccountID, BinIndex, and CumulativePayment

+

Payment header contains account_id, reservation_period, cumulative_payment, and salt

diff --git a/api/docs/eigenda-protos.md b/api/docs/eigenda-protos.md index 1e9b61912..732b7dc77 100644 --- a/api/docs/eigenda-protos.md +++ b/api/docs/eigenda-protos.md @@ -280,9 +280,10 @@ KZG commitment, degree proof, the actual degree, and data length in number of sy | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| account_id | [string](#string) | | | -| bin_index | [uint32](#uint32) | | | -| cumulative_payment | [bytes](#bytes) | | | +| account_id | [string](#string) | | The account ID of the disperser client. This should be a hex-encoded string of the ECSDA public key corresponding to the key used by the client to sign the BlobHeader. | +| reservation_period | [uint32](#uint32) | | The reservation period of the dispersal request. | +| cumulative_payment | [bytes](#bytes) | | The cumulative payment of the dispersal request. | +| salt | [uint32](#uint32) | | The salt of the disperser request. This is used to ensure that the payment header is intentionally unique. | @@ -638,7 +639,7 @@ BlobStatusRequest is used to query the status of a blob. | ----- | ---- | ----- | ----------- | | data | [bytes](#bytes) | | The data to be dispersed. Same requirements as DisperseBlobRequest. | | quorum_numbers | [uint32](#uint32) | repeated | The quorums to which the blob to be sent | -| payment_header | [common.PaymentHeader](#common-PaymentHeader) | | Payment header contains AccountID, BinIndex, and CumulativePayment | +| payment_header | [common.PaymentHeader](#common-PaymentHeader) | | Payment header contains account_id, reservation_period, cumulative_payment, and salt | | payment_signature | [bytes](#bytes) | | signature of payment_header | diff --git a/api/grpc/common/common.pb.go b/api/grpc/common/common.pb.go index cdf0f16ab..8f31656a0 100644 --- a/api/grpc/common/common.pb.go +++ b/api/grpc/common/common.pb.go @@ -155,9 +155,15 @@ type PaymentHeader struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - AccountId string `protobuf:"bytes,1,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` - BinIndex uint32 `protobuf:"varint,2,opt,name=bin_index,json=binIndex,proto3" json:"bin_index,omitempty"` + // The account ID of the disperser client. This should be a hex-encoded string of the ECSDA public key + // corresponding to the key used by the client to sign the BlobHeader. + AccountId string `protobuf:"bytes,1,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` + // The reservation period of the dispersal request. + ReservationPeriod uint32 `protobuf:"varint,2,opt,name=reservation_period,json=reservationPeriod,proto3" json:"reservation_period,omitempty"` + // The cumulative payment of the dispersal request. CumulativePayment []byte `protobuf:"bytes,3,opt,name=cumulative_payment,json=cumulativePayment,proto3" json:"cumulative_payment,omitempty"` + // The salt of the disperser request. This is used to ensure that the payment header is intentionally unique. + Salt uint32 `protobuf:"varint,4,opt,name=salt,proto3" json:"salt,omitempty"` } func (x *PaymentHeader) Reset() { @@ -199,9 +205,9 @@ func (x *PaymentHeader) GetAccountId() string { return "" } -func (x *PaymentHeader) GetBinIndex() uint32 { +func (x *PaymentHeader) GetReservationPeriod() uint32 { if x != nil { - return x.BinIndex + return x.ReservationPeriod } return 0 } @@ -213,6 +219,13 @@ func (x *PaymentHeader) GetCumulativePayment() []byte { return nil } +func (x *PaymentHeader) GetSalt() uint32 { + if x != nil { + return x.Salt + } + return 0 +} + var File_common_common_proto protoreflect.FileDescriptor var file_common_common_proto_rawDesc = []byte{ @@ -230,18 +243,20 @@ var file_common_common_proto_rawDesc = []byte{ 0x67, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 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, 0x22, 0x7a, 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 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, 0x1b, 0x0a, 0x09, 0x62, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x69, 0x6e, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, - 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, - 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x42, 0x2e, 0x5a, 0x2c, 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, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x67, 0x74, 0x68, 0x22, 0xa0, 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 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, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x11, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x65, + 0x72, 0x69, 0x6f, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x11, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x61, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x04, 0x73, 0x61, 0x6c, 0x74, 0x42, 0x2e, 0x5a, 0x2c, 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, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/grpc/disperser/disperser.pb.go b/api/grpc/disperser/disperser.pb.go index d10d4d736..47d211b77 100644 --- a/api/grpc/disperser/disperser.pb.go +++ b/api/grpc/disperser/disperser.pb.go @@ -458,7 +458,7 @@ type DispersePaidBlobRequest struct { Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` // The quorums to which the blob to be sent QuorumNumbers []uint32 `protobuf:"varint,2,rep,packed,name=quorum_numbers,json=quorumNumbers,proto3" json:"quorum_numbers,omitempty"` - // Payment header contains AccountID, BinIndex, and CumulativePayment + // Payment header contains account_id, reservation_period, cumulative_payment, and salt PaymentHeader *common.PaymentHeader `protobuf:"bytes,3,opt,name=payment_header,json=paymentHeader,proto3" json:"payment_header,omitempty"` // signature of payment_header PaymentSignature []byte `protobuf:"bytes,4,opt,name=payment_signature,json=paymentSignature,proto3" json:"payment_signature,omitempty"` diff --git a/api/proto/common/common.proto b/api/proto/common/common.proto index a457652ff..a75327ed7 100644 --- a/api/proto/common/common.proto +++ b/api/proto/common/common.proto @@ -19,7 +19,13 @@ message BlobCommitment { } message PaymentHeader { + // The account ID of the disperser client. This should be a hex-encoded string of the ECSDA public key + // corresponding to the key used by the client to sign the BlobHeader. string account_id = 1; - uint32 bin_index = 2; + // The reservation period of the dispersal request. + uint32 reservation_period = 2; + // The cumulative payment of the dispersal request. bytes cumulative_payment = 3; + // The salt of the disperser request. This is used to ensure that the payment header is intentionally unique. + uint32 salt = 4; } diff --git a/api/proto/disperser/disperser.proto b/api/proto/disperser/disperser.proto index f4bfa1384..ee537ee15 100644 --- a/api/proto/disperser/disperser.proto +++ b/api/proto/disperser/disperser.proto @@ -99,7 +99,7 @@ message DispersePaidBlobRequest { // The quorums to which the blob to be sent repeated uint32 quorum_numbers = 2; - // Payment header contains AccountID, BinIndex, and CumulativePayment + // Payment header contains account_id, reservation_period, cumulative_payment, and salt common.PaymentHeader payment_header = 3; // signature of payment_header bytes payment_signature = 4; diff --git a/core/auth/v2/auth_test.go b/core/auth/v2/auth_test.go index e700e3b9e..fb7c60cec 100644 --- a/core/auth/v2/auth_test.go +++ b/core/auth/v2/auth_test.go @@ -110,7 +110,7 @@ func testHeader(t *testing.T, accountID string) *corev2.BlobHeader { QuorumNumbers: []core.QuorumID{0, 1}, PaymentMetadata: core.PaymentMetadata{ AccountID: accountID, - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100), }, Signature: []byte{}, diff --git a/core/data.go b/core/data.go index 256f6f70e..adfb1509d 100644 --- a/core/data.go +++ b/core/data.go @@ -490,11 +490,13 @@ type PaymentMetadata struct { // AccountID is the ETH account address for the payer AccountID string `json:"account_id"` - // BinIndex represents the range of time at which the dispersal is made - BinIndex uint32 `json:"bin_index"` + // ReservationPeriod represents the range of time at which the dispersal is made + ReservationPeriod uint32 `json:"reservation_period"` // TODO: we are thinking the contract can use uint128 for cumulative payment, // but the definition on v2 uses uint64. Double check with team. CumulativePayment *big.Int `json:"cumulative_payment"` + // Allow same blob to be dispersed multiple times within the same reservation period + Salt uint32 `json:"salt"` } // Hash returns the Keccak256 hash of the PaymentMetadata @@ -505,13 +507,17 @@ func (pm *PaymentMetadata) Hash() ([32]byte, error) { Type: "string", }, { - Name: "binIndex", + Name: "reservationPeriod", Type: "uint32", }, { Name: "cumulativePayment", Type: "uint256", }, + { + Name: "salt", + Type: "uint32", + }, }) if err != nil { return [32]byte{}, err @@ -539,11 +545,12 @@ func (pm *PaymentMetadata) Hash() ([32]byte, error) { func (pm *PaymentMetadata) MarshalDynamoDBAttributeValue() (types.AttributeValue, error) { return &types.AttributeValueMemberM{ Value: map[string]types.AttributeValue{ - "AccountID": &types.AttributeValueMemberS{Value: pm.AccountID}, - "BinIndex": &types.AttributeValueMemberN{Value: fmt.Sprintf("%d", pm.BinIndex)}, + "AccountID": &types.AttributeValueMemberS{Value: pm.AccountID}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: fmt.Sprintf("%d", pm.ReservationPeriod)}, "CumulativePayment": &types.AttributeValueMemberN{ Value: pm.CumulativePayment.String(), }, + "Salt": &types.AttributeValueMemberN{Value: fmt.Sprintf("%d", pm.Salt)}, }, }, nil } @@ -554,38 +561,26 @@ func (pm *PaymentMetadata) UnmarshalDynamoDBAttributeValue(av types.AttributeVal return fmt.Errorf("expected *types.AttributeValueMemberM, got %T", av) } pm.AccountID = m.Value["AccountID"].(*types.AttributeValueMemberS).Value - binIndex, err := strconv.ParseUint(m.Value["BinIndex"].(*types.AttributeValueMemberN).Value, 10, 32) + reservationPeriod, err := strconv.ParseUint(m.Value["ReservationPeriod"].(*types.AttributeValueMemberN).Value, 10, 32) if err != nil { - return fmt.Errorf("failed to parse BinIndex: %w", err) + return fmt.Errorf("failed to parse ReservationPeriod: %w", err) } - pm.BinIndex = uint32(binIndex) + pm.ReservationPeriod = uint32(reservationPeriod) pm.CumulativePayment, _ = new(big.Int).SetString(m.Value["CumulativePayment"].(*types.AttributeValueMemberN).Value, 10) + salt, err := strconv.ParseUint(m.Value["Salt"].(*types.AttributeValueMemberN).Value, 10, 32) + if err != nil { + return fmt.Errorf("failed to parse Salt: %w", err) + } + pm.Salt = uint32(salt) return nil } func (pm *PaymentMetadata) ToProtobuf() *commonpb.PaymentHeader { return &commonpb.PaymentHeader{ AccountId: pm.AccountID, - BinIndex: pm.BinIndex, - CumulativePayment: pm.CumulativePayment.Bytes(), - } -} - -// ConvertPaymentHeader converts a protobuf payment header to a PaymentMetadata -func ConvertPaymentHeader(header *commonpb.PaymentHeader) *PaymentMetadata { - return &PaymentMetadata{ - AccountID: header.AccountId, - BinIndex: header.BinIndex, - CumulativePayment: new(big.Int).SetBytes(header.CumulativePayment), - } -} - -// ConvertToProtoPaymentHeader converts a PaymentMetadata to a protobuf payment header -func (pm *PaymentMetadata) ConvertToProtoPaymentHeader() *commonpb.PaymentHeader { - return &commonpb.PaymentHeader{ - AccountId: pm.AccountID, - BinIndex: pm.BinIndex, + ReservationPeriod: pm.ReservationPeriod, CumulativePayment: pm.CumulativePayment.Bytes(), + Salt: pm.Salt, } } @@ -593,8 +588,9 @@ func (pm *PaymentMetadata) ConvertToProtoPaymentHeader() *commonpb.PaymentHeader func ConvertToPaymentMetadata(ph *commonpb.PaymentHeader) *PaymentMetadata { return &PaymentMetadata{ AccountID: ph.AccountId, - BinIndex: ph.BinIndex, + ReservationPeriod: ph.ReservationPeriod, CumulativePayment: new(big.Int).SetBytes(ph.CumulativePayment), + Salt: ph.Salt, } } diff --git a/core/meterer/meterer.go b/core/meterer/meterer.go index f8d46d0b7..7195972c4 100644 --- a/core/meterer/meterer.go +++ b/core/meterer/meterer.go @@ -22,7 +22,7 @@ type Config struct { } // Meterer handles payment accounting across different accounts. Disperser API server receives requests from clients and each request contains a blob header -// with payments information (CumulativePayments, BinIndex, and Signature). Disperser will pass the blob header to the meterer, which will check if the +// with payments information (CumulativePayments, ReservationPeriod, Salt, and Signature). Disperser will pass the blob header to the meterer, which will check if the // payments information is valid. type Meterer struct { Config @@ -100,7 +100,7 @@ func (m *Meterer) ServeReservationRequest(ctx context.Context, header core.Payme if err := m.ValidateQuorum(quorumNumbers, reservation.QuorumNumbers); err != nil { return fmt.Errorf("invalid quorum for reservation: %w", err) } - if !m.ValidateBinIndex(header, reservation) { + if !m.ValidateReservationPeriod(header, reservation) { return fmt.Errorf("invalid bin index for reservation") } @@ -131,13 +131,13 @@ func (m *Meterer) ValidateQuorum(headerQuorums []uint8, allowedQuorums []uint8) return nil } -// ValidateBinIndex checks if the provided bin index is valid -func (m *Meterer) ValidateBinIndex(header core.PaymentMetadata, reservation *core.ActiveReservation) bool { +// ValidateReservationPeriod checks if the provided bin index is valid +func (m *Meterer) ValidateReservationPeriod(header core.PaymentMetadata, reservation *core.ActiveReservation) bool { now := uint64(time.Now().Unix()) reservationWindow := m.ChainPaymentState.GetReservationWindow() - currentBinIndex := GetBinIndex(now, reservationWindow) + currentReservationPeriod := GetReservationPeriod(now, reservationWindow) // Valid bin indexes are either the current bin or the previous bin - if (header.BinIndex != currentBinIndex && header.BinIndex != (currentBinIndex-1)) || (GetBinIndex(reservation.StartTimestamp, reservationWindow) > header.BinIndex || header.BinIndex > GetBinIndex(reservation.EndTimestamp, reservationWindow)) { + if (header.ReservationPeriod != currentReservationPeriod && header.ReservationPeriod != (currentReservationPeriod-1)) || (GetReservationPeriod(reservation.StartTimestamp, reservationWindow) > header.ReservationPeriod || header.ReservationPeriod > GetReservationPeriod(reservation.EndTimestamp, reservationWindow)) { return false } return true @@ -146,7 +146,7 @@ func (m *Meterer) ValidateBinIndex(header core.PaymentMetadata, reservation *cor // IncrementBinUsage increments the bin usage atomically and checks for overflow func (m *Meterer) IncrementBinUsage(ctx context.Context, header core.PaymentMetadata, reservation *core.ActiveReservation, numSymbols uint) error { symbolsCharged := m.SymbolsCharged(numSymbols) - newUsage, err := m.OffchainStore.UpdateReservationBin(ctx, header.AccountID, uint64(header.BinIndex), uint64(symbolsCharged)) + newUsage, err := m.OffchainStore.UpdateReservationBin(ctx, header.AccountID, uint64(header.ReservationPeriod), uint64(symbolsCharged)) if err != nil { return fmt.Errorf("failed to increment bin usage: %w", err) } @@ -159,8 +159,8 @@ func (m *Meterer) IncrementBinUsage(ctx context.Context, header core.PaymentMeta // metered usage before updating the size already exceeded the limit return fmt.Errorf("bin has already been filled") } - if newUsage <= 2*usageLimit && header.BinIndex+2 <= GetBinIndex(reservation.EndTimestamp, m.ChainPaymentState.GetReservationWindow()) { - _, err := m.OffchainStore.UpdateReservationBin(ctx, header.AccountID, uint64(header.BinIndex+2), newUsage-usageLimit) + if newUsage <= 2*usageLimit && header.ReservationPeriod+2 <= GetReservationPeriod(reservation.EndTimestamp, m.ChainPaymentState.GetReservationWindow()) { + _, err := m.OffchainStore.UpdateReservationBin(ctx, header.AccountID, uint64(header.ReservationPeriod+2), newUsage-usageLimit) if err != nil { return err } @@ -169,9 +169,9 @@ func (m *Meterer) IncrementBinUsage(ctx context.Context, header core.PaymentMeta return fmt.Errorf("overflow usage exceeds bin limit") } -// GetBinIndex returns the current bin index by chunking time by the bin interval; +// GetReservationPeriod returns the current bin index by chunking time by the bin interval; // bin interval used by the disperser should be public information -func GetBinIndex(timestamp uint64, binInterval uint32) uint32 { +func GetReservationPeriod(timestamp uint64, binInterval uint32) uint32 { return uint32(timestamp) / binInterval } @@ -256,16 +256,16 @@ func (m *Meterer) SymbolsCharged(numSymbols uint) uint32 { return uint32(core.RoundUpDivide(uint(numSymbols), uint(m.ChainPaymentState.GetMinNumSymbols()))) * m.ChainPaymentState.GetMinNumSymbols() } -// ValidateBinIndex checks if the provided bin index is valid -func (m *Meterer) ValidateGlobalBinIndex(header core.PaymentMetadata) (uint32, error) { +// ValidateReservationPeriod checks if the provided bin index is valid +func (m *Meterer) ValidateGlobalReservationPeriod(header core.PaymentMetadata) (uint32, error) { // Deterministic function: local clock -> index (1second intervals) - currentBinIndex := uint32(time.Now().Unix()) + currentReservationPeriod := uint32(time.Now().Unix()) // Valid bin indexes are either the current bin or the previous bin (allow this second or prev sec) - if header.BinIndex != currentBinIndex && header.BinIndex != (currentBinIndex-1) { + if header.ReservationPeriod != currentReservationPeriod && header.ReservationPeriod != (currentReservationPeriod-1) { return 0, fmt.Errorf("invalid bin index for on-demand request") } - return currentBinIndex, nil + return currentReservationPeriod, nil } // IncrementBinUsage increments the bin usage atomically and checks for overflow diff --git a/core/meterer/meterer_test.go b/core/meterer/meterer_test.go index dfc4b5f6b..6e3f980db 100644 --- a/core/meterer/meterer_test.go +++ b/core/meterer/meterer_test.go @@ -174,7 +174,7 @@ func TestMetererReservations(t *testing.T) { paymentChainState.On("GetGlobalRateBinInterval", testifymock.Anything).Return(uint64(1), nil) paymentChainState.On("GetMinNumSymbols", testifymock.Anything).Return(uint32(3), nil) - binIndex := meterer.GetBinIndex(uint64(time.Now().Unix()), mt.ChainPaymentState.GetReservationWindow()) + reservationPeriod := meterer.GetReservationPeriod(uint64(time.Now().Unix()), mt.ChainPaymentState.GetReservationWindow()) quoromNumbers := []uint8{0, 1} paymentChainState.On("GetActiveReservationByAccount", testifymock.Anything, testifymock.MatchedBy(func(account gethcommon.Address) bool { @@ -191,11 +191,11 @@ func TestMetererReservations(t *testing.T) { assert.ErrorContains(t, err, "quorum number mismatch") // overwhelming bin overflow for empty bins - header = createPaymentHeader(binIndex-1, 0, accountID2) + header = createPaymentHeader(reservationPeriod-1, 0, accountID2) err = mt.MeterRequest(ctx, *header, 10, quoromNumbers) assert.NoError(t, err) // overwhelming bin overflow for empty bins - header = createPaymentHeader(binIndex-1, 0, accountID2) + header = createPaymentHeader(reservationPeriod-1, 0, accountID2) err = mt.MeterRequest(ctx, *header, 1000, quoromNumbers) assert.ErrorContains(t, err, "overflow usage exceeds bin limit") @@ -210,7 +210,7 @@ func TestMetererReservations(t *testing.T) { assert.ErrorContains(t, err, "failed to get active reservation by account: reservation not found") // test invalid bin index - header = createPaymentHeader(binIndex, 0, accountID1) + header = createPaymentHeader(reservationPeriod, 0, accountID1) err = mt.MeterRequest(ctx, *header, 2000, quoromNumbers) assert.ErrorContains(t, err, "invalid bin index for reservation") @@ -218,37 +218,37 @@ func TestMetererReservations(t *testing.T) { symbolLength := uint(20) requiredLength := uint(21) // 21 should be charged for length of 20 since minNumSymbols is 3 for i := 0; i < 9; i++ { - header = createPaymentHeader(binIndex, 0, accountID2) + header = createPaymentHeader(reservationPeriod, 0, accountID2) err = mt.MeterRequest(ctx, *header, symbolLength, quoromNumbers) assert.NoError(t, err) item, err := dynamoClient.GetItem(ctx, reservationTableName, commondynamodb.Key{ - "AccountID": &types.AttributeValueMemberS{Value: accountID2.Hex()}, - "BinIndex": &types.AttributeValueMemberN{Value: strconv.Itoa(int(binIndex))}, + "AccountID": &types.AttributeValueMemberS{Value: accountID2.Hex()}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: strconv.Itoa(int(reservationPeriod))}, }) assert.NoError(t, err) assert.Equal(t, accountID2.Hex(), item["AccountID"].(*types.AttributeValueMemberS).Value) - assert.Equal(t, strconv.Itoa(int(binIndex)), item["BinIndex"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, strconv.Itoa(int(reservationPeriod)), item["ReservationPeriod"].(*types.AttributeValueMemberN).Value) assert.Equal(t, strconv.Itoa((i+1)*int(requiredLength)), item["BinUsage"].(*types.AttributeValueMemberN).Value) } // first over flow is allowed - header = createPaymentHeader(binIndex, 0, accountID2) + header = createPaymentHeader(reservationPeriod, 0, accountID2) assert.NoError(t, err) err = mt.MeterRequest(ctx, *header, 25, quoromNumbers) assert.NoError(t, err) - overflowedBinIndex := binIndex + 2 + overflowedReservationPeriod := reservationPeriod + 2 item, err := dynamoClient.GetItem(ctx, reservationTableName, commondynamodb.Key{ - "AccountID": &types.AttributeValueMemberS{Value: accountID2.Hex()}, - "BinIndex": &types.AttributeValueMemberN{Value: strconv.Itoa(int(overflowedBinIndex))}, + "AccountID": &types.AttributeValueMemberS{Value: accountID2.Hex()}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: strconv.Itoa(int(overflowedReservationPeriod))}, }) assert.NoError(t, err) assert.Equal(t, accountID2.Hex(), item["AccountID"].(*types.AttributeValueMemberS).Value) - assert.Equal(t, strconv.Itoa(int(overflowedBinIndex)), item["BinIndex"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, strconv.Itoa(int(overflowedReservationPeriod)), item["ReservationPeriod"].(*types.AttributeValueMemberN).Value) // 25 rounded up to the nearest multiple of minNumSymbols - (200-21*9) = 16 assert.Equal(t, strconv.Itoa(int(16)), item["BinUsage"].(*types.AttributeValueMemberN).Value) // second over flow - header = createPaymentHeader(binIndex, 0, accountID2) + header = createPaymentHeader(reservationPeriod, 0, accountID2) assert.NoError(t, err) err = mt.MeterRequest(ctx, *header, 1, quoromNumbers) assert.ErrorContains(t, err, "bin has already been filled") @@ -259,7 +259,7 @@ func TestMetererOnDemand(t *testing.T) { quorumNumbers := []uint8{0, 1} paymentChainState.On("GetPricePerSymbol", testifymock.Anything).Return(uint32(2), nil) paymentChainState.On("GetMinNumSymbols", testifymock.Anything).Return(uint32(3), nil) - binIndex := uint32(0) // this field doesn't matter for on-demand payments wrt global rate limit + reservationPeriod := uint32(0) // this field doesn't matter for on-demand payments wrt global rate limit paymentChainState.On("GetOnDemandPaymentByAccount", testifymock.Anything, testifymock.MatchedBy(func(account gethcommon.Address) bool { return account == accountID1 @@ -275,18 +275,18 @@ func TestMetererOnDemand(t *testing.T) { if err != nil { t.Fatalf("Failed to generate key: %v", err) } - header := createPaymentHeader(binIndex, 2, crypto.PubkeyToAddress(unregisteredUser.PublicKey)) + header := createPaymentHeader(reservationPeriod, 2, crypto.PubkeyToAddress(unregisteredUser.PublicKey)) assert.NoError(t, err) err = mt.MeterRequest(ctx, *header, 1000, quorumNumbers) assert.ErrorContains(t, err, "failed to get on-demand payment by account: payment not found") // test invalid quorom ID - header = createPaymentHeader(binIndex, 1, accountID1) + header = createPaymentHeader(reservationPeriod, 1, accountID1) err = mt.MeterRequest(ctx, *header, 1000, []uint8{0, 1, 2}) assert.ErrorContains(t, err, "invalid quorum for On-Demand Request") // test insufficient cumulative payment - header = createPaymentHeader(binIndex, 1, accountID1) + header = createPaymentHeader(reservationPeriod, 1, accountID1) err = mt.MeterRequest(ctx, *header, 1000, quorumNumbers) assert.ErrorContains(t, err, "insufficient cumulative payment increment") // No rollback after meter request @@ -301,22 +301,22 @@ func TestMetererOnDemand(t *testing.T) { symbolLength := uint(100) priceCharged := mt.PaymentCharged(symbolLength) assert.Equal(t, uint64(102*mt.ChainPaymentState.GetPricePerSymbol()), priceCharged) - header = createPaymentHeader(binIndex, priceCharged, accountID2) + header = createPaymentHeader(reservationPeriod, priceCharged, accountID2) err = mt.MeterRequest(ctx, *header, symbolLength, quorumNumbers) assert.NoError(t, err) - header = createPaymentHeader(binIndex, priceCharged, accountID2) + header = createPaymentHeader(reservationPeriod, priceCharged, accountID2) err = mt.MeterRequest(ctx, *header, symbolLength, quorumNumbers) assert.ErrorContains(t, err, "exact payment already exists") // test valid payments for i := 1; i < 9; i++ { - header = createPaymentHeader(binIndex, uint64(priceCharged)*uint64(i+1), accountID2) + header = createPaymentHeader(reservationPeriod, uint64(priceCharged)*uint64(i+1), accountID2) err = mt.MeterRequest(ctx, *header, symbolLength, quorumNumbers) assert.NoError(t, err) } // test cumulative payment on-chain constraint - header = createPaymentHeader(binIndex, 2023, accountID2) + header = createPaymentHeader(reservationPeriod, 2023, accountID2) err = mt.MeterRequest(ctx, *header, 1, quorumNumbers) assert.ErrorContains(t, err, "invalid on-demand payment: request claims a cumulative payment greater than the on-chain deposit") @@ -324,13 +324,13 @@ func TestMetererOnDemand(t *testing.T) { previousCumulativePayment := uint64(priceCharged) * uint64(9) symbolLength = uint(2) priceCharged = mt.PaymentCharged(symbolLength) - header = createPaymentHeader(binIndex, previousCumulativePayment+priceCharged-1, accountID2) + header = createPaymentHeader(reservationPeriod, previousCumulativePayment+priceCharged-1, accountID2) err = mt.MeterRequest(ctx, *header, symbolLength, quorumNumbers) assert.ErrorContains(t, err, "invalid on-demand payment: insufficient cumulative payment increment") previousCumulativePayment = previousCumulativePayment + priceCharged // test cannot insert cumulative payment in out of order - header = createPaymentHeader(binIndex, mt.PaymentCharged(50), accountID2) + header = createPaymentHeader(reservationPeriod, mt.PaymentCharged(50), accountID2) err = mt.MeterRequest(ctx, *header, 50, quorumNumbers) assert.ErrorContains(t, err, "invalid on-demand payment: breaking cumulative payment invariants") @@ -342,7 +342,7 @@ func TestMetererOnDemand(t *testing.T) { assert.NoError(t, err) assert.Equal(t, numPrevRecords, len(result)) // test failed global rate limit (previously payment recorded: 2, global limit: 1009) - header = createPaymentHeader(binIndex, previousCumulativePayment+mt.PaymentCharged(1010), accountID1) + header = createPaymentHeader(reservationPeriod, previousCumulativePayment+mt.PaymentCharged(1010), accountID1) err = mt.MeterRequest(ctx, *header, 1010, quorumNumbers) assert.ErrorContains(t, err, "failed global rate limiting") // Correct rollback @@ -465,10 +465,10 @@ func TestMeterer_symbolsCharged(t *testing.T) { } } -func createPaymentHeader(binIndex uint32, cumulativePayment uint64, accountID gethcommon.Address) *core.PaymentMetadata { +func createPaymentHeader(reservationPeriod uint32, cumulativePayment uint64, accountID gethcommon.Address) *core.PaymentMetadata { return &core.PaymentMetadata{ AccountID: accountID.Hex(), - BinIndex: binIndex, + ReservationPeriod: reservationPeriod, CumulativePayment: big.NewInt(int64(cumulativePayment)), } } diff --git a/core/meterer/offchain_store.go b/core/meterer/offchain_store.go index bb5f8e185..f80ddd891 100644 --- a/core/meterer/offchain_store.go +++ b/core/meterer/offchain_store.go @@ -66,10 +66,10 @@ func NewOffchainStore( } type ReservationBin struct { - AccountID string - BinIndex uint32 - BinUsage uint32 - UpdatedAt time.Time + AccountID string + ReservationPeriod uint32 + BinUsage uint32 + UpdatedAt time.Time } type PaymentTuple struct { @@ -78,15 +78,15 @@ type PaymentTuple struct { } type GlobalBin struct { - BinIndex uint32 - BinUsage uint64 - UpdatedAt time.Time + ReservationPeriod uint32 + BinUsage uint64 + UpdatedAt time.Time } -func (s *OffchainStore) UpdateReservationBin(ctx context.Context, accountID string, binIndex uint64, size uint64) (uint64, error) { +func (s *OffchainStore) UpdateReservationBin(ctx context.Context, accountID string, reservationPeriod uint64, size uint64) (uint64, error) { key := map[string]types.AttributeValue{ - "AccountID": &types.AttributeValueMemberS{Value: accountID}, - "BinIndex": &types.AttributeValueMemberN{Value: strconv.FormatUint(binIndex, 10)}, + "AccountID": &types.AttributeValueMemberS{Value: accountID}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: strconv.FormatUint(reservationPeriod, 10)}, } res, err := s.dynamoClient.IncrementBy(ctx, s.reservationTableName, key, "BinUsage", size) @@ -112,9 +112,9 @@ func (s *OffchainStore) UpdateReservationBin(ctx context.Context, accountID stri return binUsageValue, nil } -func (s *OffchainStore) UpdateGlobalBin(ctx context.Context, binIndex uint64, size uint64) (uint64, error) { +func (s *OffchainStore) UpdateGlobalBin(ctx context.Context, reservationPeriod uint64, size uint64) (uint64, error) { key := map[string]types.AttributeValue{ - "BinIndex": &types.AttributeValueMemberN{Value: strconv.FormatUint(binIndex, 10)}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: strconv.FormatUint(reservationPeriod, 10)}, } res, err := s.dynamoClient.IncrementBy(ctx, s.globalBinTableName, key, "BinUsage", size) @@ -241,14 +241,14 @@ 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) { +func (s *OffchainStore) GetBinRecords(ctx context.Context, accountID string, reservationPeriod 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"), + KeyConditionExpression: aws.String("AccountID = :account AND ReservationPeriod > :reservationPeriod"), ExpressionAttributeValues: commondynamodb.ExpressionValues{ - ":account": &types.AttributeValueMemberS{Value: accountID}, - ":binIndex": &types.AttributeValueMemberN{Value: strconv.FormatUint(uint64(binIndex), 10)}, + ":account": &types.AttributeValueMemberS{Value: accountID}, + ":reservationPeriod": &types.AttributeValueMemberN{Value: strconv.FormatUint(uint64(reservationPeriod), 10)}, }, ScanIndexForward: aws.Bool(true), Limit: aws.Int32(MinNumBins), @@ -299,19 +299,19 @@ func (s *OffchainStore) GetLargestCumulativePayment(ctx context.Context, account } func parseBinRecord(bin map[string]types.AttributeValue) (*pb.BinRecord, error) { - binIndex, ok := bin["BinIndex"] + reservationPeriod, ok := bin["ReservationPeriod"] if !ok { - return nil, errors.New("BinIndex is not present in the response") + return nil, errors.New("ReservationPeriod is not present in the response") } - binIndexAttr, ok := binIndex.(*types.AttributeValueMemberN) + reservationPeriodAttr, ok := reservationPeriod.(*types.AttributeValueMemberN) if !ok { - return nil, fmt.Errorf("unexpected type for BinIndex: %T", binIndex) + return nil, fmt.Errorf("unexpected type for ReservationPeriod: %T", reservationPeriod) } - binIndexValue, err := strconv.ParseUint(binIndexAttr.Value, 10, 32) + reservationPeriodValue, err := strconv.ParseUint(reservationPeriodAttr.Value, 10, 32) if err != nil { - return nil, fmt.Errorf("failed to parse BinIndex: %w", err) + return nil, fmt.Errorf("failed to parse ReservationPeriod: %w", err) } binUsage, ok := bin["BinUsage"] @@ -330,7 +330,7 @@ func parseBinRecord(bin map[string]types.AttributeValue) (*pb.BinRecord, error) } return &pb.BinRecord{ - Index: uint32(binIndexValue), + Index: uint32(reservationPeriodValue), Usage: uint64(binUsageValue), }, nil } diff --git a/core/meterer/offchain_store_test.go b/core/meterer/offchain_store_test.go index 1c3351764..79963b059 100644 --- a/core/meterer/offchain_store_test.go +++ b/core/meterer/offchain_store_test.go @@ -21,22 +21,22 @@ func TestReservationBinsBasicOperations(t *testing.T) { ctx := context.Background() err = dynamoClient.PutItem(ctx, tableName, commondynamodb.Item{ - "AccountID": &types.AttributeValueMemberS{Value: "account1"}, - "BinIndex": &types.AttributeValueMemberN{Value: "1"}, - "BinUsage": &types.AttributeValueMemberN{Value: "1000"}, - "UpdatedAt": &types.AttributeValueMemberS{Value: time.Now().Format(time.RFC3339)}, + "AccountID": &types.AttributeValueMemberS{Value: "account1"}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, + "BinUsage": &types.AttributeValueMemberN{Value: "1000"}, + "UpdatedAt": &types.AttributeValueMemberS{Value: time.Now().Format(time.RFC3339)}, }, ) assert.NoError(t, err) item, err := dynamoClient.GetItem(ctx, tableName, commondynamodb.Key{ - "AccountID": &types.AttributeValueMemberS{Value: "account1"}, - "BinIndex": &types.AttributeValueMemberN{Value: "1"}, + "AccountID": &types.AttributeValueMemberS{Value: "account1"}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, }) assert.NoError(t, err) assert.Equal(t, "account1", item["AccountID"].(*types.AttributeValueMemberS).Value) - assert.Equal(t, "1", item["BinIndex"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "1", item["ReservationPeriod"].(*types.AttributeValueMemberN).Value) assert.Equal(t, "1000", item["BinUsage"].(*types.AttributeValueMemberN).Value) items, err := dynamoClient.Query(ctx, tableName, "AccountID = :account", commondynamodb.ExpressionValues{ @@ -51,25 +51,25 @@ func TestReservationBinsBasicOperations(t *testing.T) { assert.Error(t, err) _, err = dynamoClient.UpdateItem(ctx, tableName, commondynamodb.Key{ - "AccountID": &types.AttributeValueMemberS{Value: "account1"}, - "BinIndex": &types.AttributeValueMemberN{Value: "1"}, + "AccountID": &types.AttributeValueMemberS{Value: "account1"}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, }, commondynamodb.Item{ "BinUsage": &types.AttributeValueMemberN{Value: "2000"}, }) assert.NoError(t, err) err = dynamoClient.PutItem(ctx, tableName, commondynamodb.Item{ - "AccountID": &types.AttributeValueMemberS{Value: "account2"}, - "BinIndex": &types.AttributeValueMemberN{Value: "1"}, - "BinUsage": &types.AttributeValueMemberN{Value: "3000"}, - "UpdatedAt": &types.AttributeValueMemberS{Value: time.Now().Format(time.RFC3339)}, + "AccountID": &types.AttributeValueMemberS{Value: "account2"}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, + "BinUsage": &types.AttributeValueMemberN{Value: "3000"}, + "UpdatedAt": &types.AttributeValueMemberS{Value: time.Now().Format(time.RFC3339)}, }, ) assert.NoError(t, err) item, err = dynamoClient.GetItem(ctx, tableName, commondynamodb.Key{ - "AccountID": &types.AttributeValueMemberS{Value: "account1"}, - "BinIndex": &types.AttributeValueMemberN{Value: "1"}, + "AccountID": &types.AttributeValueMemberS{Value: "account1"}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, }) assert.NoError(t, err) assert.Equal(t, "2000", item["BinUsage"].(*types.AttributeValueMemberN).Value) @@ -82,8 +82,8 @@ func TestReservationBinsBasicOperations(t *testing.T) { assert.Equal(t, "2000", items[0]["BinUsage"].(*types.AttributeValueMemberN).Value) item, err = dynamoClient.GetItem(ctx, tableName, commondynamodb.Key{ - "AccountID": &types.AttributeValueMemberS{Value: "account2"}, - "BinIndex": &types.AttributeValueMemberN{Value: "1"}, + "AccountID": &types.AttributeValueMemberS{Value: "account2"}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, }) assert.NoError(t, err) assert.Equal(t, "3000", item["BinUsage"].(*types.AttributeValueMemberN).Value) @@ -102,34 +102,34 @@ func TestGlobalBinsBasicOperations(t *testing.T) { items := make([]commondynamodb.Item, numItems) for i := 0; i < numItems; i += 1 { items[i] = commondynamodb.Item{ - "BinIndex": &types.AttributeValueMemberN{Value: fmt.Sprintf("%d", i)}, - "BinUsage": &types.AttributeValueMemberN{Value: "1000"}, - "UpdatedAt": &types.AttributeValueMemberS{Value: time.Now().Format(time.RFC3339)}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: fmt.Sprintf("%d", i)}, + "BinUsage": &types.AttributeValueMemberN{Value: "1000"}, + "UpdatedAt": &types.AttributeValueMemberS{Value: time.Now().Format(time.RFC3339)}, } } unprocessed, err := dynamoClient.PutItems(ctx, tableName, items) assert.NoError(t, err) assert.Len(t, unprocessed, 0) - queryResult, err := dynamoClient.Query(ctx, tableName, "BinIndex = :index", commondynamodb.ExpressionValues{ + queryResult, err := dynamoClient.Query(ctx, tableName, "ReservationPeriod = :index", commondynamodb.ExpressionValues{ ":index": &types.AttributeValueMemberN{ Value: "1", }}) assert.NoError(t, err) assert.Len(t, queryResult, 1) - assert.Equal(t, "1", queryResult[0]["BinIndex"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "1", queryResult[0]["ReservationPeriod"].(*types.AttributeValueMemberN).Value) assert.Equal(t, "1000", queryResult[0]["BinUsage"].(*types.AttributeValueMemberN).Value) - queryResult, err = dynamoClient.Query(ctx, tableName, "BinIndex = :index", commondynamodb.ExpressionValues{ + queryResult, err = dynamoClient.Query(ctx, tableName, "ReservationPeriod = :index", commondynamodb.ExpressionValues{ ":index": &types.AttributeValueMemberN{ Value: "1", }}) assert.NoError(t, err) assert.Len(t, queryResult, 1) - assert.Equal(t, "1", queryResult[0]["BinIndex"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "1", queryResult[0]["ReservationPeriod"].(*types.AttributeValueMemberN).Value) assert.Equal(t, "1000", queryResult[0]["BinUsage"].(*types.AttributeValueMemberN).Value) - queryResult, err = dynamoClient.Query(ctx, tableName, "BinIndex = :index", commondynamodb.ExpressionValues{ + queryResult, err = dynamoClient.Query(ctx, tableName, "ReservationPeriod = :index", commondynamodb.ExpressionValues{ ":index": &types.AttributeValueMemberN{ Value: "32", }}) @@ -137,7 +137,7 @@ func TestGlobalBinsBasicOperations(t *testing.T) { assert.Len(t, queryResult, 0) _, err = dynamoClient.UpdateItem(ctx, tableName, commondynamodb.Key{ - "BinIndex": &types.AttributeValueMemberN{Value: "1"}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, }, commondynamodb.Item{ "BinUsage": &types.AttributeValueMemberN{Value: "2000"}, }) @@ -145,29 +145,29 @@ func TestGlobalBinsBasicOperations(t *testing.T) { err = dynamoClient.PutItem(ctx, tableName, commondynamodb.Item{ - "BinIndex": &types.AttributeValueMemberN{Value: "2"}, - "BinUsage": &types.AttributeValueMemberN{Value: "3000"}, - "UpdatedAt": &types.AttributeValueMemberS{Value: time.Now().Format(time.RFC3339)}, + "ReservationPeriod": &types.AttributeValueMemberN{Value: "2"}, + "BinUsage": &types.AttributeValueMemberN{Value: "3000"}, + "UpdatedAt": &types.AttributeValueMemberS{Value: time.Now().Format(time.RFC3339)}, }, ) assert.NoError(t, err) - queryResult, err = dynamoClient.Query(ctx, tableName, "BinIndex = :index", commondynamodb.ExpressionValues{ + queryResult, err = dynamoClient.Query(ctx, tableName, "ReservationPeriod = :index", commondynamodb.ExpressionValues{ ":index": &types.AttributeValueMemberN{ Value: "1", }}) assert.NoError(t, err) assert.Len(t, queryResult, 1) - assert.Equal(t, "1", queryResult[0]["BinIndex"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "1", queryResult[0]["ReservationPeriod"].(*types.AttributeValueMemberN).Value) assert.Equal(t, "2000", queryResult[0]["BinUsage"].(*types.AttributeValueMemberN).Value) - queryResult, err = dynamoClient.Query(ctx, tableName, "BinIndex = :index", commondynamodb.ExpressionValues{ + queryResult, err = dynamoClient.Query(ctx, tableName, "ReservationPeriod = :index", commondynamodb.ExpressionValues{ ":index": &types.AttributeValueMemberN{ Value: "2", }}) assert.NoError(t, err) assert.Len(t, queryResult, 1) - assert.Equal(t, "2", queryResult[0]["BinIndex"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "2", queryResult[0]["ReservationPeriod"].(*types.AttributeValueMemberN).Value) assert.Equal(t, "3000", queryResult[0]["BinUsage"].(*types.AttributeValueMemberN).Value) } diff --git a/core/meterer/util.go b/core/meterer/util.go index 92b769370..0aa44dffd 100644 --- a/core/meterer/util.go +++ b/core/meterer/util.go @@ -19,7 +19,7 @@ func CreateReservationTable(clientConfig commonaws.ClientConfig, tableName strin AttributeType: types.ScalarAttributeTypeS, }, { - AttributeName: aws.String("BinIndex"), + AttributeName: aws.String("ReservationPeriod"), AttributeType: types.ScalarAttributeTypeN, }, }, @@ -29,7 +29,7 @@ func CreateReservationTable(clientConfig commonaws.ClientConfig, tableName strin KeyType: types.KeyTypeHash, }, { - AttributeName: aws.String("BinIndex"), + AttributeName: aws.String("ReservationPeriod"), KeyType: types.KeyTypeRange, }, }, @@ -47,22 +47,22 @@ func CreateGlobalReservationTable(clientConfig commonaws.ClientConfig, tableName _, err := test_utils.CreateTable(ctx, clientConfig, tableName, &dynamodb.CreateTableInput{ AttributeDefinitions: []types.AttributeDefinition{ { - AttributeName: aws.String("BinIndex"), + AttributeName: aws.String("ReservationPeriod"), AttributeType: types.ScalarAttributeTypeN, }, }, KeySchema: []types.KeySchemaElement{ { - AttributeName: aws.String("BinIndex"), + AttributeName: aws.String("ReservationPeriod"), KeyType: types.KeyTypeHash, }, }, GlobalSecondaryIndexes: []types.GlobalSecondaryIndex{ { - IndexName: aws.String("BinIndexIndex"), + IndexName: aws.String("ReservationPeriodIndex"), KeySchema: []types.KeySchemaElement{ { - AttributeName: aws.String("BinIndex"), + AttributeName: aws.String("ReservationPeriod"), KeyType: types.KeyTypeHash, }, }, diff --git a/core/v2/serialization_test.go b/core/v2/serialization_test.go index 20b4d830f..78e97d190 100644 --- a/core/v2/serialization_test.go +++ b/core/v2/serialization_test.go @@ -23,13 +23,14 @@ func TestBlobKey(t *testing.T) { func TestPaymentHash(t *testing.T) { pm := core.PaymentMetadata{ AccountID: "0x123", - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100), + Salt: 42, } hash, err := pm.Hash() assert.NoError(t, err) - // 0xf5894a8e9281b5687c0c7757d3d45fb76152bf659e6e61b1062f4c6bcb69c449 verified in solidity - assert.Equal(t, "f5894a8e9281b5687c0c7757d3d45fb76152bf659e6e61b1062f4c6bcb69c449", hex.EncodeToString(hash[:])) + // 0xd0c8a7a362a45a875d9eb78ef577d563d759e3a615a5f81f71bfc5e85f6bcf59 verified in solidity + assert.Equal(t, "d0c8a7a362a45a875d9eb78ef577d563d759e3a615a5f81f71bfc5e85f6bcf59", hex.EncodeToString(hash[:])) } func TestBlobKeyFromHeader(t *testing.T) { @@ -45,15 +46,15 @@ func TestBlobKeyFromHeader(t *testing.T) { QuorumNumbers: []core.QuorumID{0, 1}, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x123", - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100), }, Signature: []byte{1, 2, 3}, } blobKey, err := bh.BlobKey() assert.NoError(t, err) - // 0x40efb7273649f39590b27550ea06eeb81efd6ae4d719385a302fbd93173a395d verified in solidity - assert.Equal(t, "40efb7273649f39590b27550ea06eeb81efd6ae4d719385a302fbd93173a395d", blobKey.Hex()) + // 0x1354b29d9dd9a332959795d17f456c219566417fdbf1a7b4f5d118f5c2a36bbd verified in solidity + assert.Equal(t, "1354b29d9dd9a332959795d17f456c219566417fdbf1a7b4f5d118f5c2a36bbd", blobKey.Hex()) } func TestBatchHeaderHash(t *testing.T) { @@ -99,7 +100,7 @@ func TestBlobCertHash(t *testing.T) { QuorumNumbers: []core.QuorumID{0, 1}, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x123", - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100), }, Signature: []byte{1, 2, 3}, @@ -109,8 +110,8 @@ func TestBlobCertHash(t *testing.T) { hash, err := blobCert.Hash() assert.NoError(t, err) - // 0x3719a91e2a294feafdd624c1c88a6f1db1a5c79ee0863b352255bc9162f02751 verified in solidity - assert.Equal(t, "3719a91e2a294feafdd624c1c88a6f1db1a5c79ee0863b352255bc9162f02751", hex.EncodeToString(hash[:])) + // 0xad938e477d0bc1f9f4e8de7c5cd837560bdbb2dc7094207a7ad53e7442611a43 verified in solidity + assert.Equal(t, "ad938e477d0bc1f9f4e8de7c5cd837560bdbb2dc7094207a7ad53e7442611a43", hex.EncodeToString(hash[:])) } func TestBlobCertSerialization(t *testing.T) { @@ -127,7 +128,7 @@ func TestBlobCertSerialization(t *testing.T) { QuorumNumbers: []core.QuorumID{0, 1}, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x123", - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100), }, Signature: []byte{1, 2, 3}, diff --git a/core/v2/types.go b/core/v2/types.go index cc34b8ccf..90cb5524a 100644 --- a/core/v2/types.go +++ b/core/v2/types.go @@ -4,7 +4,6 @@ import ( "encoding/hex" "errors" "fmt" - "math/big" "strings" commonpb "github.com/Layr-Labs/eigenda/api/grpc/common/v2" @@ -109,11 +108,7 @@ func BlobHeaderFromProtobuf(proto *commonpb.BlobHeader) (*BlobHeader, error) { quorumNumbers[i] = core.QuorumID(q) } - paymentMetadata := core.PaymentMetadata{ - AccountID: proto.GetPaymentHeader().GetAccountId(), - BinIndex: proto.GetPaymentHeader().GetBinIndex(), - CumulativePayment: new(big.Int).SetBytes(proto.GetPaymentHeader().GetCumulativePayment()), - } + paymentMetadata := core.ConvertToPaymentMetadata(proto.GetPaymentHeader()) return &BlobHeader{ BlobVersion: BlobVersion(proto.GetVersion()), @@ -124,7 +119,7 @@ func BlobHeaderFromProtobuf(proto *commonpb.BlobHeader) (*BlobHeader, error) { Length: uint(proto.GetCommitment().GetLength()), }, QuorumNumbers: quorumNumbers, - PaymentMetadata: paymentMetadata, + PaymentMetadata: *paymentMetadata, Signature: proto.GetSignature(), }, nil } diff --git a/core/v2/types_test.go b/core/v2/types_test.go index a5e25f0d1..8647ae5b5 100644 --- a/core/v2/types_test.go +++ b/core/v2/types_test.go @@ -23,7 +23,7 @@ func TestConvertBatchToFromProtobuf(t *testing.T) { QuorumNumbers: []core.QuorumID{0, 1}, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x123", - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100), }, Signature: []byte{1, 2, 3}, @@ -34,7 +34,7 @@ func TestConvertBatchToFromProtobuf(t *testing.T) { QuorumNumbers: []core.QuorumID{0, 1}, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x456", - BinIndex: 6, + ReservationPeriod: 6, CumulativePayment: big.NewInt(200), }, Signature: []byte{1, 2, 3}, @@ -79,7 +79,7 @@ func TestConvertBlobHeaderToFromProtobuf(t *testing.T) { QuorumNumbers: []core.QuorumID{0, 1}, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x123", - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100), }, Signature: []byte{1, 2, 3}, @@ -107,7 +107,7 @@ func TestConvertBlobCertToFromProtobuf(t *testing.T) { QuorumNumbers: []core.QuorumID{0, 1}, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x123", - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100), }, Signature: []byte{1, 2, 3}, diff --git a/disperser/apiserver/disperse_blob_v2.go b/disperser/apiserver/disperse_blob_v2.go index 230d2a82c..c5999a4d6 100644 --- a/disperser/apiserver/disperse_blob_v2.go +++ b/disperser/apiserver/disperse_blob_v2.go @@ -127,19 +127,19 @@ func (s *DispersalServerV2) validateDispersalRequest(ctx context.Context, req *p return api.NewErrorInvalidArg(fmt.Sprintf("authentication failed: %s", err.Error())) } - if len(blobHeader.PaymentMetadata.AccountID) == 0 || blobHeader.PaymentMetadata.BinIndex == 0 || blobHeader.PaymentMetadata.CumulativePayment == nil { + if len(blobHeader.PaymentMetadata.AccountID) == 0 || blobHeader.PaymentMetadata.ReservationPeriod == 0 || blobHeader.PaymentMetadata.CumulativePayment == nil { return api.NewErrorInvalidArg("invalid payment metadata") } // handle payments and check rate limits if blobHeaderProto.GetPaymentHeader() != nil { - binIndex := blobHeaderProto.GetPaymentHeader().GetBinIndex() + reservationPeriod := blobHeaderProto.GetPaymentHeader().GetReservationPeriod() cumulativePayment := new(big.Int).SetBytes(blobHeaderProto.GetPaymentHeader().GetCumulativePayment()) accountID := blobHeaderProto.GetPaymentHeader().GetAccountId() paymentHeader := core.PaymentMetadata{ AccountID: accountID, - BinIndex: binIndex, + ReservationPeriod: reservationPeriod, CumulativePayment: cumulativePayment, } diff --git a/disperser/apiserver/server_v2.go b/disperser/apiserver/server_v2.go index 877598a3f..e808a813c 100644 --- a/disperser/apiserver/server_v2.go +++ b/disperser/apiserver/server_v2.go @@ -250,8 +250,8 @@ func (s *DispersalServerV2) GetPaymentState(ctx context.Context, req *pb.GetPaym // 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) + currentReservationPeriod := meterer.GetReservationPeriod(now, reservationWindow) + binRecords, err := s.meterer.OffchainStore.GetBinRecords(ctx, req.AccountId, currentReservationPeriod) if err != nil { return nil, api.NewErrorNotFound("failed to get active reservation") } @@ -280,7 +280,6 @@ func (s *DispersalServerV2) GetPaymentState(ctx context.Context, req *pb.GetPaym for i, v := range reservation.QuorumNumbers { quorumNumbers[i] = uint32(v) } - quorumSplits := make([]uint32, len(reservation.QuorumSplits)) for i, v := range reservation.QuorumSplits { quorumSplits[i] = uint32(v) @@ -293,8 +292,8 @@ func (s *DispersalServerV2) GetPaymentState(ctx context.Context, req *pb.GetPaym SymbolsPerSecond: reservation.SymbolsPerSecond, StartTimestamp: uint32(reservation.StartTimestamp), EndTimestamp: uint32(reservation.EndTimestamp), - QuorumNumbers: quorumNumbers, QuorumSplits: quorumSplits, + QuorumNumbers: quorumNumbers, }, CumulativePayment: largestCumulativePayment.Bytes(), OnchainCumulativePayment: onDemandPayment.CumulativePayment.Bytes(), diff --git a/disperser/apiserver/server_v2_test.go b/disperser/apiserver/server_v2_test.go index 1d9d76e4e..e0398c086 100644 --- a/disperser/apiserver/server_v2_test.go +++ b/disperser/apiserver/server_v2_test.go @@ -66,7 +66,7 @@ func TestV2DisperseBlob(t *testing.T) { Commitment: commitmentProto, PaymentHeader: &pbcommon.PaymentHeader{ AccountId: accountID, - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100).Bytes(), }, } @@ -124,7 +124,7 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) { QuorumNumbers: []uint32{0, 1}, PaymentHeader: &pbcommon.PaymentHeader{ AccountId: accountID, - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100).Bytes(), }, } @@ -143,7 +143,7 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) { Commitment: commitmentProto, PaymentHeader: &pbcommon.PaymentHeader{ AccountId: accountID, - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100).Bytes(), }, } @@ -160,7 +160,7 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) { Commitment: commitmentProto, PaymentHeader: &pbcommon.PaymentHeader{ AccountId: accountID, - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100).Bytes(), }, } @@ -177,7 +177,7 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) { Commitment: commitmentProto, PaymentHeader: &pbcommon.PaymentHeader{ AccountId: accountID, - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100).Bytes(), }, } @@ -194,7 +194,7 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) { Commitment: commitmentProto, PaymentHeader: &pbcommon.PaymentHeader{ AccountId: accountID, - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100).Bytes(), }, Signature: []byte{1, 2, 3}, @@ -212,7 +212,7 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) { Commitment: commitmentProto, PaymentHeader: &pbcommon.PaymentHeader{ AccountId: accountID, - BinIndex: 0, + ReservationPeriod: 0, CumulativePayment: big.NewInt(100).Bytes(), }, } @@ -237,7 +237,7 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) { Commitment: invalidCommitment, PaymentHeader: &pbcommon.PaymentHeader{ AccountId: accountID, - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100).Bytes(), }, } @@ -267,7 +267,7 @@ func TestV2DisperseBlobRequestValidation(t *testing.T) { Commitment: commitmentProto, PaymentHeader: &pbcommon.PaymentHeader{ AccountId: accountID, - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100).Bytes(), }, } @@ -293,7 +293,7 @@ func TestV2GetBlobStatus(t *testing.T) { QuorumNumbers: []core.QuorumID{0}, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x1234", - BinIndex: 0, + ReservationPeriod: 0, CumulativePayment: big.NewInt(532), }, } diff --git a/disperser/common/v2/blobstore/dynamo_metadata_store_test.go b/disperser/common/v2/blobstore/dynamo_metadata_store_test.go index 081b1d38d..c2887b21a 100644 --- a/disperser/common/v2/blobstore/dynamo_metadata_store_test.go +++ b/disperser/common/v2/blobstore/dynamo_metadata_store_test.go @@ -205,7 +205,7 @@ func TestBlobMetadataStoreCerts(t *testing.T) { BlobCommitments: mockCommitment, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x123", - BinIndex: uint32(i), + ReservationPeriod: uint32(i), CumulativePayment: big.NewInt(321), }, Signature: []byte("signature"), @@ -223,14 +223,14 @@ func TestBlobMetadataStoreCerts(t *testing.T) { assert.NoError(t, err) assert.Len(t, certs, numCerts) assert.Len(t, fragmentInfos, numCerts) - binIndexes := make(map[uint32]struct{}) + reservationPeriodes := make(map[uint32]struct{}) for i := 0; i < numCerts; i++ { assert.Equal(t, fragmentInfos[i], fragmentInfo) - binIndexes[certs[i].BlobHeader.PaymentMetadata.BinIndex] = struct{}{} + reservationPeriodes[certs[i].BlobHeader.PaymentMetadata.ReservationPeriod] = struct{}{} } - assert.Len(t, binIndexes, numCerts) + assert.Len(t, reservationPeriodes, numCerts) for i := 0; i < numCerts; i++ { - assert.Contains(t, binIndexes, uint32(i)) + assert.Contains(t, reservationPeriodes, uint32(i)) } deleteItems(t, []commondynamodb.Key{ @@ -502,7 +502,7 @@ func newBlob(t *testing.T) (corev2.BlobKey, *corev2.BlobHeader) { _, err := rand.Read(accountBytes) require.NoError(t, err) accountID := hex.EncodeToString(accountBytes) - binIndex, err := rand.Int(rand.Reader, big.NewInt(256)) + reservationPeriod, err := rand.Int(rand.Reader, big.NewInt(256)) require.NoError(t, err) cumulativePayment, err := rand.Int(rand.Reader, big.NewInt(1024)) require.NoError(t, err) @@ -515,7 +515,7 @@ func newBlob(t *testing.T) (corev2.BlobKey, *corev2.BlobHeader) { BlobCommitments: mockCommitment, PaymentMetadata: core.PaymentMetadata{ AccountID: accountID, - BinIndex: uint32(binIndex.Int64()), + ReservationPeriod: uint32(reservationPeriod.Int64()), CumulativePayment: cumulativePayment, }, Signature: sig, diff --git a/disperser/controller/controller_test.go b/disperser/controller/controller_test.go index dd1f0efa8..46954cec9 100644 --- a/disperser/controller/controller_test.go +++ b/disperser/controller/controller_test.go @@ -159,7 +159,7 @@ func newBlob(t *testing.T) (corev2.BlobKey, *corev2.BlobHeader) { _, err := rand.Read(accountBytes) require.NoError(t, err) accountID := hex.EncodeToString(accountBytes) - binIndex, err := rand.Int(rand.Reader, big.NewInt(256)) + reservationPeriod, err := rand.Int(rand.Reader, big.NewInt(256)) require.NoError(t, err) cumulativePayment, err := rand.Int(rand.Reader, big.NewInt(1024)) require.NoError(t, err) @@ -172,7 +172,7 @@ func newBlob(t *testing.T) (corev2.BlobKey, *corev2.BlobHeader) { BlobCommitments: mockCommitment, PaymentMetadata: core.PaymentMetadata{ AccountID: accountID, - BinIndex: uint32(binIndex.Int64()), + ReservationPeriod: uint32(reservationPeriod.Int64()), CumulativePayment: cumulativePayment, }, Signature: sig, diff --git a/disperser/controller/dispatcher_test.go b/disperser/controller/dispatcher_test.go index 84ba68198..cdc507cfd 100644 --- a/disperser/controller/dispatcher_test.go +++ b/disperser/controller/dispatcher_test.go @@ -203,7 +203,7 @@ func TestDispatcherBuildMerkleTree(t *testing.T) { BlobCommitments: mockCommitment, PaymentMetadata: core.PaymentMetadata{ AccountID: "account 1", - BinIndex: 0, + ReservationPeriod: 0, CumulativePayment: big.NewInt(532), }, Signature: []byte("signature"), @@ -217,7 +217,7 @@ func TestDispatcherBuildMerkleTree(t *testing.T) { BlobCommitments: mockCommitment, PaymentMetadata: core.PaymentMetadata{ AccountID: "account 2", - BinIndex: 0, + ReservationPeriod: 0, CumulativePayment: big.NewInt(532), }, Signature: []byte("signature"), diff --git a/disperser/dataapi/server_v2_test.go b/disperser/dataapi/server_v2_test.go index 23cc9abf7..1e1da4114 100644 --- a/disperser/dataapi/server_v2_test.go +++ b/disperser/dataapi/server_v2_test.go @@ -132,7 +132,9 @@ func makeBlobHeaderV2(t *testing.T) *corev2.BlobHeader { _, err := rand.Read(accountBytes) require.NoError(t, err) accountID := hex.EncodeToString(accountBytes) - binIndex, err := rand.Int(rand.Reader, big.NewInt(42)) + reservationPeriod, err := rand.Int(rand.Reader, big.NewInt(42)) + require.NoError(t, err) + salt, err := rand.Int(rand.Reader, big.NewInt(1000)) require.NoError(t, err) cumulativePayment, err := rand.Int(rand.Reader, big.NewInt(123)) require.NoError(t, err) @@ -145,8 +147,9 @@ func makeBlobHeaderV2(t *testing.T) *corev2.BlobHeader { BlobCommitments: makeCommitment(t), PaymentMetadata: core.PaymentMetadata{ AccountID: accountID, - BinIndex: uint32(binIndex.Int64()), + ReservationPeriod: uint32(reservationPeriod.Int64()), CumulativePayment: cumulativePayment, + Salt: uint32(salt.Int64()), }, Signature: sig, } @@ -190,7 +193,7 @@ func TestFetchBlobHandlerV2(t *testing.T) { assert.Equal(t, uint8(0), response.BlobHeader.BlobVersion) assert.Equal(t, blobHeader.Signature, response.BlobHeader.Signature) assert.Equal(t, blobHeader.PaymentMetadata.AccountID, response.BlobHeader.PaymentMetadata.AccountID) - assert.Equal(t, blobHeader.PaymentMetadata.BinIndex, response.BlobHeader.PaymentMetadata.BinIndex) + assert.Equal(t, blobHeader.PaymentMetadata.ReservationPeriod, response.BlobHeader.PaymentMetadata.ReservationPeriod) assert.Equal(t, blobHeader.PaymentMetadata.CumulativePayment, response.BlobHeader.PaymentMetadata.CumulativePayment) } diff --git a/disperser/encoder/server_v2_test.go b/disperser/encoder/server_v2_test.go index 83dcb2af1..15d13a906 100644 --- a/disperser/encoder/server_v2_test.go +++ b/disperser/encoder/server_v2_test.go @@ -198,7 +198,7 @@ func createTestBlobHeader(t *testing.T) *corev2.BlobHeader { BlobCommitments: mockCommitment, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x1234", - BinIndex: 0, + ReservationPeriod: 0, CumulativePayment: big.NewInt(532), }, } diff --git a/node/mock/testdata.go b/node/mock/testdata.go index ae4805142..859862782 100644 --- a/node/mock/testdata.go +++ b/node/mock/testdata.go @@ -21,7 +21,7 @@ func MockBatch(t *testing.T) ([]v2.BlobKey, *v2.Batch, []map[core.QuorumID]core. QuorumNumbers: []core.QuorumID{0, 1}, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x123", - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100), }, Signature: []byte{1, 2, 3}, @@ -32,7 +32,7 @@ func MockBatch(t *testing.T) ([]v2.BlobKey, *v2.Batch, []map[core.QuorumID]core. QuorumNumbers: []core.QuorumID{0, 1}, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x456", - BinIndex: 6, + ReservationPeriod: 6, CumulativePayment: big.NewInt(200), }, Signature: []byte{1, 2, 3}, @@ -43,7 +43,7 @@ func MockBatch(t *testing.T) ([]v2.BlobKey, *v2.Batch, []map[core.QuorumID]core. QuorumNumbers: []core.QuorumID{1, 2}, PaymentMetadata: core.PaymentMetadata{ AccountID: "0x789", - BinIndex: 7, + ReservationPeriod: 7, CumulativePayment: big.NewInt(300), }, Signature: []byte{1, 2, 3}, diff --git a/relay/relay_test_utils.go b/relay/relay_test_utils.go index 28051afb2..375e823b6 100644 --- a/relay/relay_test_utils.go +++ b/relay/relay_test_utils.go @@ -3,6 +3,14 @@ package relay import ( "context" "fmt" + "log" + "math/big" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + pbcommon "github.com/Layr-Labs/eigenda/api/grpc/common" pbcommonv2 "github.com/Layr-Labs/eigenda/api/grpc/common/v2" "github.com/Layr-Labs/eigenda/common" @@ -27,13 +35,6 @@ import ( "github.com/ory/dockertest/v3" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "log" - "math/big" - "os" - "path/filepath" - "runtime" - "strings" - "testing" ) var ( @@ -211,7 +212,7 @@ func randomBlob(t *testing.T) (*v2.BlobHeader, []byte) { Commitment: commitmentProto, PaymentHeader: &pbcommon.PaymentHeader{ AccountId: tu.RandomString(10), - BinIndex: 5, + ReservationPeriod: 5, CumulativePayment: big.NewInt(100).Bytes(), }, }