From 6400f30d9cecd474308068c0efb7d8e4165153e6 Mon Sep 17 00:00:00 2001 From: hopeyen Date: Fri, 3 Jan 2025 07:53:00 -0800 Subject: [PATCH 1/4] feat: periodic pruning of payment tables --- api/clients/v2/accountant.go | 78 +++++------ api/clients/v2/accountant_test.go | 30 ++-- api/clients/v2/disperser_client.go | 2 +- api/docs/disperser_v2.html | 74 +++++----- api/docs/disperser_v2.md | 38 +++--- api/docs/eigenda-protos.html | 74 +++++----- api/docs/eigenda-protos.md | 38 +++--- api/grpc/disperser/v2/disperser_v2.pb.go | 72 +++++----- api/proto/disperser/v2/disperser_v2.proto | 6 +- core/meterer/meterer.go | 79 +++++++---- core/meterer/meterer_test.go | 20 +-- core/meterer/offchain_store.go | 159 +++++++++++++++++----- core/meterer/offchain_store_test.go | 29 ++-- disperser/apiserver/server_test.go | 7 +- disperser/apiserver/server_v2.go | 7 +- disperser/apiserver/server_v2_test.go | 8 +- disperser/cmd/apiserver/config.go | 8 +- disperser/cmd/apiserver/flags/flags.go | 26 +++- disperser/cmd/apiserver/main.go | 7 +- inabox/deploy/config.go | 4 + inabox/deploy/env_vars.go | 4 + test/integration_test.go | 7 +- 22 files changed, 466 insertions(+), 311 deletions(-) diff --git a/api/clients/v2/accountant.go b/api/clients/v2/accountant.go index 02dd58d9f6..31f8863fd1 100644 --- a/api/clients/v2/accountant.go +++ b/api/clients/v2/accountant.go @@ -26,15 +26,15 @@ type Accountant struct { // local accounting // contains 3 bins; circular wrapping of indices - binRecords []BinRecord - usageLock sync.Mutex - cumulativePayment *big.Int + reservationPeriodRecords []ReservationPeriodRecord + usageLock sync.Mutex + cumulativePayment *big.Int - // number of bins in the circular accounting, restricted by minNumBins which is 3 + // number of bins in the circular accounting, restricted by MinNumPeriods which is 3 numBins uint32 } -type BinRecord struct { +type ReservationPeriodRecord struct { Index uint32 Usage uint64 } @@ -43,20 +43,20 @@ func NewAccountant(accountID string, reservation *core.ReservedPayment, onDemand //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 - binRecords := make([]BinRecord, numBins) - for i := range binRecords { - binRecords[i] = BinRecord{Index: uint32(i), Usage: 0} + reservationPeriodRecords := make([]ReservationPeriodRecord, numBins) + for i := range reservationPeriodRecords { + reservationPeriodRecords[i] = ReservationPeriodRecord{Index: uint32(i), Usage: 0} } a := Accountant{ - accountID: accountID, - reservation: reservation, - onDemand: onDemand, - reservationWindow: reservationWindow, - pricePerSymbol: pricePerSymbol, - minNumSymbols: minNumSymbols, - binRecords: binRecords, - cumulativePayment: big.NewInt(0), - numBins: max(numBins, uint32(meterer.MinNumBins)), + accountID: accountID, + reservation: reservation, + onDemand: onDemand, + reservationWindow: reservationWindow, + pricePerSymbol: pricePerSymbol, + minNumSymbols: minNumSymbols, + reservationPeriodRecords: reservationPeriodRecords, + cumulativePayment: big.NewInt(0), + numBins: max(numBins, uint32(meterer.MinNumPeriods)), } // TODO: add a routine to refresh the on-chain state occasionally? return &a @@ -67,39 +67,39 @@ func NewAccountant(accountID string, reservation *core.ReservedPayment, onDemand // then on-demand if the reservation is not available. The returned values are // reservation period for reservation payments and cumulative payment for on-demand payments, // and both fields are used to create the payment header and signature -func (a *Accountant) BlobPaymentInfo(ctx context.Context, numSymbols uint32, quorumNumbers []uint8) (uint32, *big.Int, error) { +func (a *Accountant) BlobPaymentInfo(ctx context.Context, numSymbols uint64, quorumNumbers []uint8) (uint32, *big.Int, error) { now := time.Now().Unix() currentReservationPeriod := meterer.GetReservationPeriod(uint64(now), a.reservationWindow) - symbolUsage := uint64(a.SymbolsCharged(numSymbols)) + symbolUsage := uint64(a.SymbolsCharged(uint32(numSymbols))) a.usageLock.Lock() defer a.usageLock.Unlock() - relativeBinRecord := a.GetRelativeBinRecord(currentReservationPeriod) - relativeBinRecord.Usage += symbolUsage + relativeReservationPeriodRecord := a.GetRelativeReservationPeriodRecord(currentReservationPeriod) + relativeReservationPeriodRecord.Usage += symbolUsage // first attempt to use the active reservation binLimit := a.reservation.SymbolsPerSecond * uint64(a.reservationWindow) - if relativeBinRecord.Usage <= binLimit { + if relativeReservationPeriodRecord.Usage <= binLimit { if err := QuorumCheck(quorumNumbers, a.reservation.QuorumNumbers); err != nil { return 0, big.NewInt(0), err } return currentReservationPeriod, big.NewInt(0), nil } - overflowBinRecord := a.GetRelativeBinRecord(currentReservationPeriod + 2) + overflowReservationPeriodRecord := a.GetRelativeReservationPeriodRecord(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-symbolUsage < binLimit && symbolUsage <= binLimit { - overflowBinRecord.Usage += relativeBinRecord.Usage - binLimit + if overflowReservationPeriodRecord.Usage == 0 && relativeReservationPeriodRecord.Usage-symbolUsage < binLimit && symbolUsage <= binLimit { + overflowReservationPeriodRecord.Usage += relativeReservationPeriodRecord.Usage - binLimit if err := QuorumCheck(quorumNumbers, a.reservation.QuorumNumbers); err != nil { return 0, big.NewInt(0), err } return currentReservationPeriod, big.NewInt(0), nil } - // reservation not available, rollback reservation records, attempt on-demand - //todo: rollback on-demand if disperser respond with some type of rejection? - relativeBinRecord.Usage -= symbolUsage - incrementRequired := big.NewInt(int64(a.PaymentCharged(numSymbols))) + // reservation not available, attempt on-demand + //todo: rollback later if disperser respond with some type of rejection? + relativeReservationPeriodRecord.Usage -= symbolUsage + incrementRequired := big.NewInt(int64(a.PaymentCharged(uint32(numSymbols)))) a.cumulativePayment.Add(a.cumulativePayment, incrementRequired) if a.cumulativePayment.Cmp(a.onDemand.CumulativePayment) <= 0 { if err := QuorumCheck(quorumNumbers, requiredQuorums); err != nil { @@ -111,7 +111,7 @@ func (a *Accountant) BlobPaymentInfo(ctx context.Context, numSymbols uint32, quo } // AccountBlob accountant provides and records payment information -func (a *Accountant) AccountBlob(ctx context.Context, numSymbols uint32, quorums []uint8, salt uint32) (*core.PaymentMetadata, error) { +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 @@ -143,16 +143,16 @@ func (a *Accountant) SymbolsCharged(numSymbols uint32) uint32 { return uint32(core.RoundUpDivide(uint(numSymbols), uint(a.minNumSymbols))) * a.minNumSymbols } -func (a *Accountant) GetRelativeBinRecord(index uint32) *BinRecord { +func (a *Accountant) GetRelativeReservationPeriodRecord(index uint32) *ReservationPeriodRecord { relativeIndex := index % a.numBins - if a.binRecords[relativeIndex].Index != uint32(index) { - a.binRecords[relativeIndex] = BinRecord{ + if a.reservationPeriodRecords[relativeIndex].Index != uint32(index) { + a.reservationPeriodRecords[relativeIndex] = ReservationPeriodRecord{ Index: uint32(index), Usage: 0, } } - return &a.binRecords[relativeIndex] + return &a.reservationPeriodRecords[relativeIndex] } // SetPaymentState sets the accountant's state from the disperser's response @@ -214,18 +214,18 @@ func (a *Accountant) SetPaymentState(paymentState *disperser_rpc.GetPaymentState } } - binRecords := make([]BinRecord, len(paymentState.GetBinRecords())) - for i, record := range paymentState.GetBinRecords() { + reservationPeriodRecords := make([]ReservationPeriodRecord, len(paymentState.GetReservationPeriodRecords())) + for i, record := range paymentState.GetReservationPeriodRecords() { if record == nil { - binRecords[i] = BinRecord{Index: 0, Usage: 0} + reservationPeriodRecords[i] = ReservationPeriodRecord{Index: 0, Usage: 0} } else { - binRecords[i] = BinRecord{ + reservationPeriodRecords[i] = ReservationPeriodRecord{ Index: record.Index, Usage: record.Usage, } } } - a.binRecords = binRecords + a.reservationPeriodRecords = reservationPeriodRecords return nil } diff --git a/api/clients/v2/accountant_test.go b/api/clients/v2/accountant_test.go index ac8672ff5f..1bbd64a209 100644 --- a/api/clients/v2/accountant_test.go +++ b/api/clients/v2/accountant_test.go @@ -43,7 +43,7 @@ func TestNewAccountant(t *testing.T) { assert.Equal(t, reservationWindow, accountant.reservationWindow) assert.Equal(t, pricePerSymbol, accountant.pricePerSymbol) assert.Equal(t, minNumSymbols, accountant.minNumSymbols) - assert.Equal(t, []BinRecord{{Index: 0, Usage: 0}, {Index: 1, Usage: 0}, {Index: 2, Usage: 0}}, accountant.binRecords) + assert.Equal(t, []ReservationPeriodRecord{{Index: 0, Usage: 0}, {Index: 1, Usage: 0}, {Index: 2, Usage: 0}}, accountant.reservationPeriodRecords) assert.Equal(t, big.NewInt(0), accountant.cumulativePayment) } @@ -76,7 +76,7 @@ func TestAccountBlob_Reservation(t *testing.T) { assert.NoError(t, err) 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) + assert.Equal(t, isRotation([]uint64{500, 0, 0}, mapRecordUsage(accountant.reservationPeriodRecords)), true) symbolLength = uint32(700) @@ -85,7 +85,7 @@ func TestAccountBlob_Reservation(t *testing.T) { assert.NoError(t, err) 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) + assert.Equal(t, isRotation([]uint64{1200, 0, 200}, mapRecordUsage(accountant.reservationPeriodRecords)), true) // Second call should use on-demand payment header, err = accountant.AccountBlob(ctx, 300, quorums, salt) @@ -125,7 +125,7 @@ func TestAccountBlob_OnDemand(t *testing.T) { expectedPayment := big.NewInt(int64(numSymbols * pricePerSymbol)) 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, isRotation([]uint64{0, 0, 0}, mapRecordUsage(accountant.reservationPeriodRecords)), true) assert.Equal(t, expectedPayment, accountant.cumulativePayment) } @@ -225,7 +225,7 @@ func TestAccountBlob_BinRotation(t *testing.T) { // First call _, err = accountant.AccountBlob(ctx, 800, quorums, salt) assert.NoError(t, err) - assert.Equal(t, isRotation([]uint64{800, 0, 0}, mapRecordUsage(accountant.binRecords)), true) + assert.Equal(t, isRotation([]uint64{800, 0, 0}, mapRecordUsage(accountant.reservationPeriodRecords)), true) // next reservation duration time.Sleep(1000 * time.Millisecond) @@ -233,12 +233,12 @@ func TestAccountBlob_BinRotation(t *testing.T) { // Second call _, err = accountant.AccountBlob(ctx, 300, quorums, salt) assert.NoError(t, err) - assert.Equal(t, isRotation([]uint64{800, 300, 0}, mapRecordUsage(accountant.binRecords)), true) + assert.Equal(t, isRotation([]uint64{800, 300, 0}, mapRecordUsage(accountant.reservationPeriodRecords)), true) // Third call _, err = accountant.AccountBlob(ctx, 500, quorums, salt) assert.NoError(t, err) - assert.Equal(t, isRotation([]uint64{800, 800, 0}, mapRecordUsage(accountant.binRecords)), true) + assert.Equal(t, isRotation([]uint64{800, 800, 0}, mapRecordUsage(accountant.reservationPeriodRecords)), true) } func TestConcurrentBinRotationAndAccountBlob(t *testing.T) { @@ -279,7 +279,7 @@ func TestConcurrentBinRotationAndAccountBlob(t *testing.T) { wg.Wait() // Check final state - usages := mapRecordUsage(accountant.binRecords) + usages := mapRecordUsage(accountant.reservationPeriodRecords) assert.Equal(t, uint64(1000), usages[0]+usages[1]+usages[2]) } @@ -313,14 +313,14 @@ func TestAccountBlob_ReservationWithOneOverflow(t *testing.T) { 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) + assert.Equal(t, isRotation([]uint64{800, 0, 0}, mapRecordUsage(accountant.reservationPeriodRecords)), true) // Second call: Allow one overflow 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) + assert.Equal(t, isRotation([]uint64{1300, 0, 300}, mapRecordUsage(accountant.reservationPeriodRecords)), true) // Third call: Should use on-demand payment header, err = accountant.AccountBlob(ctx, 200, quorums, salt+2) @@ -328,7 +328,7 @@ func TestAccountBlob_ReservationWithOneOverflow(t *testing.T) { 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) + assert.Equal(t, isRotation([]uint64{1300, 0, 300}, mapRecordUsage(accountant.reservationPeriodRecords)), true) } func TestAccountBlob_ReservationOverflowReset(t *testing.T) { @@ -357,12 +357,12 @@ func TestAccountBlob_ReservationOverflowReset(t *testing.T) { // full reservation _, err = accountant.AccountBlob(ctx, 1000, quorums, salt) assert.NoError(t, err) - assert.Equal(t, isRotation([]uint64{1000, 0, 0}, mapRecordUsage(accountant.binRecords)), true) + assert.Equal(t, isRotation([]uint64{1000, 0, 0}, mapRecordUsage(accountant.reservationPeriodRecords)), true) // no overflow 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, isRotation([]uint64{1000, 0, 0}, mapRecordUsage(accountant.reservationPeriodRecords)), true) assert.Equal(t, big.NewInt(500), header.CumulativePayment) // Wait for next reservation duration @@ -371,7 +371,7 @@ func TestAccountBlob_ReservationOverflowReset(t *testing.T) { // Third call: Should use new bin and allow overflow again _, err = accountant.AccountBlob(ctx, 500, quorums, salt) assert.NoError(t, err) - assert.Equal(t, isRotation([]uint64{1000, 500, 0}, mapRecordUsage(accountant.binRecords)), true) + assert.Equal(t, isRotation([]uint64{1000, 500, 0}, mapRecordUsage(accountant.reservationPeriodRecords)), true) } func TestQuorumCheck(t *testing.T) { @@ -431,7 +431,7 @@ func TestQuorumCheck(t *testing.T) { } } -func mapRecordUsage(records []BinRecord) []uint64 { +func mapRecordUsage(records []ReservationPeriodRecord) []uint64 { return []uint64{records[0].Usage, records[1].Usage, records[2].Usage} } diff --git a/api/clients/v2/disperser_client.go b/api/clients/v2/disperser_client.go index 3ef20404be..eb78c7da93 100644 --- a/api/clients/v2/disperser_client.go +++ b/api/clients/v2/disperser_client.go @@ -139,7 +139,7 @@ func (c *disperserClient) DisperseBlob( } symbolLength := encoding.GetBlobLengthPowerOf2(uint(len(data))) - payment, err := c.accountant.AccountBlob(ctx, uint32(symbolLength), quorums, salt) + 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_v2.html b/api/docs/disperser_v2.html index 50b3685d72..d975346a5d 100644 --- a/api/docs/disperser_v2.html +++ b/api/docs/disperser_v2.html @@ -182,10 +182,6 @@

Table of Contents

MAttestation -
  • - MBinRecord -
  • -
  • MBlobCommitmentReply
  • @@ -230,6 +226,10 @@

    Table of Contents

    MReservation +
  • + MReservationPeriodRecord +
  • +
  • MSignedBatch
  • @@ -321,37 +321,6 @@

    Attestation

    -

    BinRecord

    -

    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.

    - - - - - - - - - - - - - - - - - - - - - - - -
    FieldTypeLabelDescription
    indexuint32

    usageuint64

    - - - - -

    BlobCommitmentReply

    @@ -585,8 +554,8 @@

    GetPaymentStateReply

    - bin_records - BinRecord + reservation_period_records + ReservationPeriodRecord repeated

    off-chain account reservation usage records

    @@ -755,6 +724,37 @@

    Reservation

    +

    ReservationPeriodRecord

    +

    ReservationPeriodRecord 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.

    + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeLabelDescription
    indexuint32

    usageuint64

    + + + + +

    SignedBatch

    SignedBatch is a batch of blobs with a signature.

    diff --git a/api/docs/disperser_v2.md b/api/docs/disperser_v2.md index 0ca84f3b0d..5424a041bb 100644 --- a/api/docs/disperser_v2.md +++ b/api/docs/disperser_v2.md @@ -5,7 +5,6 @@ - [disperser/v2/disperser_v2.proto](#disperser_v2_disperser_v2-proto) - [Attestation](#disperser-v2-Attestation) - - [BinRecord](#disperser-v2-BinRecord) - [BlobCommitmentReply](#disperser-v2-BlobCommitmentReply) - [BlobCommitmentRequest](#disperser-v2-BlobCommitmentRequest) - [BlobStatusReply](#disperser-v2-BlobStatusReply) @@ -17,6 +16,7 @@ - [GetPaymentStateRequest](#disperser-v2-GetPaymentStateRequest) - [PaymentGlobalParams](#disperser-v2-PaymentGlobalParams) - [Reservation](#disperser-v2-Reservation) + - [ReservationPeriodRecord](#disperser-v2-ReservationPeriodRecord) - [SignedBatch](#disperser-v2-SignedBatch) - [BlobStatus](#disperser-v2-BlobStatus) @@ -54,23 +54,6 @@ - - -### BinRecord -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. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| index | [uint32](#uint32) | | | -| usage | [uint64](#uint64) | | | - - - - - - ### BlobCommitmentReply @@ -192,7 +175,7 @@ GetPaymentStateReply contains the payment state of an account. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | payment_global_params | [PaymentGlobalParams](#disperser-v2-PaymentGlobalParams) | | global payment vault parameters | -| bin_records | [BinRecord](#disperser-v2-BinRecord) | repeated | off-chain account reservation usage records | +| reservation_period_records | [ReservationPeriodRecord](#disperser-v2-ReservationPeriodRecord) | repeated | off-chain account reservation usage records | | reservation | [Reservation](#disperser-v2-Reservation) | | on-chain account reservation setting | | cumulative_payment | [bytes](#bytes) | | off-chain on-demand payment usage | | onchain_cumulative_payment | [bytes](#bytes) | | on-chain on-demand payment deposited | @@ -256,6 +239,23 @@ GetPaymentStateRequest contains parameters to query the payment state of an acco + + +### ReservationPeriodRecord +ReservationPeriodRecord 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. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| index | [uint32](#uint32) | | | +| usage | [uint64](#uint64) | | | + + + + + + ### SignedBatch diff --git a/api/docs/eigenda-protos.html b/api/docs/eigenda-protos.html index 44474db3c8..53e5fcd704 100644 --- a/api/docs/eigenda-protos.html +++ b/api/docs/eigenda-protos.html @@ -350,10 +350,6 @@

    Table of Contents

    MAttestation -
  • - MBinRecord -
  • -
  • MBlobCommitmentReply
  • @@ -398,6 +394,10 @@

    Table of Contents

    MReservation +
  • + MReservationPeriodRecord +
  • +
  • MSignedBatch
  • @@ -1992,37 +1992,6 @@

    Attestation

    -

    BinRecord

    -

    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.

    - - - - - - - - - - - - - - - - - - - - - - - -
    FieldTypeLabelDescription
    indexuint32

    usageuint64

    - - - - -

    BlobCommitmentReply

    @@ -2256,8 +2225,8 @@

    GetPaymentStateReply

    - bin_records - BinRecord + reservation_period_records + ReservationPeriodRecord repeated

    off-chain account reservation usage records

    @@ -2426,6 +2395,37 @@

    Reservation

    +

    ReservationPeriodRecord

    +

    ReservationPeriodRecord 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.

    + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeLabelDescription
    indexuint32

    usageuint64

    + + + + +

    SignedBatch

    SignedBatch is a batch of blobs with a signature.

    diff --git a/api/docs/eigenda-protos.md b/api/docs/eigenda-protos.md index 60d2a21c90..b49dbf0d08 100644 --- a/api/docs/eigenda-protos.md +++ b/api/docs/eigenda-protos.md @@ -47,7 +47,6 @@ - [disperser/v2/disperser_v2.proto](#disperser_v2_disperser_v2-proto) - [Attestation](#disperser-v2-Attestation) - - [BinRecord](#disperser-v2-BinRecord) - [BlobCommitmentReply](#disperser-v2-BlobCommitmentReply) - [BlobCommitmentRequest](#disperser-v2-BlobCommitmentRequest) - [BlobStatusReply](#disperser-v2-BlobStatusReply) @@ -59,6 +58,7 @@ - [GetPaymentStateRequest](#disperser-v2-GetPaymentStateRequest) - [PaymentGlobalParams](#disperser-v2-PaymentGlobalParams) - [Reservation](#disperser-v2-Reservation) + - [ReservationPeriodRecord](#disperser-v2-ReservationPeriodRecord) - [SignedBatch](#disperser-v2-SignedBatch) - [BlobStatus](#disperser-v2-BlobStatus) @@ -756,23 +756,6 @@ If DisperseBlob returns the following error codes: INVALID_ARGUMENT (400): reque - - -### BinRecord -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. - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| index | [uint32](#uint32) | | | -| usage | [uint64](#uint64) | | | - - - - - - ### BlobCommitmentReply @@ -894,7 +877,7 @@ GetPaymentStateReply contains the payment state of an account. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | payment_global_params | [PaymentGlobalParams](#disperser-v2-PaymentGlobalParams) | | global payment vault parameters | -| bin_records | [BinRecord](#disperser-v2-BinRecord) | repeated | off-chain account reservation usage records | +| reservation_period_records | [ReservationPeriodRecord](#disperser-v2-ReservationPeriodRecord) | repeated | off-chain account reservation usage records | | reservation | [Reservation](#disperser-v2-Reservation) | | on-chain account reservation setting | | cumulative_payment | [bytes](#bytes) | | off-chain on-demand payment usage | | onchain_cumulative_payment | [bytes](#bytes) | | on-chain on-demand payment deposited | @@ -958,6 +941,23 @@ GetPaymentStateRequest contains parameters to query the payment state of an acco + + +### ReservationPeriodRecord +ReservationPeriodRecord 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. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| index | [uint32](#uint32) | | | +| usage | [uint64](#uint64) | | | + + + + + + ### SignedBatch diff --git a/api/grpc/disperser/v2/disperser_v2.pb.go b/api/grpc/disperser/v2/disperser_v2.pb.go index 4fb9718f65..04ff63e64d 100644 --- a/api/grpc/disperser/v2/disperser_v2.pb.go +++ b/api/grpc/disperser/v2/disperser_v2.pb.go @@ -488,7 +488,7 @@ type GetPaymentStateReply struct { // 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"` + ReservationPeriodRecords []*ReservationPeriodRecord `protobuf:"bytes,2,rep,name=reservation_period_records,json=reservationPeriodRecords,proto3" json:"reservation_period_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 @@ -536,9 +536,9 @@ func (x *GetPaymentStateReply) GetPaymentGlobalParams() *PaymentGlobalParams { return nil } -func (x *GetPaymentStateReply) GetBinRecords() []*BinRecord { +func (x *GetPaymentStateReply) GetReservationPeriodRecords() []*ReservationPeriodRecord { if x != nil { - return x.BinRecords + return x.ReservationPeriodRecords } return nil } @@ -941,9 +941,9 @@ func (x *Reservation) GetQuorumSplits() []uint32 { return nil } -// BinRecord is the usage record of an account in a bin. The API should return the active bin +// ReservationPeriodRecord 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 { +type ReservationPeriodRecord struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -952,8 +952,8 @@ type BinRecord struct { Usage uint64 `protobuf:"varint,2,opt,name=usage,proto3" json:"usage,omitempty"` } -func (x *BinRecord) Reset() { - *x = BinRecord{} +func (x *ReservationPeriodRecord) Reset() { + *x = ReservationPeriodRecord{} if protoimpl.UnsafeEnabled { mi := &file_disperser_v2_disperser_v2_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -961,13 +961,13 @@ func (x *BinRecord) Reset() { } } -func (x *BinRecord) String() string { +func (x *ReservationPeriodRecord) String() string { return protoimpl.X.MessageStringOf(x) } -func (*BinRecord) ProtoMessage() {} +func (*ReservationPeriodRecord) ProtoMessage() {} -func (x *BinRecord) ProtoReflect() protoreflect.Message { +func (x *ReservationPeriodRecord) ProtoReflect() protoreflect.Message { mi := &file_disperser_v2_disperser_v2_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -979,19 +979,19 @@ func (x *BinRecord) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use BinRecord.ProtoReflect.Descriptor instead. -func (*BinRecord) Descriptor() ([]byte, []int) { +// Deprecated: Use ReservationPeriodRecord.ProtoReflect.Descriptor instead. +func (*ReservationPeriodRecord) Descriptor() ([]byte, []int) { return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{13} } -func (x *BinRecord) GetIndex() uint32 { +func (x *ReservationPeriodRecord) GetIndex() uint32 { if x != nil { return x.Index } return 0 } -func (x *BinRecord) GetUsage() uint64 { +func (x *ReservationPeriodRecord) GetUsage() uint64 { if x != nil { return x.Usage } @@ -1049,7 +1049,7 @@ var file_disperser_v2_disperser_v2_proto_rawDesc = []byte{ 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, + 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xfc, 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, @@ -1189,25 +1189,25 @@ 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, 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 - (*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 + (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 + (*ReservationPeriodRecord)(nil), // 14: disperser.v2.ReservationPeriodRecord + (*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{ 15, // 0: disperser.v2.DisperseBlobRequest.blob_header:type_name -> common.v2.BlobHeader @@ -1217,7 +1217,7 @@ var file_disperser_v2_disperser_v2_proto_depIdxs = []int32{ 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 + 14, // 7: disperser.v2.GetPaymentStateReply.reservation_period_records:type_name -> disperser.v2.ReservationPeriodRecord 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 @@ -1400,7 +1400,7 @@ func file_disperser_v2_disperser_v2_proto_init() { } } file_disperser_v2_disperser_v2_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BinRecord); i { + switch v := v.(*ReservationPeriodRecord); i { case 0: return &v.state case 1: diff --git a/api/proto/disperser/v2/disperser_v2.proto b/api/proto/disperser/v2/disperser_v2.proto index 0cae0f0d1b..7765e5040b 100644 --- a/api/proto/disperser/v2/disperser_v2.proto +++ b/api/proto/disperser/v2/disperser_v2.proto @@ -79,7 +79,7 @@ message GetPaymentStateReply { // global payment vault parameters PaymentGlobalParams payment_global_params = 1; // off-chain account reservation usage records - repeated BinRecord bin_records = 2; + repeated ReservationPeriodRecord reservation_period_records = 2; // on-chain account reservation setting Reservation reservation = 3; // off-chain on-demand payment usage @@ -169,9 +169,9 @@ message Reservation { repeated uint32 quorum_splits = 5; } -// BinRecord is the usage record of an account in a bin. The API should return the active bin +// ReservationPeriodRecord 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 { +message ReservationPeriodRecord { uint32 index = 1; uint64 usage = 2; } diff --git a/core/meterer/meterer.go b/core/meterer/meterer.go index f9b9dfd3b7..5605507065 100644 --- a/core/meterer/meterer.go +++ b/core/meterer/meterer.go @@ -18,8 +18,10 @@ type Config struct { // ChainReadTimeout is the timeout for reading payment state from chain ChainReadTimeout time.Duration - // UpdateInterval is the interval for refreshing the on-chain state - UpdateInterval time.Duration + // OnchainUpdateInterval is the interval for refreshing the on-chain state + OnchainUpdateInterval time.Duration + // OffchainPruneInterval is the interval for pruning the off-chain state + OffchainPruneInterval time.Duration } // Meterer handles payment accounting across different accounts. Disperser API server receives requests from clients and each request contains a blob header @@ -51,10 +53,10 @@ func NewMeterer( } } -// Start starts to periodically refreshing the on-chain state +// Start starts to periodically refreshing the on-chain state and pruning the off-chain state func (m *Meterer) Start(ctx context.Context) { go func() { - ticker := time.NewTicker(m.UpdateInterval) + ticker := time.NewTicker(m.OnchainUpdateInterval) defer ticker.Stop() for { @@ -68,6 +70,23 @@ func (m *Meterer) Start(ctx context.Context) { } } }() + go func() { + ticker := time.NewTicker(m.OffchainPruneInterval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + now := uint64(time.Now().Unix()) + reservationWindow := m.ChainPaymentState.GetReservationWindow() + if err := m.OffchainStore.DeleteOldPeriods(ctx, GetReservationPeriod(now, reservationWindow)-uint32(MinNumPeriods)); err != nil { + m.logger.Error("Failed to prune off-chain state", "error", err) + } + case <-ctx.Done(): + return + } + } + }() } // MeterRequest validates a blob header and adds it to the meterer's state @@ -108,9 +127,9 @@ func (m *Meterer) ServeReservationRequest(ctx context.Context, header core.Payme return fmt.Errorf("invalid reservation period for reservation") } - // Update bin usage atomically and check against reservation's data rate as the bin limit - if err := m.IncrementBinUsage(ctx, header, reservation, numSymbols); err != nil { - return fmt.Errorf("bin overflows: %w", err) + // Update reservation period usage atomically and check against reservation's data rate as the period limit + if err := m.IncrementReservatiionPeriodUsage(ctx, header, reservation, numSymbols); err != nil { + return fmt.Errorf("period overflows: %w", err) } return nil @@ -140,46 +159,46 @@ func (m *Meterer) ValidateReservationPeriod(header core.PaymentMetadata, reserva now := uint64(time.Now().Unix()) reservationWindow := m.ChainPaymentState.GetReservationWindow() currentReservationPeriod := GetReservationPeriod(now, reservationWindow) - // Valid reservation periodes are either the current bin or the previous bin + // Valid reservation periods are either the current period or the previous period if (header.ReservationPeriod != currentReservationPeriod && header.ReservationPeriod != (currentReservationPeriod-1)) || (GetReservationPeriod(reservation.StartTimestamp, reservationWindow) > header.ReservationPeriod || header.ReservationPeriod > GetReservationPeriod(reservation.EndTimestamp, reservationWindow)) { return false } return true } -// IncrementBinUsage increments the bin usage atomically and checks for overflow -func (m *Meterer) IncrementBinUsage(ctx context.Context, header core.PaymentMetadata, reservation *core.ReservedPayment, numSymbols uint) error { +// IncrementReservatiionPeriodUsage increments the reservation period usage atomically and checks for overflow +func (m *Meterer) IncrementReservatiionPeriodUsage(ctx context.Context, header core.PaymentMetadata, reservation *core.ReservedPayment, numSymbols uint) error { symbolsCharged := m.SymbolsCharged(numSymbols) - newUsage, err := m.OffchainStore.UpdateReservationBin(ctx, header.AccountID, uint64(header.ReservationPeriod), uint64(symbolsCharged)) + newUsage, err := m.OffchainStore.UpdateReservationPeriod(ctx, header.AccountID, uint64(header.ReservationPeriod), uint64(symbolsCharged)) if err != nil { - return fmt.Errorf("failed to increment bin usage: %w", err) + return fmt.Errorf("failed to increment reservation period usage: %w", err) } - // metered usage stays within the bin limit - usageLimit := m.GetReservationBinLimit(reservation) + // metered usage stays within the period limit + usageLimit := m.GetReservationPeriodLimit(reservation) if newUsage <= usageLimit { return nil } else if newUsage-uint64(symbolsCharged) >= usageLimit { // metered usage before updating the size already exceeded the limit - return fmt.Errorf("bin has already been filled") + return fmt.Errorf("period has already been filled") } 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) + _, err := m.OffchainStore.UpdateReservationPeriod(ctx, header.AccountID, uint64(header.ReservationPeriod+2), newUsage-usageLimit) if err != nil { return err } return nil } - return fmt.Errorf("overflow usage exceeds bin limit") + return fmt.Errorf("overflow usage exceeds period limit") } -// GetReservationPeriod returns the current reservation period by chunking time by the bin interval; +// GetReservationPeriod returns the current reservation period by chunking time by the period interval; // bin interval used by the disperser should be public information -func GetReservationPeriod(timestamp uint64, binInterval uint32) uint32 { - if binInterval == 0 { +func GetReservationPeriod(timestamp uint64, periodInterval uint32) uint32 { + if periodInterval == 0 { return 0 } - return uint32(timestamp) / binInterval + return uint32(timestamp) / periodInterval } // ServeOnDemandRequest handles the rate limiting logic for incoming requests @@ -207,8 +226,8 @@ func (m *Meterer) ServeOnDemandRequest(ctx context.Context, header core.PaymentM return fmt.Errorf("invalid on-demand payment: %w", err) } - // Update bin usage atomically and check against bin capacity - if err := m.IncrementGlobalBinUsage(ctx, uint64(symbolsCharged)); err != nil { + // Update reservation period usage atomically and check against period capacity + if err := m.IncrementGlobalPeriodUsage(ctx, uint64(symbolsCharged)); err != nil { //TODO: conditionally remove the payment based on the error type (maybe if the error is store-op related) dbErr := m.OffchainStore.RemoveOnDemandPayment(ctx, header.AccountID, header.CumulativePayment) if dbErr != nil { @@ -265,21 +284,21 @@ func (m *Meterer) SymbolsCharged(numSymbols uint) uint32 { return uint32(core.RoundUpDivide(uint(numSymbols), uint(m.ChainPaymentState.GetMinNumSymbols()))) * m.ChainPaymentState.GetMinNumSymbols() } -// IncrementBinUsage increments the bin usage atomically and checks for overflow -func (m *Meterer) IncrementGlobalBinUsage(ctx context.Context, symbolsCharged uint64) error { +// IncrementGlobalPeriodUsage increments the period usage atomically and checks for overflow +func (m *Meterer) IncrementGlobalPeriodUsage(ctx context.Context, symbolsCharged uint64) error { globalPeriod := GetReservationPeriod(uint64(time.Now().Unix()), m.ChainPaymentState.GetGlobalRatePeriodInterval()) - newUsage, err := m.OffchainStore.UpdateGlobalBin(ctx, globalPeriod, symbolsCharged) + newUsage, err := m.OffchainStore.UpdateGlobalPeriod(ctx, globalPeriod, symbolsCharged) if err != nil { - return fmt.Errorf("failed to increment global bin usage: %w", err) + return fmt.Errorf("failed to increment global reservation period usage: %w", err) } if newUsage > m.ChainPaymentState.GetGlobalSymbolsPerSecond()*uint64(m.ChainPaymentState.GetGlobalRatePeriodInterval()) { - return fmt.Errorf("global bin usage overflows") + return fmt.Errorf("global reservation period usage overflows") } return nil } -// GetReservationBinLimit returns the bin limit for a given reservation -func (m *Meterer) GetReservationBinLimit(reservation *core.ReservedPayment) uint64 { +// GetReservationPeriodLimit returns the period limit for a given reservation +func (m *Meterer) GetReservationPeriodLimit(reservation *core.ReservedPayment) uint64 { return reservation.SymbolsPerSecond * uint64(m.ChainPaymentState.GetReservationWindow()) } diff --git a/core/meterer/meterer_test.go b/core/meterer/meterer_test.go index 0f9c16569c..9f51492d0c 100644 --- a/core/meterer/meterer_test.go +++ b/core/meterer/meterer_test.go @@ -110,8 +110,9 @@ func setup(_ *testing.M) { logger = logging.NewNoopLogger() config := meterer.Config{ - ChainReadTimeout: 3 * time.Second, - UpdateInterval: 1 * time.Second, + ChainReadTimeout: 1 * time.Second, + OnchainUpdateInterval: 1 * time.Second, + OffchainPruneInterval: 1 * time.Second, } err = meterer.CreateReservationTable(clientConfig, reservationTableName) @@ -145,6 +146,7 @@ func setup(_ *testing.M) { reservationTableName, ondemandTableName, globalReservationTableName, + uint64(100), logger, ) @@ -202,14 +204,14 @@ func TestMetererReservations(t *testing.T) { err := mt.MeterRequest(ctx, *header, 1000, []uint8{0, 1, 2}) assert.ErrorContains(t, err, "quorum number mismatch") - // overwhelming bin overflow for empty bins + // overwhelming period overflow for empty periods header = createPaymentHeader(reservationPeriod-1, big.NewInt(0), accountID2) err = mt.MeterRequest(ctx, *header, 10, quoromNumbers) assert.NoError(t, err) - // overwhelming bin overflow for empty bins + // overwhelming period overflow for empty periods header = createPaymentHeader(reservationPeriod-1, big.NewInt(0), accountID2) err = mt.MeterRequest(ctx, *header, 1000, quoromNumbers) - assert.ErrorContains(t, err, "overflow usage exceeds bin limit") + assert.ErrorContains(t, err, "overflow usage exceeds period limit") // test non-existent account unregisteredUser, err := crypto.GenerateKey() @@ -231,7 +233,7 @@ func TestMetererReservations(t *testing.T) { err = mt.MeterRequest(ctx, *header, 2000, quoromNumbers) assert.ErrorContains(t, err, "invalid reservation period for reservation") - // test bin usage metering + // test period usage metering symbolLength := uint(20) requiredLength := uint(21) // 21 should be charged for length of 20 since minNumSymbols is 3 for i := 0; i < 9; i++ { @@ -245,7 +247,7 @@ func TestMetererReservations(t *testing.T) { assert.NoError(t, err) assert.Equal(t, accountID2.Hex(), item["AccountID"].(*types.AttributeValueMemberS).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) + assert.Equal(t, strconv.Itoa((i+1)*int(requiredLength)), item["PeriodUsage"].(*types.AttributeValueMemberN).Value) } // first over flow is allowed @@ -262,13 +264,13 @@ func TestMetererReservations(t *testing.T) { assert.Equal(t, accountID2.Hex(), item["AccountID"].(*types.AttributeValueMemberS).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) + assert.Equal(t, strconv.Itoa(int(16)), item["PeriodUsage"].(*types.AttributeValueMemberN).Value) // second over flow header = createPaymentHeader(reservationPeriod, big.NewInt(0), accountID2) assert.NoError(t, err) err = mt.MeterRequest(ctx, *header, 1, quoromNumbers) - assert.ErrorContains(t, err, "bin has already been filled") + assert.ErrorContains(t, err, "period has already been filled") } func TestMetererOnDemand(t *testing.T) { diff --git a/core/meterer/offchain_store.go b/core/meterer/offchain_store.go index 5899ca75e9..a0577c20a9 100644 --- a/core/meterer/offchain_store.go +++ b/core/meterer/offchain_store.go @@ -17,7 +17,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" ) -const MinNumBins int32 = 3 +const MinNumPeriods int32 = 3 type OffchainStore struct { dynamoClient commondynamodb.Client @@ -26,6 +26,7 @@ type OffchainStore struct { globalBinTableName string logger logging.Logger // TODO: add maximum storage for both tables + MaxOnDemandStorage uint64 } func NewOffchainStore( @@ -33,6 +34,7 @@ func NewOffchainStore( reservationTableName string, onDemandTableName string, globalBinTableName string, + maxOnDemandStorage uint64, logger logging.Logger, ) (OffchainStore, error) { @@ -54,71 +56,72 @@ func NewOffchainStore( return OffchainStore{}, err } //TODO: add a separate thread to periodically clean up the tables - // delete expired reservation bins (= int(s.MaxOnDemandStorage) { + numToDelete := len(payments) - int(s.MaxOnDemandStorage) + 1 + // Create keys for all payments to delete (taking the smallest cumulative payments) + keysToDelete := make([]commondynamodb.Key, numToDelete) + for i := 0; i < numToDelete; i++ { + keysToDelete[i] = commondynamodb.Key{ + "AccountID": payments[i]["AccountID"], + "CumulativePayments": payments[i]["CumulativePayments"], + } + } + + // Delete the items in batches + failedKeys, err := s.dynamoClient.DeleteItems(ctx, s.onDemandTableName, keysToDelete) + if err != nil { + return fmt.Errorf("failed to delete oldest payments: %w", err) + } + if len(failedKeys) > 0 { + return fmt.Errorf("failed to delete %d payments", len(failedKeys)) + } + } + return nil } @@ -249,8 +296,8 @@ func (s *OffchainStore) GetRelevantOnDemandRecords(ctx context.Context, accountI return prevPayment, nextPayment, nextDataLength, nil } -func (s *OffchainStore) GetBinRecords(ctx context.Context, accountID string, reservationPeriod uint32) ([MinNumBins]*pb.BinRecord, error) { - // Fetch the 3 bins start from the current bin +func (s *OffchainStore) GetReservationPeriodRecords(ctx context.Context, accountID string, reservationPeriod uint32) ([MinNumPeriods]*pb.ReservationPeriodRecord, error) { + // Fetch the 3 periods start from the current bin queryInput := &dynamodb.QueryInput{ TableName: aws.String(s.reservationTableName), KeyConditionExpression: aws.String("AccountID = :account AND ReservationPeriod > :reservationPeriod"), @@ -259,20 +306,20 @@ func (s *OffchainStore) GetBinRecords(ctx context.Context, accountID string, res ":reservationPeriod": &types.AttributeValueMemberN{Value: strconv.FormatUint(uint64(reservationPeriod), 10)}, }, ScanIndexForward: aws.Bool(true), - Limit: aws.Int32(MinNumBins), + Limit: aws.Int32(MinNumPeriods), } - bins, err := s.dynamoClient.QueryWithInput(ctx, queryInput) + periods, err := s.dynamoClient.QueryWithInput(ctx, queryInput) if err != nil { - return [MinNumBins]*pb.BinRecord{}, fmt.Errorf("failed to query payments for account: %w", err) + return [MinNumPeriods]*pb.ReservationPeriodRecord{}, 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]) + records := [MinNumPeriods]*pb.ReservationPeriodRecord{} + for i := 0; i < len(periods) && i < int(MinNumPeriods); i++ { + reservationPeriodRecord, err := parseReservationPeriodRecord(periods[i]) if err != nil { - return [MinNumBins]*pb.BinRecord{}, fmt.Errorf("failed to parse bin %d record: %w", i, err) + return [MinNumPeriods]*pb.ReservationPeriodRecord{}, fmt.Errorf("failed to parse bin %d record: %w", i, err) } - records[i] = binRecord + records[i] = reservationPeriodRecord } return records, nil @@ -307,7 +354,45 @@ func (s *OffchainStore) GetLargestCumulativePayment(ctx context.Context, account return payment, nil } -func parseBinRecord(bin map[string]types.AttributeValue) (*pb.BinRecord, error) { +// DeleteOldPeriods removes all reservation bin entries with indices strictly less than the provided reservationPeriod +func (s *OffchainStore) DeleteOldPeriods(ctx context.Context, reservationPeriod uint32) error { + // get all keys that need to be deleted + queryInput := &dynamodb.QueryInput{ + TableName: aws.String(s.reservationTableName), + FilterExpression: aws.String("ReservationPeriod < :reservationPeriod"), + ExpressionAttributeValues: commondynamodb.ExpressionValues{ + ":reservationPeriod": &types.AttributeValueMemberN{Value: strconv.FormatUint(uint64(reservationPeriod), 10)}, + }, + } + + items, err := s.dynamoClient.QueryWithInput(ctx, queryInput) + if err != nil { + return fmt.Errorf("failed to query old periods: %w", err) + } + + keys := make([]commondynamodb.Key, len(items)) + for i, item := range items { + keys[i] = commondynamodb.Key{ + "AccountID": item["AccountID"], + "ReservationPeriod": item["ReservationPeriod"], + } + } + + // Delete the items in batches + if len(keys) > 0 { + failedKeys, err := s.dynamoClient.DeleteItems(ctx, s.reservationTableName, keys) + if err != nil { + return fmt.Errorf("failed to delete old periods: %w", err) + } + if len(failedKeys) > 0 { + return fmt.Errorf("failed to delete %d periods", len(failedKeys)) + } + } + + return nil +} + +func parseReservationPeriodRecord(bin map[string]types.AttributeValue) (*pb.ReservationPeriodRecord, error) { reservationPeriod, ok := bin["ReservationPeriod"] if !ok { return nil, errors.New("ReservationPeriod is not present in the response") @@ -323,23 +408,23 @@ func parseBinRecord(bin map[string]types.AttributeValue) (*pb.BinRecord, error) return nil, fmt.Errorf("failed to parse ReservationPeriod: %w", err) } - binUsage, ok := bin["BinUsage"] + periodUsage, ok := bin["PeriodUsage"] if !ok { - return nil, errors.New("BinUsage is not present in the response") + return nil, errors.New("PeriodUsage is not present in the response") } - binUsageAttr, ok := binUsage.(*types.AttributeValueMemberN) + periodUsageAttr, ok := periodUsage.(*types.AttributeValueMemberN) if !ok { - return nil, fmt.Errorf("unexpected type for BinUsage: %T", binUsage) + return nil, fmt.Errorf("unexpected type for PeriodUsage: %T", periodUsage) } - binUsageValue, err := strconv.ParseUint(binUsageAttr.Value, 10, 32) + periodUsageValue, err := strconv.ParseUint(periodUsageAttr.Value, 10, 32) if err != nil { - return nil, fmt.Errorf("failed to parse BinUsage: %w", err) + return nil, fmt.Errorf("failed to parse PeriodUsage: %w", err) } - return &pb.BinRecord{ + return &pb.ReservationPeriodRecord{ Index: uint32(reservationPeriodValue), - Usage: uint64(binUsageValue), + Usage: uint64(periodUsageValue), }, nil } diff --git a/core/meterer/offchain_store_test.go b/core/meterer/offchain_store_test.go index 79963b0594..006699c9eb 100644 --- a/core/meterer/offchain_store_test.go +++ b/core/meterer/offchain_store_test.go @@ -23,7 +23,7 @@ func TestReservationBinsBasicOperations(t *testing.T) { commondynamodb.Item{ "AccountID": &types.AttributeValueMemberS{Value: "account1"}, "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, - "BinUsage": &types.AttributeValueMemberN{Value: "1000"}, + "PeriodUsage": &types.AttributeValueMemberN{Value: "1000"}, "UpdatedAt": &types.AttributeValueMemberS{Value: time.Now().Format(time.RFC3339)}, }, ) @@ -37,7 +37,7 @@ func TestReservationBinsBasicOperations(t *testing.T) { assert.Equal(t, "account1", item["AccountID"].(*types.AttributeValueMemberS).Value) assert.Equal(t, "1", item["ReservationPeriod"].(*types.AttributeValueMemberN).Value) - assert.Equal(t, "1000", item["BinUsage"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "1000", item["PeriodUsage"].(*types.AttributeValueMemberN).Value) items, err := dynamoClient.Query(ctx, tableName, "AccountID = :account", commondynamodb.ExpressionValues{ ":account": &types.AttributeValueMemberS{Value: "account1"}, @@ -54,14 +54,14 @@ func TestReservationBinsBasicOperations(t *testing.T) { "AccountID": &types.AttributeValueMemberS{Value: "account1"}, "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, }, commondynamodb.Item{ - "BinUsage": &types.AttributeValueMemberN{Value: "2000"}, + "PeriodUsage": &types.AttributeValueMemberN{Value: "2000"}, }) assert.NoError(t, err) err = dynamoClient.PutItem(ctx, tableName, commondynamodb.Item{ "AccountID": &types.AttributeValueMemberS{Value: "account2"}, "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, - "BinUsage": &types.AttributeValueMemberN{Value: "3000"}, + "PeriodUsage": &types.AttributeValueMemberN{Value: "3000"}, "UpdatedAt": &types.AttributeValueMemberS{Value: time.Now().Format(time.RFC3339)}, }, ) @@ -72,21 +72,21 @@ func TestReservationBinsBasicOperations(t *testing.T) { "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, }) assert.NoError(t, err) - assert.Equal(t, "2000", item["BinUsage"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "2000", item["PeriodUsage"].(*types.AttributeValueMemberN).Value) items, err = dynamoClient.Query(ctx, tableName, "AccountID = :account", commondynamodb.ExpressionValues{ ":account": &types.AttributeValueMemberS{Value: "account1"}, }) assert.NoError(t, err) assert.Len(t, items, 1) - assert.Equal(t, "2000", items[0]["BinUsage"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "2000", items[0]["PeriodUsage"].(*types.AttributeValueMemberN).Value) item, err = dynamoClient.GetItem(ctx, tableName, commondynamodb.Key{ "AccountID": &types.AttributeValueMemberS{Value: "account2"}, "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, }) assert.NoError(t, err) - assert.Equal(t, "3000", item["BinUsage"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "3000", item["PeriodUsage"].(*types.AttributeValueMemberN).Value) err = dynamoClient.DeleteTable(ctx, tableName) assert.NoError(t, err) @@ -103,7 +103,7 @@ func TestGlobalBinsBasicOperations(t *testing.T) { for i := 0; i < numItems; i += 1 { items[i] = commondynamodb.Item{ "ReservationPeriod": &types.AttributeValueMemberN{Value: fmt.Sprintf("%d", i)}, - "BinUsage": &types.AttributeValueMemberN{Value: "1000"}, + "PeriodUsage": &types.AttributeValueMemberN{Value: "1000"}, "UpdatedAt": &types.AttributeValueMemberS{Value: time.Now().Format(time.RFC3339)}, } } @@ -118,7 +118,7 @@ func TestGlobalBinsBasicOperations(t *testing.T) { assert.NoError(t, err) assert.Len(t, queryResult, 1) assert.Equal(t, "1", queryResult[0]["ReservationPeriod"].(*types.AttributeValueMemberN).Value) - assert.Equal(t, "1000", queryResult[0]["BinUsage"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "1000", queryResult[0]["PeriodUsage"].(*types.AttributeValueMemberN).Value) queryResult, err = dynamoClient.Query(ctx, tableName, "ReservationPeriod = :index", commondynamodb.ExpressionValues{ ":index": &types.AttributeValueMemberN{ @@ -127,7 +127,7 @@ func TestGlobalBinsBasicOperations(t *testing.T) { assert.NoError(t, err) assert.Len(t, queryResult, 1) assert.Equal(t, "1", queryResult[0]["ReservationPeriod"].(*types.AttributeValueMemberN).Value) - assert.Equal(t, "1000", queryResult[0]["BinUsage"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "1000", queryResult[0]["PeriodUsage"].(*types.AttributeValueMemberN).Value) queryResult, err = dynamoClient.Query(ctx, tableName, "ReservationPeriod = :index", commondynamodb.ExpressionValues{ ":index": &types.AttributeValueMemberN{ @@ -139,14 +139,14 @@ func TestGlobalBinsBasicOperations(t *testing.T) { _, err = dynamoClient.UpdateItem(ctx, tableName, commondynamodb.Key{ "ReservationPeriod": &types.AttributeValueMemberN{Value: "1"}, }, commondynamodb.Item{ - "BinUsage": &types.AttributeValueMemberN{Value: "2000"}, + "PeriodUsage": &types.AttributeValueMemberN{Value: "2000"}, }) assert.NoError(t, err) err = dynamoClient.PutItem(ctx, tableName, commondynamodb.Item{ "ReservationPeriod": &types.AttributeValueMemberN{Value: "2"}, - "BinUsage": &types.AttributeValueMemberN{Value: "3000"}, + "PeriodUsage": &types.AttributeValueMemberN{Value: "3000"}, "UpdatedAt": &types.AttributeValueMemberS{Value: time.Now().Format(time.RFC3339)}, }, ) @@ -159,7 +159,7 @@ func TestGlobalBinsBasicOperations(t *testing.T) { assert.NoError(t, err) assert.Len(t, queryResult, 1) assert.Equal(t, "1", queryResult[0]["ReservationPeriod"].(*types.AttributeValueMemberN).Value) - assert.Equal(t, "2000", queryResult[0]["BinUsage"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "2000", queryResult[0]["PeriodUsage"].(*types.AttributeValueMemberN).Value) queryResult, err = dynamoClient.Query(ctx, tableName, "ReservationPeriod = :index", commondynamodb.ExpressionValues{ ":index": &types.AttributeValueMemberN{ @@ -168,7 +168,7 @@ func TestGlobalBinsBasicOperations(t *testing.T) { assert.NoError(t, err) assert.Len(t, queryResult, 1) assert.Equal(t, "2", queryResult[0]["ReservationPeriod"].(*types.AttributeValueMemberN).Value) - assert.Equal(t, "3000", queryResult[0]["BinUsage"].(*types.AttributeValueMemberN).Value) + assert.Equal(t, "3000", queryResult[0]["PeriodUsage"].(*types.AttributeValueMemberN).Value) } func TestOnDemandUsageBasicOperations(t *testing.T) { @@ -230,7 +230,6 @@ func TestOnDemandUsageBasicOperations(t *testing.T) { updatedItem, err := dynamoClient.UpdateItem(ctx, tableName, commondynamodb.Key{ "AccountID": &types.AttributeValueMemberS{Value: "account1"}, "CumulativePayments": &types.AttributeValueMemberN{Value: "1"}, - // "BinUsage": &types.AttributeValueMemberN{Value: "1000"}, }, commondynamodb.Item{ "AccountID": &types.AttributeValueMemberS{Value: "account1"}, "CumulativePayments": &types.AttributeValueMemberN{Value: "3"}, diff --git a/disperser/apiserver/server_test.go b/disperser/apiserver/server_test.go index a696c46863..53d876d2fd 100644 --- a/disperser/apiserver/server_test.go +++ b/disperser/apiserver/server_test.go @@ -791,13 +791,18 @@ func newTestServer(transactor core.Writer, testName string) *apiserver.Dispersal table_names[0], table_names[1], table_names[2], + uint64(100), logger, ) if err != nil { teardown() panic("failed to create offchain store") } - mt := meterer.NewMeterer(meterer.Config{}, mockState, store, logger) + mt := meterer.NewMeterer(meterer.Config{ + ChainReadTimeout: 1 * time.Second, + OnchainUpdateInterval: 1 * time.Second, + OffchainPruneInterval: 1 * time.Second, + }, mockState, store, logger) err = mt.ChainPaymentState.RefreshOnchainPaymentState(context.Background()) if err != nil { panic("failed to make initial query to the on-chain state") diff --git a/disperser/apiserver/server_v2.go b/disperser/apiserver/server_v2.go index efe5566500..6deb20abc9 100644 --- a/disperser/apiserver/server_v2.go +++ b/disperser/apiserver/server_v2.go @@ -57,6 +57,7 @@ type DispersalServerV2 struct { onchainState atomic.Pointer[OnchainState] maxNumSymbolsPerBlob uint64 onchainStateRefreshInterval time.Duration + OffchainPruneInterval time.Duration metrics *metricsV2 } @@ -72,6 +73,7 @@ func NewDispersalServerV2( prover encoding.Prover, maxNumSymbolsPerBlob uint64, onchainStateRefreshInterval time.Duration, + OffchainPruneInterval time.Duration, _logger logging.Logger, registry *prometheus.Registry, ) (*DispersalServerV2, error) { @@ -115,6 +117,7 @@ func NewDispersalServerV2( maxNumSymbolsPerBlob: maxNumSymbolsPerBlob, onchainStateRefreshInterval: onchainStateRefreshInterval, + OffchainPruneInterval: OffchainPruneInterval, metrics: newAPIServerV2Metrics(registry), }, nil @@ -281,7 +284,7 @@ func (s *DispersalServerV2) GetPaymentState(ctx context.Context, req *pb.GetPaym // off-chain account specific payment state now := uint64(time.Now().Unix()) currentReservationPeriod := meterer.GetReservationPeriod(now, reservationWindow) - binRecords, err := s.meterer.OffchainStore.GetBinRecords(ctx, req.AccountId, currentReservationPeriod) + reservationPeriodRecords, err := s.meterer.OffchainStore.GetReservationPeriodRecords(ctx, req.AccountId, currentReservationPeriod) if err != nil { s.logger.Debug("failed to get reservation records, use placeholders", "err", err, "accountID", accountID) } @@ -335,7 +338,7 @@ func (s *DispersalServerV2) GetPaymentState(ctx context.Context, req *pb.GetPaym // build reply reply := &pb.GetPaymentStateReply{ PaymentGlobalParams: &paymentGlobalParams, - BinRecords: binRecords[:], + ReservationPeriodRecords: reservationPeriodRecords[:], Reservation: pbReservation, CumulativePayment: largestCumulativePaymentBytes, OnchainCumulativePayment: onchainCumulativePaymentBytes, diff --git a/disperser/apiserver/server_v2_test.go b/disperser/apiserver/server_v2_test.go index 367d3c9bbb..13276126b7 100644 --- a/disperser/apiserver/server_v2_test.go +++ b/disperser/apiserver/server_v2_test.go @@ -482,13 +482,18 @@ func newTestServerV2(t *testing.T) *testComponents { table_names[0], table_names[1], table_names[2], + uint64(100), logger, ) if err != nil { teardown() panic("failed to create offchain store") } - meterer := meterer.NewMeterer(meterer.Config{}, mockState, store, logger) + meterer := meterer.NewMeterer(meterer.Config{ + ChainReadTimeout: 1 * time.Second, + OnchainUpdateInterval: 1 * time.Second, + OffchainPruneInterval: 1 * time.Second, + }, mockState, store, logger) chainReader.On("GetCurrentBlockNumber").Return(uint32(100), nil) chainReader.On("GetQuorumCount").Return(uint8(2), nil) @@ -516,6 +521,7 @@ func newTestServerV2(t *testing.T) *testComponents { prover, 10, time.Hour, + time.Hour, logger, prometheus.NewRegistry()) assert.NoError(t, err) diff --git a/disperser/cmd/apiserver/config.go b/disperser/cmd/apiserver/config.go index a27bd0b793..72ab684025 100644 --- a/disperser/cmd/apiserver/config.go +++ b/disperser/cmd/apiserver/config.go @@ -35,7 +35,8 @@ type Config struct { EncodingConfig kzg.KzgConfig EnableRatelimiter bool EnablePaymentMeterer bool - UpdateInterval int + OnchainUpdateInterval int + OffchainMaxOnDemandStorage int ChainReadTimeout int ReservationsTableName string OnDemandTableName string @@ -46,6 +47,7 @@ type Config struct { MaxBlobSize int MaxNumSymbolsPerBlob uint OnchainStateRefreshInterval time.Duration + OffchainPruneInterval time.Duration BLSOperatorStateRetrieverAddr string EigenDAServiceManagerAddr string @@ -119,12 +121,14 @@ func NewConfig(ctx *cli.Context) (Config, error) { GlobalRateTableName: ctx.GlobalString(flags.GlobalRateTableName.Name), BucketTableName: ctx.GlobalString(flags.BucketTableName.Name), BucketStoreSize: ctx.GlobalInt(flags.BucketStoreSize.Name), - UpdateInterval: ctx.GlobalInt(flags.UpdateInterval.Name), + OnchainUpdateInterval: ctx.GlobalInt(flags.OnchainUpdateInterval.Name), ChainReadTimeout: ctx.GlobalInt(flags.ChainReadTimeout.Name), EthClientConfig: geth.ReadEthClientConfigRPCOnly(ctx), MaxBlobSize: ctx.GlobalInt(flags.MaxBlobSize.Name), MaxNumSymbolsPerBlob: ctx.GlobalUint(flags.MaxNumSymbolsPerBlob.Name), OnchainStateRefreshInterval: ctx.GlobalDuration(flags.OnchainStateRefreshInterval.Name), + OffchainPruneInterval: ctx.GlobalDuration(flags.OffchainPruneInterval.Name), + OffchainMaxOnDemandStorage: ctx.GlobalInt(flags.OffchainMaxOnDemandStorage.Name), BLSOperatorStateRetrieverAddr: ctx.GlobalString(flags.BlsOperatorStateRetrieverFlag.Name), EigenDAServiceManagerAddr: ctx.GlobalString(flags.EigenDAServiceManagerFlag.Name), diff --git a/disperser/cmd/apiserver/flags/flags.go b/disperser/cmd/apiserver/flags/flags.go index af31d87ff9..755115c9e5 100644 --- a/disperser/cmd/apiserver/flags/flags.go +++ b/disperser/cmd/apiserver/flags/flags.go @@ -107,11 +107,11 @@ var ( Value: "global_rate", EnvVar: common.PrefixEnvVar(envVarPrefix, "GLOBAL_RATE_TABLE_NAME"), } - UpdateInterval = cli.DurationFlag{ - Name: common.PrefixFlag(FlagPrefix, "update-interval"), + OnchainUpdateInterval = cli.DurationFlag{ + Name: common.PrefixFlag(FlagPrefix, "onchain-update-interval"), Usage: "update interval for refreshing the on-chain state", - Value: 1 * time.Second, - EnvVar: common.PrefixEnvVar(envVarPrefix, "UPDATE_INTERVAL"), + Value: 2 * time.Minute, + EnvVar: common.PrefixEnvVar(envVarPrefix, "ONCHAIN_UPDATE_INTERVAL"), Required: false, } ChainReadTimeout = cli.UintFlag{ @@ -146,7 +146,21 @@ var ( Usage: "The interval at which to refresh the onchain state. This flag is only relevant in v2", Required: false, EnvVar: common.PrefixEnvVar(envVarPrefix, "ONCHAIN_STATE_REFRESH_INTERVAL"), - Value: 1 * time.Hour, + Value: 2 * time.Minute, + } + OffchainPruneInterval = cli.DurationFlag{ + Name: common.PrefixFlag(FlagPrefix, "offchain-state-pruning-interval"), + Usage: "The interval at which to prune the outdated offchain state. This flag is only relevant in v2", + Required: false, + EnvVar: common.PrefixEnvVar(envVarPrefix, "OFFCHAIN_STATE_PRUNING_INTERVAL"), + Value: 2 * time.Minute, + } + OffchainMaxOnDemandStorage = cli.UintFlag{ + Name: common.PrefixFlag(FlagPrefix, "offchain-max-on-demand-storage"), + Usage: "max number of on-demand payments to store in the off-chain state", + Value: 100, + EnvVar: common.PrefixEnvVar(envVarPrefix, "OFFCHAIN_MAX_ON_DEMAND_STORAGE"), + Required: false, } MaxNumSymbolsPerBlob = cli.UintFlag{ Name: common.PrefixFlag(FlagPrefix, "max-num-symbols-per-blob"), @@ -259,6 +273,8 @@ var optionalFlags = []cli.Flag{ OnDemandTableName, GlobalRateTableName, OnchainStateRefreshInterval, + OffchainPruneInterval, + OffchainMaxOnDemandStorage, MaxNumSymbolsPerBlob, PprofHttpPort, EnablePprof, diff --git a/disperser/cmd/apiserver/main.go b/disperser/cmd/apiserver/main.go index 2659f0c87b..ad20b7c7de 100644 --- a/disperser/cmd/apiserver/main.go +++ b/disperser/cmd/apiserver/main.go @@ -99,8 +99,9 @@ func RunDisperserServer(ctx *cli.Context) error { var meterer *mt.Meterer if config.EnablePaymentMeterer { mtConfig := mt.Config{ - ChainReadTimeout: time.Duration(config.ChainReadTimeout) * time.Second, - UpdateInterval: time.Duration(config.UpdateInterval) * time.Second, + ChainReadTimeout: time.Duration(config.ChainReadTimeout) * time.Second, + OnchainUpdateInterval: time.Duration(config.OnchainUpdateInterval) * time.Second, + OffchainPruneInterval: time.Duration(config.OffchainPruneInterval) * time.Second, } paymentChainState, err := mt.NewOnchainPaymentState(context.Background(), transactor) @@ -116,6 +117,7 @@ func RunDisperserServer(ctx *cli.Context) error { config.ReservationsTableName, config.OnDemandTableName, config.GlobalRateTableName, + uint64(config.OffchainMaxOnDemandStorage), logger, ) if err != nil { @@ -180,6 +182,7 @@ func RunDisperserServer(ctx *cli.Context) error { prover, uint64(config.MaxNumSymbolsPerBlob), config.OnchainStateRefreshInterval, + config.OffchainPruneInterval, logger, reg, ) diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go index d4f7fe88e5..4ffba7f749 100644 --- a/inabox/deploy/config.go +++ b/inabox/deploy/config.go @@ -222,6 +222,10 @@ func (env *Config) generateDisperserV2Vars(ind int, logPath, dbPath, grpcPort st DISPERSER_SERVER_BUCKET_MULTIPLIERS: "1", DISPERSER_SERVER_COUNT_FAILED: "true", + DISPERSER_SERVER_ONCHAIN_STATE_REFRESH_INTERVAL: "2m", + DISPERSER_SERVER_OFFCHAIN_PRUNE_INTERVAL: "2m", + DISPERSER_SERVER_OFFCHAIN_MAX_ON_DEMAND_STORAGE: "100", + DISPERSER_SERVER_BLS_OPERATOR_STATE_RETRIVER: env.EigenDA.OperatorStateRetreiver, DISPERSER_SERVER_EIGENDA_SERVICE_MANAGER: env.EigenDA.ServiceManager, DISPERSER_SERVER_DISPERSER_VERSION: "2", diff --git a/inabox/deploy/env_vars.go b/inabox/deploy/env_vars.go index e55984eb01..4e5cc24b6c 100644 --- a/inabox/deploy/env_vars.go +++ b/inabox/deploy/env_vars.go @@ -39,6 +39,10 @@ type DisperserVars struct { DISPERSER_SERVER_ONCHAIN_STATE_REFRESH_INTERVAL string + DISPERSER_SERVER_OFFCHAIN_PRUNE_INTERVAL string + + DISPERSER_SERVER_OFFCHAIN_MAX_ON_DEMAND_STORAGE string + DISPERSER_SERVER_MAX_NUM_SYMBOLS_PER_BLOB string DISPERSER_SERVER_PPROF_HTTP_PORT string diff --git a/test/integration_test.go b/test/integration_test.go index e3c390e56d..6d79456a52 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -283,6 +283,7 @@ func mustMakeDisperser(t *testing.T, cst core.IndexedChainState, store disperser table_names[0], table_names[1], table_names[2], + uint64(100), logger, ) if err != nil { @@ -294,7 +295,11 @@ func mustMakeDisperser(t *testing.T, cst core.IndexedChainState, store disperser panic("failed to make initial query to the on-chain state") } - mt := meterer.NewMeterer(meterer.Config{}, mockState, offchainStore, logger) + mt := meterer.NewMeterer(meterer.Config{ + ChainReadTimeout: 1 * time.Second, + OnchainUpdateInterval: 1 * time.Second, + OffchainPruneInterval: 1 * time.Second, + }, mockState, offchainStore, logger) server := apiserver.NewDispersalServer(serverConfig, store, tx, logger, disperserMetrics, grpcprom.NewServerMetrics(), mt, ratelimiter, rateConfig, testMaxBlobSize) return TestDisperser{ From 33af3e5e3ce58540b3a3f6bdb7b88c6f9330a755 Mon Sep 17 00:00:00 2001 From: hopeyen Date: Fri, 3 Jan 2025 08:15:53 -0800 Subject: [PATCH 2/4] chore: proto rebase --- api/grpc/disperser/v2/disperser_v2.pb.go | 236 ++++++++++++----------- 1 file changed, 120 insertions(+), 116 deletions(-) diff --git a/api/grpc/disperser/v2/disperser_v2.pb.go b/api/grpc/disperser/v2/disperser_v2.pb.go index 04ff63e64d..8356625dac 100644 --- a/api/grpc/disperser/v2/disperser_v2.pb.go +++ b/api/grpc/disperser/v2/disperser_v2.pb.go @@ -1056,122 +1056,126 @@ var file_disperser_v2_disperser_v2_proto_rawDesc = []byte{ 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, 0xec, 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, 0x12, 0x3a, 0x0a, 0x19, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, - 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, - 0x67, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x17, 0x71, 0x75, 0x6f, 0x72, 0x75, - 0x6d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, - 0x65, 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, - 0xd5, 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, 0x23, 0x0a, 0x0d, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x73, 0x70, 0x6c, - 0x69, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x71, 0x75, 0x6f, 0x72, 0x75, - 0x6d, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x73, 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, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x63, 0x0a, 0x1a, 0x72, + 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, + 0x64, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x25, 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, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, + 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x18, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 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, 0xec, + 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, 0x12, 0x3a, 0x0a, 0x19, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x73, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x73, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x17, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x53, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 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, 0xd5, 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, 0x23, 0x0a, + 0x0d, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x53, 0x70, 0x6c, 0x69, + 0x74, 0x73, 0x22, 0x45, 0x0a, 0x17, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 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 ( From c461dfbcbf5288288548035351a1ad5658b27198 Mon Sep 17 00:00:00 2001 From: hopeyen Date: Fri, 3 Jan 2025 08:17:55 -0800 Subject: [PATCH 3/4] fix: lint --- api/clients/v2/accountant_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api/clients/v2/accountant_test.go b/api/clients/v2/accountant_test.go index 1bbd64a209..94e5714c5b 100644 --- a/api/clients/v2/accountant_test.go +++ b/api/clients/v2/accountant_test.go @@ -68,7 +68,7 @@ func TestAccountBlob_Reservation(t *testing.T) { accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins) ctx := context.Background() - symbolLength := uint32(500) + symbolLength := uint64(500) quorums := []uint8{0, 1} header, err := accountant.AccountBlob(ctx, symbolLength, quorums, salt) @@ -78,7 +78,7 @@ func TestAccountBlob_Reservation(t *testing.T) { assert.Equal(t, big.NewInt(0), header.CumulativePayment) assert.Equal(t, isRotation([]uint64{500, 0, 0}, mapRecordUsage(accountant.reservationPeriodRecords)), true) - symbolLength = uint32(700) + symbolLength = uint64(700) header, err = accountant.AccountBlob(ctx, symbolLength, quorums, salt) @@ -116,13 +116,13 @@ func TestAccountBlob_OnDemand(t *testing.T) { accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins) ctx := context.Background() - numSymbols := uint32(1500) + numSymbols := uint64(1500) quorums := []uint8{0, 1} header, err := accountant.AccountBlob(ctx, numSymbols, quorums, salt) assert.NoError(t, err) - expectedPayment := big.NewInt(int64(numSymbols * pricePerSymbol)) + expectedPayment := big.NewInt(int64(numSymbols * uint64(pricePerSymbol))) assert.Equal(t, uint32(0), header.ReservationPeriod) assert.Equal(t, expectedPayment, header.CumulativePayment) assert.Equal(t, isRotation([]uint64{0, 0, 0}, mapRecordUsage(accountant.reservationPeriodRecords)), true) @@ -144,7 +144,7 @@ func TestAccountBlob_InsufficientOnDemand(t *testing.T) { accountant := NewAccountant(accountId, reservation, onDemand, reservationWindow, pricePerSymbol, minNumSymbols, numBins) ctx := context.Background() - numSymbols := uint32(2000) + numSymbols := uint64(2000) quorums := []uint8{0, 1} _, err = accountant.AccountBlob(ctx, numSymbols, quorums, salt) From 7d182bc61862db4298c9861c46d800697859de0f Mon Sep 17 00:00:00 2001 From: hopeyen Date: Mon, 6 Jan 2025 08:42:21 -0800 Subject: [PATCH 4/4] refactor: pruning based on max storage size --- core/meterer/meterer.go | 19 ----- core/meterer/meterer_test.go | 2 +- core/meterer/offchain_store.go | 109 +++++++++++++++++++------ disperser/apiserver/server_test.go | 2 +- disperser/apiserver/server_v2.go | 3 - disperser/apiserver/server_v2_test.go | 3 +- disperser/cmd/apiserver/config.go | 4 +- disperser/cmd/apiserver/flags/flags.go | 16 ++-- disperser/cmd/apiserver/main.go | 3 +- test/integration_test.go | 2 +- 10 files changed, 98 insertions(+), 65 deletions(-) diff --git a/core/meterer/meterer.go b/core/meterer/meterer.go index 5605507065..7794d458ed 100644 --- a/core/meterer/meterer.go +++ b/core/meterer/meterer.go @@ -20,8 +20,6 @@ type Config struct { // OnchainUpdateInterval is the interval for refreshing the on-chain state OnchainUpdateInterval time.Duration - // OffchainPruneInterval is the interval for pruning the off-chain state - OffchainPruneInterval time.Duration } // Meterer handles payment accounting across different accounts. Disperser API server receives requests from clients and each request contains a blob header @@ -70,23 +68,6 @@ func (m *Meterer) Start(ctx context.Context) { } } }() - go func() { - ticker := time.NewTicker(m.OffchainPruneInterval) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - now := uint64(time.Now().Unix()) - reservationWindow := m.ChainPaymentState.GetReservationWindow() - if err := m.OffchainStore.DeleteOldPeriods(ctx, GetReservationPeriod(now, reservationWindow)-uint32(MinNumPeriods)); err != nil { - m.logger.Error("Failed to prune off-chain state", "error", err) - } - case <-ctx.Done(): - return - } - } - }() } // MeterRequest validates a blob header and adds it to the meterer's state diff --git a/core/meterer/meterer_test.go b/core/meterer/meterer_test.go index 9f51492d0c..8f15087008 100644 --- a/core/meterer/meterer_test.go +++ b/core/meterer/meterer_test.go @@ -112,7 +112,6 @@ func setup(_ *testing.M) { config := meterer.Config{ ChainReadTimeout: 1 * time.Second, OnchainUpdateInterval: 1 * time.Second, - OffchainPruneInterval: 1 * time.Second, } err = meterer.CreateReservationTable(clientConfig, reservationTableName) @@ -147,6 +146,7 @@ func setup(_ *testing.M) { ondemandTableName, globalReservationTableName, uint64(100), + uint64(100), logger, ) diff --git a/core/meterer/offchain_store.go b/core/meterer/offchain_store.go index a0577c20a9..5022f80ce5 100644 --- a/core/meterer/offchain_store.go +++ b/core/meterer/offchain_store.go @@ -20,13 +20,13 @@ import ( const MinNumPeriods int32 = 3 type OffchainStore struct { - dynamoClient commondynamodb.Client - reservationTableName string - onDemandTableName string - globalBinTableName string - logger logging.Logger - // TODO: add maximum storage for both tables - MaxOnDemandStorage uint64 + dynamoClient commondynamodb.Client + reservationTableName string + onDemandTableName string + globalBinTableName string + logger logging.Logger + MaxOnDemandStorage uint64 + MaxReservationPeriods uint64 } func NewOffchainStore( @@ -35,6 +35,7 @@ func NewOffchainStore( onDemandTableName string, globalBinTableName string, maxOnDemandStorage uint64, + maxReservationPeriods uint64, logger logging.Logger, ) (OffchainStore, error) { @@ -47,23 +48,27 @@ func NewOffchainStore( if err != nil { return OffchainStore{}, err } + err = dynamoClient.TableExists(context.Background(), onDemandTableName) if err != nil { return OffchainStore{}, err } + err = dynamoClient.TableExists(context.Background(), globalBinTableName) if err != nil { return OffchainStore{}, err } + //TODO: add a separate thread to periodically clean up the tables // delete expired reservation periods (= int(s.MaxReservationPeriods) { + numToDelete := len(periods) - int(s.MaxReservationPeriods) + 1 + // Create keys for all reservation periods to delete (taking the smallest reservation periods) + keysToDelete := make([]commondynamodb.Key, numToDelete) + for i := 0; i < numToDelete; i++ { + keysToDelete[i] = commondynamodb.Key{ + "AccountID": periods[i]["AccountID"], + "ReservationPeriod": periods[i]["ReservationPeriod"], + } + } + + // Delete the items in batches + failedKeys, err := s.dynamoClient.DeleteItems(ctx, s.onDemandTableName, keysToDelete) + if err != nil { + return fmt.Errorf("failed to delete oldest reservation periods: %w", err) + } + if len(failedKeys) > 0 { + return fmt.Errorf("failed to delete %d reservation periods", len(failedKeys)) + } } - items, err := s.dynamoClient.QueryWithInput(ctx, queryInput) + return nil +} + +// DeleteOldPeriods removes all reservation bin entries with indices strictly less than the provided reservationPeriod +func (s *OffchainStore) PruneGlobalPeriods(ctx context.Context) error { + // First, get total count of entries + queryInput := &dynamodb.QueryInput{ + TableName: aws.String(s.globalBinTableName), + ScanIndexForward: aws.Bool(true), + } + + allItems, err := s.dynamoClient.QueryWithInput(ctx, queryInput) if err != nil { - return fmt.Errorf("failed to query old periods: %w", err) + return fmt.Errorf("failed to query periods: %w", err) } - keys := make([]commondynamodb.Key, len(items)) - for i, item := range items { + totalCount := len(allItems) + if totalCount <= int(s.MaxReservationPeriods) { + return nil + } + + numToDelete := totalCount - int(s.MaxReservationPeriods) + + keys := make([]commondynamodb.Key, numToDelete) + for i := 0; i < numToDelete; i++ { keys[i] = commondynamodb.Key{ - "AccountID": item["AccountID"], - "ReservationPeriod": item["ReservationPeriod"], + "ReservationPeriod": allItems[i]["ReservationPeriod"], } } // Delete the items in batches if len(keys) > 0 { - failedKeys, err := s.dynamoClient.DeleteItems(ctx, s.reservationTableName, keys) + failedKeys, err := s.dynamoClient.DeleteItems(ctx, s.globalBinTableName, keys) if err != nil { return fmt.Errorf("failed to delete old periods: %w", err) } diff --git a/disperser/apiserver/server_test.go b/disperser/apiserver/server_test.go index 53d876d2fd..2ff572a19f 100644 --- a/disperser/apiserver/server_test.go +++ b/disperser/apiserver/server_test.go @@ -792,6 +792,7 @@ func newTestServer(transactor core.Writer, testName string) *apiserver.Dispersal table_names[1], table_names[2], uint64(100), + uint64(100), logger, ) if err != nil { @@ -801,7 +802,6 @@ func newTestServer(transactor core.Writer, testName string) *apiserver.Dispersal mt := meterer.NewMeterer(meterer.Config{ ChainReadTimeout: 1 * time.Second, OnchainUpdateInterval: 1 * time.Second, - OffchainPruneInterval: 1 * time.Second, }, mockState, store, logger) err = mt.ChainPaymentState.RefreshOnchainPaymentState(context.Background()) if err != nil { diff --git a/disperser/apiserver/server_v2.go b/disperser/apiserver/server_v2.go index 6deb20abc9..2261015cb0 100644 --- a/disperser/apiserver/server_v2.go +++ b/disperser/apiserver/server_v2.go @@ -57,7 +57,6 @@ type DispersalServerV2 struct { onchainState atomic.Pointer[OnchainState] maxNumSymbolsPerBlob uint64 onchainStateRefreshInterval time.Duration - OffchainPruneInterval time.Duration metrics *metricsV2 } @@ -73,7 +72,6 @@ func NewDispersalServerV2( prover encoding.Prover, maxNumSymbolsPerBlob uint64, onchainStateRefreshInterval time.Duration, - OffchainPruneInterval time.Duration, _logger logging.Logger, registry *prometheus.Registry, ) (*DispersalServerV2, error) { @@ -117,7 +115,6 @@ func NewDispersalServerV2( maxNumSymbolsPerBlob: maxNumSymbolsPerBlob, onchainStateRefreshInterval: onchainStateRefreshInterval, - OffchainPruneInterval: OffchainPruneInterval, metrics: newAPIServerV2Metrics(registry), }, nil diff --git a/disperser/apiserver/server_v2_test.go b/disperser/apiserver/server_v2_test.go index 13276126b7..872526e669 100644 --- a/disperser/apiserver/server_v2_test.go +++ b/disperser/apiserver/server_v2_test.go @@ -483,6 +483,7 @@ func newTestServerV2(t *testing.T) *testComponents { table_names[1], table_names[2], uint64(100), + uint64(100), logger, ) if err != nil { @@ -492,7 +493,6 @@ func newTestServerV2(t *testing.T) *testComponents { meterer := meterer.NewMeterer(meterer.Config{ ChainReadTimeout: 1 * time.Second, OnchainUpdateInterval: 1 * time.Second, - OffchainPruneInterval: 1 * time.Second, }, mockState, store, logger) chainReader.On("GetCurrentBlockNumber").Return(uint32(100), nil) @@ -521,7 +521,6 @@ func newTestServerV2(t *testing.T) *testComponents { prover, 10, time.Hour, - time.Hour, logger, prometheus.NewRegistry()) assert.NoError(t, err) diff --git a/disperser/cmd/apiserver/config.go b/disperser/cmd/apiserver/config.go index 72ab684025..0396d36790 100644 --- a/disperser/cmd/apiserver/config.go +++ b/disperser/cmd/apiserver/config.go @@ -37,6 +37,7 @@ type Config struct { EnablePaymentMeterer bool OnchainUpdateInterval int OffchainMaxOnDemandStorage int + OffchainMaxReservedPeriods int ChainReadTimeout int ReservationsTableName string OnDemandTableName string @@ -47,7 +48,6 @@ type Config struct { MaxBlobSize int MaxNumSymbolsPerBlob uint OnchainStateRefreshInterval time.Duration - OffchainPruneInterval time.Duration BLSOperatorStateRetrieverAddr string EigenDAServiceManagerAddr string @@ -127,8 +127,8 @@ func NewConfig(ctx *cli.Context) (Config, error) { MaxBlobSize: ctx.GlobalInt(flags.MaxBlobSize.Name), MaxNumSymbolsPerBlob: ctx.GlobalUint(flags.MaxNumSymbolsPerBlob.Name), OnchainStateRefreshInterval: ctx.GlobalDuration(flags.OnchainStateRefreshInterval.Name), - OffchainPruneInterval: ctx.GlobalDuration(flags.OffchainPruneInterval.Name), OffchainMaxOnDemandStorage: ctx.GlobalInt(flags.OffchainMaxOnDemandStorage.Name), + OffchainMaxReservedPeriods: ctx.GlobalInt(flags.OffchainMaxReservedPeriods.Name), BLSOperatorStateRetrieverAddr: ctx.GlobalString(flags.BlsOperatorStateRetrieverFlag.Name), EigenDAServiceManagerAddr: ctx.GlobalString(flags.EigenDAServiceManagerFlag.Name), diff --git a/disperser/cmd/apiserver/flags/flags.go b/disperser/cmd/apiserver/flags/flags.go index 755115c9e5..d82a864353 100644 --- a/disperser/cmd/apiserver/flags/flags.go +++ b/disperser/cmd/apiserver/flags/flags.go @@ -148,13 +148,6 @@ var ( EnvVar: common.PrefixEnvVar(envVarPrefix, "ONCHAIN_STATE_REFRESH_INTERVAL"), Value: 2 * time.Minute, } - OffchainPruneInterval = cli.DurationFlag{ - Name: common.PrefixFlag(FlagPrefix, "offchain-state-pruning-interval"), - Usage: "The interval at which to prune the outdated offchain state. This flag is only relevant in v2", - Required: false, - EnvVar: common.PrefixEnvVar(envVarPrefix, "OFFCHAIN_STATE_PRUNING_INTERVAL"), - Value: 2 * time.Minute, - } OffchainMaxOnDemandStorage = cli.UintFlag{ Name: common.PrefixFlag(FlagPrefix, "offchain-max-on-demand-storage"), Usage: "max number of on-demand payments to store in the off-chain state", @@ -162,6 +155,13 @@ var ( EnvVar: common.PrefixEnvVar(envVarPrefix, "OFFCHAIN_MAX_ON_DEMAND_STORAGE"), Required: false, } + OffchainMaxReservedPeriods = cli.UintFlag{ + Name: common.PrefixFlag(FlagPrefix, "offchain-max-reserved-periods"), + Usage: "max number of reserved periods to store in the off-chain state for all account and global reserved periods", + Value: 100, + EnvVar: common.PrefixEnvVar(envVarPrefix, "OFFCHAIN_MAX_RESERVED_PERIODS"), + Required: false, + } MaxNumSymbolsPerBlob = cli.UintFlag{ Name: common.PrefixFlag(FlagPrefix, "max-num-symbols-per-blob"), Usage: "max number of symbols per blob. This flag is only relevant in v2", @@ -273,8 +273,8 @@ var optionalFlags = []cli.Flag{ OnDemandTableName, GlobalRateTableName, OnchainStateRefreshInterval, - OffchainPruneInterval, OffchainMaxOnDemandStorage, + OffchainMaxReservedPeriods, MaxNumSymbolsPerBlob, PprofHttpPort, EnablePprof, diff --git a/disperser/cmd/apiserver/main.go b/disperser/cmd/apiserver/main.go index ad20b7c7de..03b1d86818 100644 --- a/disperser/cmd/apiserver/main.go +++ b/disperser/cmd/apiserver/main.go @@ -101,7 +101,6 @@ func RunDisperserServer(ctx *cli.Context) error { mtConfig := mt.Config{ ChainReadTimeout: time.Duration(config.ChainReadTimeout) * time.Second, OnchainUpdateInterval: time.Duration(config.OnchainUpdateInterval) * time.Second, - OffchainPruneInterval: time.Duration(config.OffchainPruneInterval) * time.Second, } paymentChainState, err := mt.NewOnchainPaymentState(context.Background(), transactor) @@ -118,6 +117,7 @@ func RunDisperserServer(ctx *cli.Context) error { config.OnDemandTableName, config.GlobalRateTableName, uint64(config.OffchainMaxOnDemandStorage), + uint64(config.OffchainMaxReservedPeriods), logger, ) if err != nil { @@ -182,7 +182,6 @@ func RunDisperserServer(ctx *cli.Context) error { prover, uint64(config.MaxNumSymbolsPerBlob), config.OnchainStateRefreshInterval, - config.OffchainPruneInterval, logger, reg, ) diff --git a/test/integration_test.go b/test/integration_test.go index 6d79456a52..60a68735e4 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -284,6 +284,7 @@ func mustMakeDisperser(t *testing.T, cst core.IndexedChainState, store disperser table_names[1], table_names[2], uint64(100), + uint64(100), logger, ) if err != nil { @@ -298,7 +299,6 @@ func mustMakeDisperser(t *testing.T, cst core.IndexedChainState, store disperser mt := meterer.NewMeterer(meterer.Config{ ChainReadTimeout: 1 * time.Second, OnchainUpdateInterval: 1 * time.Second, - OffchainPruneInterval: 1 * time.Second, }, mockState, offchainStore, logger) server := apiserver.NewDispersalServer(serverConfig, store, tx, logger, disperserMetrics, grpcprom.NewServerMetrics(), mt, ratelimiter, rateConfig, testMaxBlobSize)