diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000..6133218a0f
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,6 @@
+# Auto-generated files should not be rendered in diffs.
+api/docs/*.md linguist-generated=true
+api/docs/*.html linguist-generated=true
+*.pb.go linguist-generated=true
+inabox/deploy/env_vars.go linguist-generated=true
+# contracts/bindings/*.go linguist-generated=true Enable once bindings are checked in CI
\ No newline at end of file
diff --git a/.github/workflows/codeQL-scanning.yaml b/.github/workflows/codeql-scanning.yaml
similarity index 94%
rename from .github/workflows/codeQL-scanning.yaml
rename to .github/workflows/codeql-scanning.yaml
index e3511339e9..46bca7add3 100644
--- a/.github/workflows/codeQL-scanning.yaml
+++ b/.github/workflows/codeql-scanning.yaml
@@ -1,4 +1,4 @@
-name: "CodeQL scanning"
+name: "codeql-scanning"
on:
push:
@@ -19,8 +19,6 @@ on:
- 'api/**'
- '.github/codeql/**'
- '.github/workflows/codeql-analysis.yml'
- paths-ignore:
- - 'contracts/bindings/**'
schedule:
- cron: '0 9 * * *'
diff --git a/Dockerfile b/Dockerfile
index 0140473fc3..64f146dd8f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -76,6 +76,7 @@ RUN --mount=type=cache,target=/go/pkg/mod \
# Controller build stage
FROM common-builder AS controller-builder
+COPY node/auth /app/node/auth
WORKDIR /app/disperser
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
diff --git a/Makefile b/Makefile
index 033741a38d..f092560872 100644
--- a/Makefile
+++ b/Makefile
@@ -59,6 +59,9 @@ dataapi-build:
unit-tests:
./test.sh
+fuzz-tests:
+ go test --fuzz=FuzzParseSignatureKMS -fuzztime=5m ./common
+
integration-tests-churner:
go test -v ./churner/tests
diff --git a/api/builder/generate-docs.sh b/api/builder/generate-docs.sh
index 011e0da37b..85badf0234 100755
--- a/api/builder/generate-docs.sh
+++ b/api/builder/generate-docs.sh
@@ -26,61 +26,92 @@ done
# Sort the proto files alphabetically. Required for deterministic output.
IFS=$'\n' PROTO_FILES=($(sort <<<"${PROTO_FILES[*]}")); unset IFS
-# Generate unified HTML doc
-echo "Generating unified HTML documentation..."
-docker run --rm \
- -v "${DOCS_DIR}":/out \
- -v "${PROTO_DIR}":/protos \
- pseudomuto/protoc-gen-doc \
- "${PROTO_FILES[@]}" \
- --doc_opt=html,eigenda-protos.html 2>/dev/null
-
-if [ $? -ne 0 ]; then
- echo "Failed to generate unified HTML documentation."
- exit 1
-fi
+PID_LIST=()
-# Generate unified markdown doc
-echo "Generating unified markdown documentation..."
-docker run --rm \
- -v "${DOCS_DIR}":/out \
- -v "${PROTO_DIR}":/protos \
- pseudomuto/protoc-gen-doc \
- "${PROTO_FILES[@]}" \
- --doc_opt=markdown,eigenda-protos.md 2>/dev/null
-
-if [ $? -ne 0 ]; then
- echo "Failed to generate unified markdown documentation."
- exit 1
-fi
+# Generate unified HTML doc
+generateUnifiedHTML() {
+ echo "Generating unified HTML documentation..."
+ docker run --rm \
+ -v "${DOCS_DIR}":/out \
+ -v "${PROTO_DIR}":/protos \
+ pseudomuto/protoc-gen-doc \
+ "${PROTO_FILES[@]}" \
+ --doc_opt=html,eigenda-protos.html 2>/dev/null &
-# Generate individual markdown/HTML docs
-for PROTO_FILE in "${PROTO_FILES[@]}"; do
- PROTO_NAME=$(basename "${PROTO_FILE}" .proto)
+ if [ $? -ne 0 ]; then
+ echo "Failed to generate unified HTML documentation."
+ exit 1
+ fi
+}
+generateUnifiedHTML &
+PID_LIST+=($!)
- echo "Generating markdown documentation for ${PROTO_NAME}..."
+# Generate unified markdown doc
+generateUnifiedMD() {
+ echo "Generating unified markdown documentation..."
docker run --rm \
-v "${DOCS_DIR}":/out \
-v "${PROTO_DIR}":/protos \
pseudomuto/protoc-gen-doc \
- "${PROTO_FILE}" \
- --doc_opt=markdown,"${PROTO_NAME}.md" 2>/dev/null
+ "${PROTO_FILES[@]}" \
+ --doc_opt=markdown,eigenda-protos.md 2>/dev/null
if [ $? -ne 0 ]; then
- echo "Failed to generate documentation for ${PROTO_NAME}."
+ echo "Failed to generate unified markdown documentation."
exit 1
fi
+}
+generateUnifiedMD &
+PID_LIST+=($!)
- echo "Generating HTML documentation for ${PROTO_NAME}..."
+# Generate markdown docs for a proto file. First argument is the name of the proto file, second is the path.
+generateMD() {
+ echo "Generating markdown documentation for ${1}..."
docker run --rm \
-v "${DOCS_DIR}":/out \
-v "${PROTO_DIR}":/protos \
pseudomuto/protoc-gen-doc \
- "${PROTO_FILE}" \
- --doc_opt=html,"${PROTO_NAME}.html" 2>/dev/null
+ "${2}" \
+ --doc_opt=markdown,"${1}.md" 2>/dev/null
if [ $? -ne 0 ]; then
- echo "Failed to generate documentation for ${PROTO_NAME}."
+ echo "Failed to generate documentation for ${1}."
exit 1
fi
-done
\ No newline at end of file
+}
+
+# Generate HTML docs for a proto file. First argument is the name of the proto file, second is the path.
+generateHTML() {
+ echo "Generating HTML documentation for ${1}..."
+ docker run --rm \
+ -v "${DOCS_DIR}":/out \
+ -v "${PROTO_DIR}":/protos \
+ pseudomuto/protoc-gen-doc \
+ "${2}" \
+ --doc_opt=html,"${1}.html" 2>/dev/null
+
+ if [ $? -ne 0 ]; then
+ echo "Failed to generate documentation for ${1}."
+ exit 1
+ fi
+}
+
+# Generate individual markdown/HTML docs
+for PROTO_FILE in "${PROTO_FILES[@]}"; do
+ PROTO_NAME=$(basename "${PROTO_FILE}" .proto)
+
+ generateMD "${PROTO_NAME}" "${PROTO_FILE}" &
+ PID_LIST+=($!)
+
+ generateHTML "${PROTO_NAME}" "${PROTO_FILE}" &
+ PID_LIST+=($!)
+done
+
+# Wait for all processes to finish
+for PID in "${PID_LIST[@]}"; do
+ wait $PID
+ if [ $? -ne 0 ]; then
+ echo "Failed to wait for process $PID."
+ exit 1
+ fi
+done
diff --git a/api/clients/mock/static_request_signer.go b/api/clients/mock/static_request_signer.go
new file mode 100644
index 0000000000..cb05368bcc
--- /dev/null
+++ b/api/clients/mock/static_request_signer.go
@@ -0,0 +1,30 @@
+package mock
+
+import (
+ "context"
+ "crypto/ecdsa"
+ "github.com/Layr-Labs/eigenda/api/clients/v2"
+ v2 "github.com/Layr-Labs/eigenda/api/grpc/node/v2"
+ "github.com/Layr-Labs/eigenda/node/auth"
+)
+
+var _ clients.DispersalRequestSigner = &staticRequestSigner{}
+
+// StaticRequestSigner is a DispersalRequestSigner that signs requests with a static key (i.e. it doesn't use AWS KMS).
+// Useful for testing.
+type staticRequestSigner struct {
+ key *ecdsa.PrivateKey
+}
+
+func NewStaticRequestSigner(key *ecdsa.PrivateKey) clients.DispersalRequestSigner {
+ return &staticRequestSigner{
+ key: key,
+ }
+}
+
+func (s *staticRequestSigner) SignStoreChunksRequest(
+ ctx context.Context,
+ request *v2.StoreChunksRequest) ([]byte, error) {
+
+ return auth.SignStoreChunksRequest(s.key, request)
+}
diff --git a/api/clients/v2/accountant.go b/api/clients/v2/accountant.go
index 02dd58d9f6..be0030d3f8 100644
--- a/api/clients/v2/accountant.go
+++ b/api/clients/v2/accountant.go
@@ -26,7 +26,7 @@ type Accountant struct {
// local accounting
// contains 3 bins; circular wrapping of indices
- binRecords []BinRecord
+ periodRecords []PeriodRecord
usageLock sync.Mutex
cumulativePayment *big.Int
@@ -34,18 +34,15 @@ type Accountant struct {
numBins uint32
}
-type BinRecord struct {
+type PeriodRecord struct {
Index uint32
Usage uint64
}
func NewAccountant(accountID string, reservation *core.ReservedPayment, onDemand *core.OnDemandPayment, reservationWindow uint32, pricePerSymbol uint32, minNumSymbols uint32, numBins uint32) *Accountant {
- //TODO: client storage; currently every instance starts fresh but on-chain or a small store makes more sense
- // Also client is currently responsible for supplying network params, we need to add RPC in order to be automatic
- // There's a subsequent PR that handles populating the accountant with on-chain state from the disperser
- binRecords := make([]BinRecord, numBins)
- for i := range binRecords {
- binRecords[i] = BinRecord{Index: uint32(i), Usage: 0}
+ periodRecords := make([]PeriodRecord, numBins)
+ for i := range periodRecords {
+ periodRecords[i] = PeriodRecord{Index: uint32(i), Usage: 0}
}
a := Accountant{
accountID: accountID,
@@ -54,7 +51,7 @@ func NewAccountant(accountID string, reservation *core.ReservedPayment, onDemand
reservationWindow: reservationWindow,
pricePerSymbol: pricePerSymbol,
minNumSymbols: minNumSymbols,
- binRecords: binRecords,
+ periodRecords: periodRecords,
cumulativePayment: big.NewInt(0),
numBins: max(numBins, uint32(meterer.MinNumBins)),
}
@@ -74,22 +71,22 @@ func (a *Accountant) BlobPaymentInfo(ctx context.Context, numSymbols uint32, quo
a.usageLock.Lock()
defer a.usageLock.Unlock()
- relativeBinRecord := a.GetRelativeBinRecord(currentReservationPeriod)
- relativeBinRecord.Usage += symbolUsage
+ relativePeriodRecord := a.GetRelativePeriodRecord(currentReservationPeriod)
+ relativePeriodRecord.Usage += symbolUsage
// first attempt to use the active reservation
binLimit := a.reservation.SymbolsPerSecond * uint64(a.reservationWindow)
- if relativeBinRecord.Usage <= binLimit {
+ if relativePeriodRecord.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)
+ overflowPeriodRecord := a.GetRelativePeriodRecord(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 overflowPeriodRecord.Usage == 0 && relativePeriodRecord.Usage-symbolUsage < binLimit && symbolUsage <= binLimit {
+ overflowPeriodRecord.Usage += relativePeriodRecord.Usage - binLimit
if err := QuorumCheck(quorumNumbers, a.reservation.QuorumNumbers); err != nil {
return 0, big.NewInt(0), err
}
@@ -98,7 +95,7 @@ func (a *Accountant) BlobPaymentInfo(ctx context.Context, numSymbols uint32, quo
// reservation not available, rollback reservation records, attempt on-demand
//todo: rollback on-demand if disperser respond with some type of rejection?
- relativeBinRecord.Usage -= symbolUsage
+ relativePeriodRecord.Usage -= symbolUsage
incrementRequired := big.NewInt(int64(a.PaymentCharged(numSymbols)))
a.cumulativePayment.Add(a.cumulativePayment, incrementRequired)
if a.cumulativePayment.Cmp(a.onDemand.CumulativePayment) <= 0 {
@@ -143,16 +140,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) GetRelativePeriodRecord(index uint32) *PeriodRecord {
relativeIndex := index % a.numBins
- if a.binRecords[relativeIndex].Index != uint32(index) {
- a.binRecords[relativeIndex] = BinRecord{
+ if a.periodRecords[relativeIndex].Index != uint32(index) {
+ a.periodRecords[relativeIndex] = PeriodRecord{
Index: uint32(index),
Usage: 0,
}
}
- return &a.binRecords[relativeIndex]
+ return &a.periodRecords[relativeIndex]
}
// SetPaymentState sets the accountant's state from the disperser's response
@@ -214,18 +211,18 @@ func (a *Accountant) SetPaymentState(paymentState *disperser_rpc.GetPaymentState
}
}
- binRecords := make([]BinRecord, len(paymentState.GetBinRecords()))
- for i, record := range paymentState.GetBinRecords() {
+ periodRecords := make([]PeriodRecord, len(paymentState.GetPeriodRecords()))
+ for i, record := range paymentState.GetPeriodRecords() {
if record == nil {
- binRecords[i] = BinRecord{Index: 0, Usage: 0}
+ periodRecords[i] = PeriodRecord{Index: 0, Usage: 0}
} else {
- binRecords[i] = BinRecord{
+ periodRecords[i] = PeriodRecord{
Index: record.Index,
Usage: record.Usage,
}
}
}
- a.binRecords = binRecords
+ a.periodRecords = periodRecords
return nil
}
diff --git a/api/clients/v2/accountant_test.go b/api/clients/v2/accountant_test.go
index ac8672ff5f..ba71c7a5b3 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, []PeriodRecord{{Index: 0, Usage: 0}, {Index: 1, Usage: 0}, {Index: 2, Usage: 0}}, accountant.periodRecords)
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.periodRecords)), 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.periodRecords)), 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.periodRecords)), 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.periodRecords)), 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.periodRecords)), 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.periodRecords)), 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.periodRecords)
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.periodRecords)), 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.periodRecords)), 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.periodRecords)), 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.periodRecords)), 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.periodRecords)), 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.periodRecords)), true)
}
func TestQuorumCheck(t *testing.T) {
@@ -431,7 +431,7 @@ func TestQuorumCheck(t *testing.T) {
}
}
-func mapRecordUsage(records []BinRecord) []uint64 {
+func mapRecordUsage(records []PeriodRecord) []uint64 {
return []uint64{records[0].Usage, records[1].Usage, records[2].Usage}
}
diff --git a/api/clients/v2/dispersal_request_signer.go b/api/clients/v2/dispersal_request_signer.go
new file mode 100644
index 0000000000..4ab4a9e091
--- /dev/null
+++ b/api/clients/v2/dispersal_request_signer.go
@@ -0,0 +1,62 @@
+package clients
+
+import (
+ "context"
+ "crypto/ecdsa"
+ "fmt"
+ grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2"
+ "github.com/Layr-Labs/eigenda/api/hashing"
+ aws2 "github.com/Layr-Labs/eigenda/common/aws"
+ "github.com/aws/aws-sdk-go-v2/aws"
+ "github.com/aws/aws-sdk-go-v2/service/kms"
+)
+
+// DispersalRequestSigner encapsulates the logic for signing GetChunks requests.
+type DispersalRequestSigner interface {
+ // SignStoreChunksRequest signs a StoreChunksRequest. Does not modify the request
+ // (i.e. it does not insert the signature).
+ SignStoreChunksRequest(ctx context.Context, request *grpc.StoreChunksRequest) ([]byte, error)
+}
+
+var _ DispersalRequestSigner = &requestSigner{}
+
+type requestSigner struct {
+ keyID string
+ publicKey *ecdsa.PublicKey
+ keyManager *kms.Client
+}
+
+// NewDispersalRequestSigner creates a new DispersalRequestSigner.
+func NewDispersalRequestSigner(
+ ctx context.Context,
+ region string,
+ endpoint string,
+ keyID string) (DispersalRequestSigner, error) {
+
+ keyManager := kms.New(kms.Options{
+ Region: region,
+ BaseEndpoint: aws.String(endpoint),
+ })
+
+ key, err := aws2.LoadPublicKeyKMS(ctx, keyManager, keyID)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get ecdsa public key: %w", err)
+ }
+
+ return &requestSigner{
+ keyID: keyID,
+ publicKey: key,
+ keyManager: keyManager,
+ }, nil
+}
+
+func (s *requestSigner) SignStoreChunksRequest(ctx context.Context, request *grpc.StoreChunksRequest) ([]byte, error) {
+ hash := hashing.HashStoreChunksRequest(request)
+
+ signature, err := aws2.SignKMS(ctx, s.keyManager, s.keyID, s.publicKey, hash)
+ if err != nil {
+ return nil, fmt.Errorf("failed to sign request: %w", err)
+ }
+
+ return signature, nil
+}
diff --git a/api/clients/v2/dispersal_request_signer_test.go b/api/clients/v2/dispersal_request_signer_test.go
new file mode 100644
index 0000000000..8ef094fe93
--- /dev/null
+++ b/api/clients/v2/dispersal_request_signer_test.go
@@ -0,0 +1,129 @@
+package clients
+
+import (
+ "context"
+ aws2 "github.com/Layr-Labs/eigenda/common/aws"
+ "github.com/Layr-Labs/eigenda/common/testutils/random"
+ "github.com/Layr-Labs/eigenda/inabox/deploy"
+ "github.com/Layr-Labs/eigenda/node/auth"
+ "github.com/aws/aws-sdk-go-v2/aws"
+ "github.com/aws/aws-sdk-go-v2/service/kms"
+ "github.com/aws/aws-sdk-go-v2/service/kms/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ory/dockertest/v3"
+ "github.com/stretchr/testify/require"
+ "log"
+ "os"
+ "path/filepath"
+ "runtime"
+ "testing"
+)
+
+var (
+ dockertestPool *dockertest.Pool
+ dockertestResource *dockertest.Resource
+)
+
+const (
+ localstackPort = "4570"
+ localstackHost = "http://0.0.0.0:4570"
+ region = "us-east-1"
+)
+
+func setup(t *testing.T) {
+ deployLocalStack := !(os.Getenv("DEPLOY_LOCALSTACK") == "false")
+
+ _, b, _, _ := runtime.Caller(0)
+ rootPath := filepath.Join(filepath.Dir(b), "../../..")
+ changeDirectory(filepath.Join(rootPath, "inabox"))
+
+ if deployLocalStack {
+ var err error
+ dockertestPool, dockertestResource, err = deploy.StartDockertestWithLocalstackContainer(localstackPort)
+ require.NoError(t, err)
+ }
+}
+
+func changeDirectory(path string) {
+ err := os.Chdir(path)
+ if err != nil {
+
+ currentDirectory, err := os.Getwd()
+ if err != nil {
+ log.Printf("Failed to get current directory. Error: %s", err)
+ }
+
+ log.Panicf("Failed to change directories. CWD: %s, Error: %s", currentDirectory, err)
+ }
+
+ newDir, err := os.Getwd()
+ if err != nil {
+ log.Panicf("Failed to get working directory. Error: %s", err)
+ }
+ log.Printf("Current Working Directory: %s\n", newDir)
+}
+
+func teardown() {
+ deployLocalStack := !(os.Getenv("DEPLOY_LOCALSTACK") == "false")
+
+ if deployLocalStack {
+ deploy.PurgeDockertestResources(dockertestPool, dockertestResource)
+ }
+}
+
+func TestRequestSigning(t *testing.T) {
+ rand := random.NewTestRandom(t)
+ setup(t)
+ defer teardown()
+
+ keyManager := kms.New(kms.Options{
+ Region: region,
+ BaseEndpoint: aws.String(localstackHost),
+ })
+
+ for i := 0; i < 10; i++ {
+ createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{
+ KeySpec: types.KeySpecEccSecgP256k1,
+ KeyUsage: types.KeyUsageTypeSignVerify,
+ })
+ require.NoError(t, err)
+
+ keyID := *createKeyOutput.KeyMetadata.KeyId
+
+ key, err := aws2.LoadPublicKeyKMS(context.Background(), keyManager, keyID)
+ require.NoError(t, err)
+
+ publicAddress := crypto.PubkeyToAddress(*key)
+
+ for j := 0; j < 10; j++ {
+ request := auth.RandomStoreChunksRequest(rand)
+ request.Signature = nil
+
+ signer, err := NewDispersalRequestSigner(context.Background(), region, localstackHost, keyID)
+ require.NoError(t, err)
+
+ // Test a valid signature.
+ signature, err := signer.SignStoreChunksRequest(context.Background(), request)
+ require.NoError(t, err)
+
+ require.Nil(t, request.Signature)
+ request.Signature = signature
+ err = auth.VerifyStoreChunksRequest(publicAddress, request)
+ require.NoError(t, err)
+
+ // Changing a byte in the middle of the signature should make the verification fail
+ badSignature := make([]byte, len(signature))
+ copy(badSignature, signature)
+ badSignature[10] = badSignature[10] + 1
+ request.Signature = badSignature
+ err = auth.VerifyStoreChunksRequest(publicAddress, request)
+ require.Error(t, err)
+
+ // Changing a byte in the middle of the request should make the verification fail
+ request.DisperserID = request.DisperserID + 1
+ request.Signature = signature
+ err = auth.VerifyStoreChunksRequest(publicAddress, request)
+ require.Error(t, err)
+ }
+ }
+}
diff --git a/api/clients/v2/node_client.go b/api/clients/v2/node_client.go
index 0d31f699dc..0a379b7bfe 100644
--- a/api/clients/v2/node_client.go
+++ b/api/clients/v2/node_client.go
@@ -3,6 +3,7 @@ package clients
import (
"context"
"fmt"
+ "github.com/Layr-Labs/eigenda/api"
"sync"
commonpb "github.com/Layr-Labs/eigenda/api/grpc/common/v2"
@@ -24,21 +25,23 @@ type NodeClient interface {
}
type nodeClient struct {
- config *NodeClientConfig
- initOnce sync.Once
- conn *grpc.ClientConn
+ config *NodeClientConfig
+ initOnce sync.Once
+ conn *grpc.ClientConn
+ requestSigner DispersalRequestSigner
dispersalClient nodegrpc.DispersalClient
}
var _ NodeClient = (*nodeClient)(nil)
-func NewNodeClient(config *NodeClientConfig) (*nodeClient, error) {
+func NewNodeClient(config *NodeClientConfig, requestSigner DispersalRequestSigner) (NodeClient, error) {
if config == nil || config.Hostname == "" || config.Port == "" {
return nil, fmt.Errorf("invalid config: %v", config)
}
return &nodeClient{
- config: config,
+ config: config,
+ requestSigner: requestSigner,
}, nil
}
@@ -60,8 +63,7 @@ func (c *nodeClient) StoreChunks(ctx context.Context, batch *corev2.Batch) (*cor
}
}
- // Call the gRPC method to store chunks
- response, err := c.dispersalClient.StoreChunks(ctx, &nodegrpc.StoreChunksRequest{
+ request := &nodegrpc.StoreChunksRequest{
Batch: &commonpb.Batch{
Header: &commonpb.BatchHeader{
BatchRoot: batch.BatchHeader.BatchRoot[:],
@@ -69,7 +71,20 @@ func (c *nodeClient) StoreChunks(ctx context.Context, batch *corev2.Batch) (*cor
},
BlobCertificates: blobCerts,
},
- })
+ DisperserID: api.EigenLabsDisperserID, // this will need to be updated when dispersers are decentralized
+ }
+
+ if c.requestSigner != nil {
+ // Sign the request to store chunks
+ signature, err := c.requestSigner.SignStoreChunksRequest(ctx, request)
+ if err != nil {
+ return nil, fmt.Errorf("failed to sign store chunks request: %v", err)
+ }
+ request.Signature = signature
+ }
+
+ // Call the gRPC method to store chunks
+ response, err := c.dispersalClient.StoreChunks(ctx, request)
if err != nil {
return nil, err
}
diff --git a/api/clients/v2/relay_client.go b/api/clients/v2/relay_client.go
index 00ad656392..a808ac8ec7 100644
--- a/api/clients/v2/relay_client.go
+++ b/api/clients/v2/relay_client.go
@@ -6,10 +6,9 @@ import (
"fmt"
"sync"
- "github.com/Layr-Labs/eigenda/core"
- "github.com/Layr-Labs/eigenda/relay/auth"
-
relaygrpc "github.com/Layr-Labs/eigenda/api/grpc/relay"
+ "github.com/Layr-Labs/eigenda/api/hashing"
+ "github.com/Layr-Labs/eigenda/core"
corev2 "github.com/Layr-Labs/eigenda/core/v2"
"github.com/Layr-Labs/eigensdk-go/logging"
"github.com/hashicorp/go-multierror"
@@ -76,7 +75,7 @@ func NewRelayClient(config *RelayClientConfig, logger logging.Logger) (RelayClie
return nil, fmt.Errorf("invalid config: %v", config)
}
- logger.Info("creating relay client", "config", config)
+ logger.Info("creating relay client", "urls", config.Sockets)
initOnce := sync.Map{}
for key := range config.Sockets {
@@ -116,7 +115,7 @@ func (c *relayClient) signGetChunksRequest(ctx context.Context, request *relaygr
return errors.New("no message signer provided in config, cannot sign get chunks request")
}
- hash := auth.HashGetChunksRequest(request)
+ hash := hashing.HashGetChunksRequest(request)
hashArray := [32]byte{}
copy(hashArray[:], hash)
signature, err := c.config.MessageSigner(ctx, hashArray)
diff --git a/api/clients/v2/retrieval_client.go b/api/clients/v2/retrieval_client.go
index a253bfd79a..4ffc0c5f4b 100644
--- a/api/clients/v2/retrieval_client.go
+++ b/api/clients/v2/retrieval_client.go
@@ -136,6 +136,10 @@ func (r *retrievalClient) GetBlob(ctx context.Context, blobHeader *corev2.BlobHe
indices = append(indices, assignmentIndices...)
}
+ if len(chunks) == 0 {
+ return nil, errors.New("failed to retrieve any chunks")
+ }
+
return r.verifier.Decode(
chunks,
indices,
diff --git a/api/clients/v2/verification/blob_verifier.go b/api/clients/v2/verification/blob_verifier.go
new file mode 100644
index 0000000000..d6a11d350a
--- /dev/null
+++ b/api/clients/v2/verification/blob_verifier.go
@@ -0,0 +1,76 @@
+package verification
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/Layr-Labs/eigenda/common"
+
+ disperser "github.com/Layr-Labs/eigenda/api/grpc/disperser/v2"
+ verifierBindings "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDABlobVerifier"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ gethcommon "github.com/ethereum/go-ethereum/common"
+)
+
+// BlobVerifier is responsible for making eth calls against the BlobVerifier contract to ensure cryptographic and
+// structural integrity of V2 certificates
+//
+// The blob verifier contract is located at https://github.com/Layr-Labs/eigenda/blob/master/contracts/src/core/EigenDABlobVerifier.sol
+type BlobVerifier struct {
+ // go binding around the EigenDABlobVerifier ethereum contract
+ blobVerifierCaller *verifierBindings.ContractEigenDABlobVerifierCaller
+}
+
+// NewBlobVerifier constructs a BlobVerifier
+func NewBlobVerifier(
+ ethClient *common.EthClient, // the eth client, which should already be set up
+ blobVerifierAddress string, // the hex address of the EigenDABlobVerifier contract
+) (*BlobVerifier, error) {
+
+ verifierCaller, err := verifierBindings.NewContractEigenDABlobVerifierCaller(
+ gethcommon.HexToAddress(blobVerifierAddress),
+ *ethClient)
+
+ if err != nil {
+ return nil, fmt.Errorf("bind to verifier contract at %s: %s", blobVerifierAddress, err)
+ }
+
+ return &BlobVerifier{
+ blobVerifierCaller: verifierCaller,
+ }, nil
+}
+
+// VerifyBlobV2FromSignedBatch calls the verifyBlobV2FromSignedBatch view function on the EigenDABlobVerifier contract
+//
+// This method returns nil if the blob is successfully verified. Otherwise, it returns an error.
+//
+// It is the responsibility of the caller to configure a timeout on the ctx, if a timeout is required.
+func (v *BlobVerifier) VerifyBlobV2FromSignedBatch(
+ ctx context.Context,
+ // The signed batch that contains the blob being verified. This is obtained from the disperser, and is used
+ // to verify that the described blob actually exists in a valid batch.
+ signedBatch *disperser.SignedBatch,
+ // Contains all necessary information about the blob, so that it can be verified.
+ blobVerificationProof *disperser.BlobVerificationInfo,
+) error {
+ convertedSignedBatch, err := verifierBindings.ConvertSignedBatch(signedBatch)
+ if err != nil {
+ return fmt.Errorf("convert signed batch: %s", err)
+ }
+
+ convertedBlobVerificationProof, err := verifierBindings.ConvertVerificationProof(blobVerificationProof)
+ if err != nil {
+ return fmt.Errorf("convert blob verification proof: %s", err)
+ }
+
+ err = v.blobVerifierCaller.VerifyBlobV2FromSignedBatch(
+ &bind.CallOpts{Context: ctx},
+ *convertedSignedBatch,
+ *convertedBlobVerificationProof)
+
+ if err != nil {
+ return fmt.Errorf("verify blob v2 from signed batch: %s", err)
+ }
+
+ return nil
+}
diff --git a/api/constants.go b/api/constants.go
new file mode 100644
index 0000000000..0f3772cce7
--- /dev/null
+++ b/api/constants.go
@@ -0,0 +1,4 @@
+package api
+
+// EigenLabsDisperserID is the ID of the disperser that is managed by Eigen Labs.
+const EigenLabsDisperserID = uint32(0)
diff --git a/api/docs/common.html b/api/docs/common.html
index 1c47ca8d1b..522008e9f9 100644
--- a/api/docs/common.html
+++ b/api/docs/common.html
@@ -175,23 +175,19 @@
Table of Contents
- common/v2/common.proto
+ common/common.proto
- M Batch
+ M BlobCommitment
- M BatchHeader
+ M G1Commitment
- M BlobCertificate
-
-
-
- M BlobHeader
+ M PaymentHeader
@@ -207,13 +203,13 @@ Table of Contents
-
common/v2/common.proto Top
+
common/common.proto Top
- Batch
- Batch is a batch of blob certificates
+ BlobCommitment
+ BlobCommitment represents commitment of a specific blob, containing its
KZG commitment, degree proof, the actual degree, and data length in number of symbols.
@@ -223,48 +219,31 @@ Batch
- header
- BatchHeader
+ commitment
+ bytes
- header contains metadata about the batch
+
- blob_certificates
- BlobCertificate
- repeated
- blob_certificates is the list of blob certificates in the batch
+ length_commitment
+ bytes
+
+
-
-
-
-
-
-
-
-
- BatchHeader is the header of a batch of blobs
-
-
-
-
- Field Type Label Description
-
-
-
- batch_root
+ length_proof
bytes
- batch_root is the root of the merkle tree of the hashes of blob certificates in the batch
+
- reference_block_number
- uint64
+ length
+ uint32
- reference_block_number is the block number that the state of the batch is based on for attestation
+
@@ -274,8 +253,8 @@
- BlobCertificate
- BlobCertificate is what gets attested by the network
+ G1Commitment
+
@@ -285,17 +264,17 @@ BlobCertificate
- blob_header
- BlobHeader
+ x
+ bytes
-
+ The X coordinate of the KZG commitment. This is the raw byte representation of the field element.
- relays
- uint32
- repeated
-
+ y
+ bytes
+
+ The Y coordinate of the KZG commitment. This is the raw byte representation of the field element.
@@ -305,7 +284,7 @@ BlobCertificate
-
+
@@ -316,38 +295,32 @@
- version
- uint32
+ account_id
+ string
- Blob version
+ The account ID of the disperser client. This should be a hex-encoded string of the ECSDA public key
+corresponding to the key used by the client to sign the BlobHeader.
- quorum_numbers
+ reservation_period
uint32
- repeated
-
-
-
-
- commitment
- common.BlobCommitment
-
+ The reservation period of the dispersal request.
- payment_header
- common.PaymentHeader
+ cumulative_payment
+ bytes
-
+ The cumulative payment of the dispersal request.
- signature
- bytes
+ salt
+ uint32
- signature over keccak hash of the blob_header that can be verified by blob_header.account_id
+ The salt of the disperser request. This is used to ensure that the payment header is intentionally unique.
diff --git a/api/docs/common.md b/api/docs/common.md
index 967305cb72..4f846da132 100644
--- a/api/docs/common.md
+++ b/api/docs/common.md
@@ -3,84 +3,69 @@
## Table of Contents
-- [common/v2/common.proto](#common_v2_common-proto)
- - [Batch](#common-v2-Batch)
- - [BatchHeader](#common-v2-BatchHeader)
- - [BlobCertificate](#common-v2-BlobCertificate)
- - [BlobHeader](#common-v2-BlobHeader)
+- [common/common.proto](#common_common-proto)
+ - [BlobCommitment](#common-BlobCommitment)
+ - [G1Commitment](#common-G1Commitment)
+ - [PaymentHeader](#common-PaymentHeader)
- [Scalar Value Types](#scalar-value-types)
-
+
Top
-## common/v2/common.proto
+## common/common.proto
-
+
-### Batch
-Batch is a batch of blob certificates
+### BlobCommitment
+BlobCommitment represents commitment of a specific blob, containing its
+KZG commitment, degree proof, the actual degree, and data length in number of symbols.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
-| header | [BatchHeader](#common-v2-BatchHeader) | | header contains metadata about the batch |
-| blob_certificates | [BlobCertificate](#common-v2-BlobCertificate) | repeated | blob_certificates is the list of blob certificates in the batch |
+| commitment | [bytes](#bytes) | | |
+| length_commitment | [bytes](#bytes) | | |
+| length_proof | [bytes](#bytes) | | |
+| length | [uint32](#uint32) | | |
-
+
-### BatchHeader
-BatchHeader is the header of a batch of blobs
+### G1Commitment
-| Field | Type | Label | Description |
-| ----- | ---- | ----- | ----------- |
-| batch_root | [bytes](#bytes) | | batch_root is the root of the merkle tree of the hashes of blob certificates in the batch |
-| reference_block_number | [uint64](#uint64) | | reference_block_number is the block number that the state of the batch is based on for attestation |
-
-
-
-
-
-
-
-
-### BlobCertificate
-BlobCertificate is what gets attested by the network
-
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
-| blob_header | [BlobHeader](#common-v2-BlobHeader) | | |
-| relays | [uint32](#uint32) | repeated | |
+| x | [bytes](#bytes) | | The X coordinate of the KZG commitment. This is the raw byte representation of the field element. |
+| y | [bytes](#bytes) | | The Y coordinate of the KZG commitment. This is the raw byte representation of the field element. |
-
+
-### BlobHeader
+### PaymentHeader
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
-| version | [uint32](#uint32) | | Blob version |
-| quorum_numbers | [uint32](#uint32) | repeated | |
-| commitment | [common.BlobCommitment](#common-BlobCommitment) | | |
-| payment_header | [common.PaymentHeader](#common-PaymentHeader) | | |
-| signature | [bytes](#bytes) | | signature over keccak hash of the blob_header that can be verified by blob_header.account_id |
+| account_id | [string](#string) | | The account ID of the disperser client. This should be a hex-encoded string of the ECSDA public key corresponding to the key used by the client to sign the BlobHeader. |
+| reservation_period | [uint32](#uint32) | | The reservation period of the dispersal request. |
+| cumulative_payment | [bytes](#bytes) | | The cumulative payment of the dispersal request. |
+| salt | [uint32](#uint32) | | The salt of the disperser request. This is used to ensure that the payment header is intentionally unique. |
diff --git a/api/docs/common_v2.html b/api/docs/common_v2.html
new file mode 100644
index 0000000000..3d94f56a0c
--- /dev/null
+++ b/api/docs/common_v2.html
@@ -0,0 +1,559 @@
+
+
+
+
+ Protocol Documentation
+
+
+
+
+
+
+
+
+
+
+ Protocol Documentation
+
+ Table of Contents
+
+
+
+
+
+
+
common/v2/common_v2.proto Top
+
+
+
+
+ Batch
+ Batch is a batch of blob certificates
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ header
+ BatchHeader
+
+ header contains metadata about the batch
+
+
+
+ blob_certificates
+ BlobCertificate
+ repeated
+ blob_certificates is the list of blob certificates in the batch
+
+
+
+
+
+
+
+
+
+
+ BatchHeader is the header of a batch of blobs
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ batch_root
+ bytes
+
+ batch_root is the root of the merkle tree of the hashes of blob certificates in the batch
+
+
+
+ reference_block_number
+ uint64
+
+ reference_block_number is the block number that the state of the batch is based on for attestation
+
+
+
+
+
+
+
+
+
+ BlobCertificate
+ BlobCertificate is what gets attested by the network
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ blob_header
+ BlobHeader
+
+
+
+
+
+ relays
+ uint32
+ repeated
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ version
+ uint32
+
+ Blob version
+
+
+
+ quorum_numbers
+ uint32
+ repeated
+
+
+
+
+ commitment
+ common.BlobCommitment
+
+
+
+
+
+ payment_header
+ common.PaymentHeader
+
+
+
+
+
+ signature
+ bytes
+
+ signature over keccak hash of the blob_header that can be verified by blob_header.account_id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Scalar Value Types
+
+
+ .proto Type Notes C++ Java Python Go C# PHP Ruby
+
+
+
+
+ double
+
+ double
+ double
+ float
+ float64
+ double
+ float
+ Float
+
+
+
+ float
+
+ float
+ float
+ float
+ float32
+ float
+ float
+ Float
+
+
+
+ int32
+ Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.
+ int32
+ int
+ int
+ int32
+ int
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ int64
+ Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.
+ int64
+ long
+ int/long
+ int64
+ long
+ integer/string
+ Bignum
+
+
+
+ uint32
+ Uses variable-length encoding.
+ uint32
+ int
+ int/long
+ uint32
+ uint
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ uint64
+ Uses variable-length encoding.
+ uint64
+ long
+ int/long
+ uint64
+ ulong
+ integer/string
+ Bignum or Fixnum (as required)
+
+
+
+ sint32
+ Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.
+ int32
+ int
+ int
+ int32
+ int
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ sint64
+ Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.
+ int64
+ long
+ int/long
+ int64
+ long
+ integer/string
+ Bignum
+
+
+
+ fixed32
+ Always four bytes. More efficient than uint32 if values are often greater than 2^28.
+ uint32
+ int
+ int
+ uint32
+ uint
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ fixed64
+ Always eight bytes. More efficient than uint64 if values are often greater than 2^56.
+ uint64
+ long
+ int/long
+ uint64
+ ulong
+ integer/string
+ Bignum
+
+
+
+ sfixed32
+ Always four bytes.
+ int32
+ int
+ int
+ int32
+ int
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ sfixed64
+ Always eight bytes.
+ int64
+ long
+ int/long
+ int64
+ long
+ integer/string
+ Bignum
+
+
+
+ bool
+
+ bool
+ boolean
+ boolean
+ bool
+ bool
+ boolean
+ TrueClass/FalseClass
+
+
+
+ string
+ A string must always contain UTF-8 encoded or 7-bit ASCII text.
+ string
+ String
+ str/unicode
+ string
+ string
+ string
+ String (UTF-8)
+
+
+
+ bytes
+ May contain any arbitrary sequence of bytes.
+ string
+ ByteString
+ str
+ []byte
+ ByteString
+ string
+ String (ASCII-8BIT)
+
+
+
+
+
+
+
diff --git a/api/docs/common_v2.md b/api/docs/common_v2.md
new file mode 100644
index 0000000000..f3b802e4ab
--- /dev/null
+++ b/api/docs/common_v2.md
@@ -0,0 +1,118 @@
+# Protocol Documentation
+
+
+## Table of Contents
+
+- [common/v2/common_v2.proto](#common_v2_common_v2-proto)
+ - [Batch](#common-v2-Batch)
+ - [BatchHeader](#common-v2-BatchHeader)
+ - [BlobCertificate](#common-v2-BlobCertificate)
+ - [BlobHeader](#common-v2-BlobHeader)
+
+- [Scalar Value Types](#scalar-value-types)
+
+
+
+
+Top
+
+## common/v2/common_v2.proto
+
+
+
+
+
+### Batch
+Batch is a batch of blob certificates
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| header | [BatchHeader](#common-v2-BatchHeader) | | header contains metadata about the batch |
+| blob_certificates | [BlobCertificate](#common-v2-BlobCertificate) | repeated | blob_certificates is the list of blob certificates in the batch |
+
+
+
+
+
+
+
+
+### BatchHeader
+BatchHeader is the header of a batch of blobs
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| batch_root | [bytes](#bytes) | | batch_root is the root of the merkle tree of the hashes of blob certificates in the batch |
+| reference_block_number | [uint64](#uint64) | | reference_block_number is the block number that the state of the batch is based on for attestation |
+
+
+
+
+
+
+
+
+### BlobCertificate
+BlobCertificate is what gets attested by the network
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| blob_header | [BlobHeader](#common-v2-BlobHeader) | | |
+| relays | [uint32](#uint32) | repeated | |
+
+
+
+
+
+
+
+
+### BlobHeader
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| version | [uint32](#uint32) | | Blob version |
+| quorum_numbers | [uint32](#uint32) | repeated | |
+| commitment | [common.BlobCommitment](#common-BlobCommitment) | | |
+| payment_header | [common.PaymentHeader](#common-PaymentHeader) | | |
+| signature | [bytes](#bytes) | | signature over keccak hash of the blob_header that can be verified by blob_header.account_id |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Scalar Value Types
+
+| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby |
+| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- |
+| double | | double | double | float | float64 | double | float | Float |
+| float | | float | float | float | float32 | float | float | Float |
+| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
+| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum |
+| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) |
+| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) |
+| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
+| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum |
+| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) |
+| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum |
+| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
+| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum |
+| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass |
+| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) |
+| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) |
+
diff --git a/api/docs/disperser_v2.html b/api/docs/disperser_v2.html
index 50b3685d72..b7d9f23109 100644
--- a/api/docs/disperser_v2.html
+++ b/api/docs/disperser_v2.html
@@ -182,10 +182,6 @@ Table of Contents
M Attestation
-
- M BinRecord
-
-
M BlobCommitmentReply
@@ -226,6 +222,10 @@ Table of Contents
M PaymentGlobalParams
+
+ M PeriodRecord
+
+
M Reservation
@@ -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.
-
-
-
-
- Field Type Label Description
-
-
-
-
- index
- uint32
-
-
-
-
-
- usage
- uint64
-
-
-
-
-
-
-
-
-
-
-
BlobCommitmentReply
@@ -585,8 +554,8 @@ GetPaymentStateReply
- bin_records
- BinRecord
+ period_records
+ PeriodRecord
repeated
off-chain account reservation usage records
@@ -703,6 +672,37 @@ PaymentGlobalParams
+ PeriodRecord
+ PeriodRecord 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
+
+
+
+
+
+ usage
+ uint64
+
+
+
+
+
+
+
+
+
+
+
Reservation
diff --git a/api/docs/disperser_v2.md b/api/docs/disperser_v2.md
index 0ca84f3b0d..eeadbd563c 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)
@@ -16,6 +15,7 @@
- [GetPaymentStateReply](#disperser-v2-GetPaymentStateReply)
- [GetPaymentStateRequest](#disperser-v2-GetPaymentStateRequest)
- [PaymentGlobalParams](#disperser-v2-PaymentGlobalParams)
+ - [PeriodRecord](#disperser-v2-PeriodRecord)
- [Reservation](#disperser-v2-Reservation)
- [SignedBatch](#disperser-v2-SignedBatch)
@@ -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 |
+| period_records | [PeriodRecord](#disperser-v2-PeriodRecord) | 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 |
@@ -237,6 +220,23 @@ GetPaymentStateRequest contains parameters to query the payment state of an acco
+
+
+### PeriodRecord
+PeriodRecord 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) | | |
+
+
+
+
+
+
### Reservation
diff --git a/api/docs/eigenda-protos.html b/api/docs/eigenda-protos.html
index 3981fbfb15..9fb7ed8a63 100644
--- a/api/docs/eigenda-protos.html
+++ b/api/docs/eigenda-protos.html
@@ -229,7 +229,7 @@ Table of Contents
- common/v2/common.proto
+ common/v2/common_v2.proto
+
+ M PeriodRecord
+
+
M Reservation
@@ -626,7 +626,7 @@ Table of Contents
- retriever/v2/retriever.proto
+ retriever/v2/retriever_v2.proto
@@ -1013,7 +1013,7 @@
-
common/v2/common.proto Top
+
common/v2/common_v2.proto Top
@@ -2015,37 +2015,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.
-
-
-
-
- Field Type Label Description
-
-
-
-
- index
- uint32
-
-
-
-
-
- usage
- uint64
-
-
-
-
-
-
-
-
-
-
-
BlobCommitmentReply
@@ -2279,8 +2248,8 @@ GetPaymentStateReply
- bin_records
- BinRecord
+ period_records
+ PeriodRecord
repeated
off-chain account reservation usage records
@@ -2397,6 +2366,37 @@ PaymentGlobalParams
+ PeriodRecord
+ PeriodRecord 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
+
+
+
+
+
+ usage
+ uint64
+
+
+
+
+
+
+
+
+
+
+
Reservation
@@ -3517,7 +3517,7 @@ StoreChunksReply
StoreChunksRequest
-
+ Request that the Node store a batch of chunks.
@@ -3533,6 +3533,46 @@ StoreChunksRequest
batch of blobs to store
+
+ disperserID
+ uint32
+
+ ID of the disperser that is requesting the storage of the batch.
+
+
+
+ signature
+ bytes
+
+ Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature
+is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing.
+
+Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned).
+A reference implementation (golang) can be found at
+https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go
+
+1. digest batch.BatchHeader.BatchRoot
+2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian)
+3. for each certificate in batch.BlobCertificates:
+ a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian)
+ b. for each quorum_number in certificate.BlobHeader.QuorumNumbers:
+ i. digest quorum_number (4 bytes, unsigned big endian)
+ c. digest certificate.BlobHeader.Commitment.Commitment
+ d. digest certificate.BlobHeader.Commitment.LengthCommitment
+ e. digest certificate.BlobHeader.Commitment.LengthProof
+ f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian)
+ g. digest certificate.BlobHeader.PaymentHeader.AccountId
+ h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian)
+ i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment
+ j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian)
+ k. digest certificate.BlobHeader.Signature
+ l. for each relay in certificate.Relays:
+ i. digest relay (4 bytes, unsigned big endian)
+4. digest disperserID (4 bytes, unsigned big endian)
+
+Note that this signature is not included in the hash for obvious reasons.
+
+
@@ -3980,7 +4020,7 @@ Retriever
-
retriever/v2/retriever.proto Top
+
retriever/v2/retriever_v2.proto Top
diff --git a/api/docs/eigenda-protos.md b/api/docs/eigenda-protos.md
index 69f4413969..061eb81d1f 100644
--- a/api/docs/eigenda-protos.md
+++ b/api/docs/eigenda-protos.md
@@ -16,7 +16,7 @@
- [G1Commitment](#common-G1Commitment)
- [PaymentHeader](#common-PaymentHeader)
-- [common/v2/common.proto](#common_v2_common-proto)
+- [common/v2/common_v2.proto](#common_v2_common_v2-proto)
- [Batch](#common-v2-Batch)
- [BatchHeader](#common-v2-BatchHeader)
- [BlobCertificate](#common-v2-BlobCertificate)
@@ -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)
@@ -58,6 +57,7 @@
- [GetPaymentStateReply](#disperser-v2-GetPaymentStateReply)
- [GetPaymentStateRequest](#disperser-v2-GetPaymentStateRequest)
- [PaymentGlobalParams](#disperser-v2-PaymentGlobalParams)
+ - [PeriodRecord](#disperser-v2-PeriodRecord)
- [Reservation](#disperser-v2-Reservation)
- [SignedBatch](#disperser-v2-SignedBatch)
@@ -119,7 +119,7 @@
- [Retriever](#retriever-Retriever)
-- [retriever/v2/retriever.proto](#retriever_v2_retriever-proto)
+- [retriever/v2/retriever_v2.proto](#retriever_v2_retriever_v2-proto)
- [BlobReply](#retriever-v2-BlobReply)
- [BlobRequest](#retriever-v2-BlobRequest)
@@ -305,10 +305,10 @@ KZG commitment, degree proof, the actual degree, and data length in number of sy
-
+
Top
-## common/v2/common.proto
+## common/v2/common_v2.proto
@@ -762,23 +762,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
@@ -900,7 +883,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 |
+| period_records | [PeriodRecord](#disperser-v2-PeriodRecord) | 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 |
@@ -945,6 +928,23 @@ GetPaymentStateRequest contains parameters to query the payment state of an acco
+
+
+### PeriodRecord
+PeriodRecord 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) | | |
+
+
+
+
+
+
### Reservation
@@ -1484,12 +1484,20 @@ Node info request
### StoreChunksRequest
-
+Request that the Node store a batch of chunks.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| batch | [common.v2.Batch](#common-v2-Batch) | | batch of blobs to store |
+| disperserID | [uint32](#uint32) | | ID of the disperser that is requesting the storage of the batch. |
+| signature | [bytes](#bytes) | | Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing.
+
+Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). A reference implementation (golang) can be found at https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go
+
+1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian) k. digest certificate.BlobHeader.Signature l. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) 4. digest disperserID (4 bytes, unsigned big endian)
+
+Note that this signature is not included in the hash for obvious reasons. |
@@ -1743,10 +1751,10 @@ worse cost and performance.
-
+
Top
-## retriever/v2/retriever.proto
+## retriever/v2/retriever_v2.proto
diff --git a/api/docs/node_v2.html b/api/docs/node_v2.html
index 22d657aca4..ad54d473b6 100644
--- a/api/docs/node_v2.html
+++ b/api/docs/node_v2.html
@@ -369,7 +369,7 @@ StoreChunksReply
StoreChunksRequest
-
+ Request that the Node store a batch of chunks.
@@ -385,6 +385,46 @@ StoreChunksRequest
batch of blobs to store
+
+ disperserID
+ uint32
+
+ ID of the disperser that is requesting the storage of the batch.
+
+
+
+ signature
+ bytes
+
+ Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature
+is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing.
+
+Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned).
+A reference implementation (golang) can be found at
+https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go
+
+1. digest batch.BatchHeader.BatchRoot
+2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian)
+3. for each certificate in batch.BlobCertificates:
+ a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian)
+ b. for each quorum_number in certificate.BlobHeader.QuorumNumbers:
+ i. digest quorum_number (4 bytes, unsigned big endian)
+ c. digest certificate.BlobHeader.Commitment.Commitment
+ d. digest certificate.BlobHeader.Commitment.LengthCommitment
+ e. digest certificate.BlobHeader.Commitment.LengthProof
+ f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian)
+ g. digest certificate.BlobHeader.PaymentHeader.AccountId
+ h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian)
+ i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment
+ j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian)
+ k. digest certificate.BlobHeader.Signature
+ l. for each relay in certificate.Relays:
+ i. digest relay (4 bytes, unsigned big endian)
+4. digest disperserID (4 bytes, unsigned big endian)
+
+Note that this signature is not included in the hash for obvious reasons.
+
+
diff --git a/api/docs/node_v2.md b/api/docs/node_v2.md
index 27dbaace45..46d43c7d41 100644
--- a/api/docs/node_v2.md
+++ b/api/docs/node_v2.md
@@ -103,12 +103,20 @@ Node info request
### StoreChunksRequest
-
+Request that the Node store a batch of chunks.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| batch | [common.v2.Batch](#common-v2-Batch) | | batch of blobs to store |
+| disperserID | [uint32](#uint32) | | ID of the disperser that is requesting the storage of the batch. |
+| signature | [bytes](#bytes) | | Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing.
+
+Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned). A reference implementation (golang) can be found at https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go
+
+1. digest batch.BatchHeader.BatchRoot 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian) 3. for each certificate in batch.BlobCertificates: a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian) b. for each quorum_number in certificate.BlobHeader.QuorumNumbers: i. digest quorum_number (4 bytes, unsigned big endian) c. digest certificate.BlobHeader.Commitment.Commitment d. digest certificate.BlobHeader.Commitment.LengthCommitment e. digest certificate.BlobHeader.Commitment.LengthProof f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian) g. digest certificate.BlobHeader.PaymentHeader.AccountId h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian) i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian) k. digest certificate.BlobHeader.Signature l. for each relay in certificate.Relays: i. digest relay (4 bytes, unsigned big endian) 4. digest disperserID (4 bytes, unsigned big endian)
+
+Note that this signature is not included in the hash for obvious reasons. |
diff --git a/api/docs/retriever.html b/api/docs/retriever.html
index c5de205af5..07a0cef13d 100644
--- a/api/docs/retriever.html
+++ b/api/docs/retriever.html
@@ -175,22 +175,22 @@ Table of Contents
- retriever/v2/retriever.proto
+ retriever/retriever.proto
@@ -203,12 +203,12 @@ Table of Contents
-
retriever/v2/retriever.proto Top
+
retriever/retriever.proto Top
- BlobReply
+ BlobReply
@@ -232,7 +232,7 @@ BlobReply
- BlobRequest
+ BlobRequest
@@ -243,10 +243,20 @@ BlobRequest
- blob_header
- common.v2.BlobHeader
+ batch_header_hash
+ bytes
+
+ The hash of the ReducedBatchHeader defined onchain, see:
+https://github.com/Layr-Labs/eigenda/blob/master/contracts/src/interfaces/IEigenDAServiceManager.sol#L43
+This identifies the batch that this blob belongs to.
+
+
+
+ blob_index
+ uint32
- header of the blob to be retrieved
+ Which blob in the batch this is requesting for (note: a batch is logically an
+ordered list of blobs).
@@ -277,8 +287,8 @@ BlobRequest
- Retriever
- The Retriever is a service for retrieving chunks corresponding to a blob from
the EigenDA operator nodes and reconstructing the original blob from the chunks.
This is a client-side library that the users are supposed to operationalize.
Note: Users generally have two ways to retrieve a blob from EigenDA V2:
1) Retrieve from the relay that the blob is assigned to: the API
is Relay.GetBlob() as defined in api/proto/relay/relay.proto
2) Retrieve directly from the EigenDA Nodes, which is supported by this Retriever.
The Relay.GetBlob() (the 1st approach) is generally faster and cheaper as the
relay manages the blobs that it has processed, whereas the Retriever.RetrieveBlob()
(the 2nd approach here) removes the need to trust the relay, with the downside of
worse cost and performance.
+ Retriever
+ The Retriever is a service for retrieving chunks corresponding to a blob from
the EigenDA operator nodes and reconstructing the original blob from the chunks.
This is a client-side library that the users are supposed to operationalize.
Note: Users generally have two ways to retrieve a blob from EigenDA:
1) Retrieve from the Disperser that the user initially used for dispersal: the API
is Disperser.RetrieveBlob() as defined in api/proto/disperser/disperser.proto
2) Retrieve directly from the EigenDA Nodes, which is supported by this Retriever.
The Disperser.RetrieveBlob() (the 1st approach) is generally faster and cheaper as the
Disperser manages the blobs that it has processed, whereas the Retriever.RetrieveBlob()
(the 2nd approach here) removes the need to trust the Disperser, with the downside of
worse cost and performance.
Method Name Request Type Response Type Description
@@ -287,8 +297,8 @@ Retriever
RetrieveBlob
- BlobRequest
- BlobReply
+ BlobRequest
+ BlobReply
This fans out request to EigenDA Nodes to retrieve the chunks and returns the
reconstructed original blob in response.
diff --git a/api/docs/retriever.md b/api/docs/retriever.md
index 27452d2f43..d78b27fe97 100644
--- a/api/docs/retriever.md
+++ b/api/docs/retriever.md
@@ -3,24 +3,24 @@
## Table of Contents
-- [retriever/v2/retriever.proto](#retriever_v2_retriever-proto)
- - [BlobReply](#retriever-v2-BlobReply)
- - [BlobRequest](#retriever-v2-BlobRequest)
+- [retriever/retriever.proto](#retriever_retriever-proto)
+ - [BlobReply](#retriever-BlobReply)
+ - [BlobRequest](#retriever-BlobRequest)
- - [Retriever](#retriever-v2-Retriever)
+ - [Retriever](#retriever-Retriever)
- [Scalar Value Types](#scalar-value-types)
-
+
Top
-## retriever/v2/retriever.proto
+## retriever/retriever.proto
-
+
### BlobReply
@@ -35,7 +35,7 @@
-
+
### BlobRequest
@@ -43,7 +43,8 @@
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
-| blob_header | [common.v2.BlobHeader](#common-v2-BlobHeader) | | header of the blob to be retrieved |
+| batch_header_hash | [bytes](#bytes) | | The hash of the ReducedBatchHeader defined onchain, see: https://github.com/Layr-Labs/eigenda/blob/master/contracts/src/interfaces/IEigenDAServiceManager.sol#L43 This identifies the batch that this blob belongs to. |
+| blob_index | [uint32](#uint32) | | Which blob in the batch this is requesting for (note: a batch is logically an ordered list of blobs). |
| reference_block_number | [uint32](#uint32) | | The Ethereum block number at which the batch for this blob was constructed. |
| quorum_id | [uint32](#uint32) | | Which quorum of the blob this is requesting for (note a blob can participate in multiple quorums). |
@@ -58,26 +59,26 @@
-
+
### Retriever
The Retriever is a service for retrieving chunks corresponding to a blob from
the EigenDA operator nodes and reconstructing the original blob from the chunks.
This is a client-side library that the users are supposed to operationalize.
-Note: Users generally have two ways to retrieve a blob from EigenDA V2:
- 1) Retrieve from the relay that the blob is assigned to: the API
- is Relay.GetBlob() as defined in api/proto/relay/relay.proto
+Note: Users generally have two ways to retrieve a blob from EigenDA:
+ 1) Retrieve from the Disperser that the user initially used for dispersal: the API
+ is Disperser.RetrieveBlob() as defined in api/proto/disperser/disperser.proto
2) Retrieve directly from the EigenDA Nodes, which is supported by this Retriever.
-The Relay.GetBlob() (the 1st approach) is generally faster and cheaper as the
-relay manages the blobs that it has processed, whereas the Retriever.RetrieveBlob()
-(the 2nd approach here) removes the need to trust the relay, with the downside of
+The Disperser.RetrieveBlob() (the 1st approach) is generally faster and cheaper as the
+Disperser manages the blobs that it has processed, whereas the Retriever.RetrieveBlob()
+(the 2nd approach here) removes the need to trust the Disperser, with the downside of
worse cost and performance.
| Method Name | Request Type | Response Type | Description |
| ----------- | ------------ | ------------- | ------------|
-| RetrieveBlob | [BlobRequest](#retriever-v2-BlobRequest) | [BlobReply](#retriever-v2-BlobReply) | This fans out request to EigenDA Nodes to retrieve the chunks and returns the reconstructed original blob in response. |
+| RetrieveBlob | [BlobRequest](#retriever-BlobRequest) | [BlobReply](#retriever-BlobReply) | This fans out request to EigenDA Nodes to retrieve the chunks and returns the reconstructed original blob in response. |
diff --git a/api/docs/retriever_v2.html b/api/docs/retriever_v2.html
new file mode 100644
index 0000000000..99b362c9ce
--- /dev/null
+++ b/api/docs/retriever_v2.html
@@ -0,0 +1,493 @@
+
+
+
+
+ Protocol Documentation
+
+
+
+
+
+
+
+
+
+
+ Protocol Documentation
+
+ Table of Contents
+
+
+
+
+
+
+
retriever/v2/retriever_v2.proto Top
+
+
+
+
+ BlobReply
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ data
+ bytes
+
+ The blob retrieved and reconstructed from the EigenDA Nodes per BlobRequest.
+
+
+
+
+
+
+
+
+
+ BlobRequest
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ blob_header
+ common.v2.BlobHeader
+
+ header of the blob to be retrieved
+
+
+
+ reference_block_number
+ uint32
+
+ The Ethereum block number at which the batch for this blob was constructed.
+
+
+
+ quorum_id
+ uint32
+
+ Which quorum of the blob this is requesting for (note a blob can participate in
+multiple quorums).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Retriever
+ The Retriever is a service for retrieving chunks corresponding to a blob from
the EigenDA operator nodes and reconstructing the original blob from the chunks.
This is a client-side library that the users are supposed to operationalize.
Note: Users generally have two ways to retrieve a blob from EigenDA V2:
1) Retrieve from the relay that the blob is assigned to: the API
is Relay.GetBlob() as defined in api/proto/relay/relay.proto
2) Retrieve directly from the EigenDA Nodes, which is supported by this Retriever.
The Relay.GetBlob() (the 1st approach) is generally faster and cheaper as the
relay manages the blobs that it has processed, whereas the Retriever.RetrieveBlob()
(the 2nd approach here) removes the need to trust the relay, with the downside of
worse cost and performance.
+
+
+ Method Name Request Type Response Type Description
+
+
+
+
+ RetrieveBlob
+ BlobRequest
+ BlobReply
+ This fans out request to EigenDA Nodes to retrieve the chunks and returns the
+reconstructed original blob in response.
+
+
+
+
+
+
+
+
+ Scalar Value Types
+
+
+ .proto Type Notes C++ Java Python Go C# PHP Ruby
+
+
+
+
+ double
+
+ double
+ double
+ float
+ float64
+ double
+ float
+ Float
+
+
+
+ float
+
+ float
+ float
+ float
+ float32
+ float
+ float
+ Float
+
+
+
+ int32
+ Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.
+ int32
+ int
+ int
+ int32
+ int
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ int64
+ Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.
+ int64
+ long
+ int/long
+ int64
+ long
+ integer/string
+ Bignum
+
+
+
+ uint32
+ Uses variable-length encoding.
+ uint32
+ int
+ int/long
+ uint32
+ uint
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ uint64
+ Uses variable-length encoding.
+ uint64
+ long
+ int/long
+ uint64
+ ulong
+ integer/string
+ Bignum or Fixnum (as required)
+
+
+
+ sint32
+ Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.
+ int32
+ int
+ int
+ int32
+ int
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ sint64
+ Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.
+ int64
+ long
+ int/long
+ int64
+ long
+ integer/string
+ Bignum
+
+
+
+ fixed32
+ Always four bytes. More efficient than uint32 if values are often greater than 2^28.
+ uint32
+ int
+ int
+ uint32
+ uint
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ fixed64
+ Always eight bytes. More efficient than uint64 if values are often greater than 2^56.
+ uint64
+ long
+ int/long
+ uint64
+ ulong
+ integer/string
+ Bignum
+
+
+
+ sfixed32
+ Always four bytes.
+ int32
+ int
+ int
+ int32
+ int
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ sfixed64
+ Always eight bytes.
+ int64
+ long
+ int/long
+ int64
+ long
+ integer/string
+ Bignum
+
+
+
+ bool
+
+ bool
+ boolean
+ boolean
+ bool
+ bool
+ boolean
+ TrueClass/FalseClass
+
+
+
+ string
+ A string must always contain UTF-8 encoded or 7-bit ASCII text.
+ string
+ String
+ str/unicode
+ string
+ string
+ string
+ String (UTF-8)
+
+
+
+ bytes
+ May contain any arbitrary sequence of bytes.
+ string
+ ByteString
+ str
+ []byte
+ ByteString
+ string
+ String (ASCII-8BIT)
+
+
+
+
+
+
+
diff --git a/api/docs/retriever_v2.md b/api/docs/retriever_v2.md
new file mode 100644
index 0000000000..456a50cce9
--- /dev/null
+++ b/api/docs/retriever_v2.md
@@ -0,0 +1,105 @@
+# Protocol Documentation
+
+
+## Table of Contents
+
+- [retriever/v2/retriever_v2.proto](#retriever_v2_retriever_v2-proto)
+ - [BlobReply](#retriever-v2-BlobReply)
+ - [BlobRequest](#retriever-v2-BlobRequest)
+
+ - [Retriever](#retriever-v2-Retriever)
+
+- [Scalar Value Types](#scalar-value-types)
+
+
+
+
+Top
+
+## retriever/v2/retriever_v2.proto
+
+
+
+
+
+### BlobReply
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| data | [bytes](#bytes) | | The blob retrieved and reconstructed from the EigenDA Nodes per BlobRequest. |
+
+
+
+
+
+
+
+
+### BlobRequest
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| blob_header | [common.v2.BlobHeader](#common-v2-BlobHeader) | | header of the blob to be retrieved |
+| reference_block_number | [uint32](#uint32) | | The Ethereum block number at which the batch for this blob was constructed. |
+| quorum_id | [uint32](#uint32) | | Which quorum of the blob this is requesting for (note a blob can participate in multiple quorums). |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+### Retriever
+The Retriever is a service for retrieving chunks corresponding to a blob from
+the EigenDA operator nodes and reconstructing the original blob from the chunks.
+This is a client-side library that the users are supposed to operationalize.
+
+Note: Users generally have two ways to retrieve a blob from EigenDA V2:
+ 1) Retrieve from the relay that the blob is assigned to: the API
+ is Relay.GetBlob() as defined in api/proto/relay/relay.proto
+ 2) Retrieve directly from the EigenDA Nodes, which is supported by this Retriever.
+
+The Relay.GetBlob() (the 1st approach) is generally faster and cheaper as the
+relay manages the blobs that it has processed, whereas the Retriever.RetrieveBlob()
+(the 2nd approach here) removes the need to trust the relay, with the downside of
+worse cost and performance.
+
+| Method Name | Request Type | Response Type | Description |
+| ----------- | ------------ | ------------- | ------------|
+| RetrieveBlob | [BlobRequest](#retriever-v2-BlobRequest) | [BlobReply](#retriever-v2-BlobReply) | This fans out request to EigenDA Nodes to retrieve the chunks and returns the reconstructed original blob in response. |
+
+
+
+
+
+## Scalar Value Types
+
+| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby |
+| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- |
+| double | | double | double | float | float64 | double | float | Float |
+| float | | float | float | float | float32 | float | float | Float |
+| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
+| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum |
+| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) |
+| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) |
+| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
+| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum |
+| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) |
+| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum |
+| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
+| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum |
+| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass |
+| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) |
+| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) |
+
diff --git a/api/grpc/common/v2/common.pb.go b/api/grpc/common/v2/common_v2.pb.go
similarity index 59%
rename from api/grpc/common/v2/common.pb.go
rename to api/grpc/common/v2/common_v2.pb.go
index 23c3f1dafa..14b71738a6 100644
--- a/api/grpc/common/v2/common.pb.go
+++ b/api/grpc/common/v2/common_v2.pb.go
@@ -2,7 +2,7 @@
// versions:
// protoc-gen-go v1.28.1
// protoc v4.23.4
-// source: common/v2/common.proto
+// source: common/v2/common_v2.proto
package v2
@@ -38,7 +38,7 @@ type BlobHeader struct {
func (x *BlobHeader) Reset() {
*x = BlobHeader{}
if protoimpl.UnsafeEnabled {
- mi := &file_common_v2_common_proto_msgTypes[0]
+ mi := &file_common_v2_common_v2_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -51,7 +51,7 @@ func (x *BlobHeader) String() string {
func (*BlobHeader) ProtoMessage() {}
func (x *BlobHeader) ProtoReflect() protoreflect.Message {
- mi := &file_common_v2_common_proto_msgTypes[0]
+ mi := &file_common_v2_common_v2_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -64,7 +64,7 @@ func (x *BlobHeader) ProtoReflect() protoreflect.Message {
// Deprecated: Use BlobHeader.ProtoReflect.Descriptor instead.
func (*BlobHeader) Descriptor() ([]byte, []int) {
- return file_common_v2_common_proto_rawDescGZIP(), []int{0}
+ return file_common_v2_common_v2_proto_rawDescGZIP(), []int{0}
}
func (x *BlobHeader) GetVersion() uint32 {
@@ -115,7 +115,7 @@ type BlobCertificate struct {
func (x *BlobCertificate) Reset() {
*x = BlobCertificate{}
if protoimpl.UnsafeEnabled {
- mi := &file_common_v2_common_proto_msgTypes[1]
+ mi := &file_common_v2_common_v2_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -128,7 +128,7 @@ func (x *BlobCertificate) String() string {
func (*BlobCertificate) ProtoMessage() {}
func (x *BlobCertificate) ProtoReflect() protoreflect.Message {
- mi := &file_common_v2_common_proto_msgTypes[1]
+ mi := &file_common_v2_common_v2_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -141,7 +141,7 @@ func (x *BlobCertificate) ProtoReflect() protoreflect.Message {
// Deprecated: Use BlobCertificate.ProtoReflect.Descriptor instead.
func (*BlobCertificate) Descriptor() ([]byte, []int) {
- return file_common_v2_common_proto_rawDescGZIP(), []int{1}
+ return file_common_v2_common_v2_proto_rawDescGZIP(), []int{1}
}
func (x *BlobCertificate) GetBlobHeader() *BlobHeader {
@@ -173,7 +173,7 @@ type BatchHeader struct {
func (x *BatchHeader) Reset() {
*x = BatchHeader{}
if protoimpl.UnsafeEnabled {
- mi := &file_common_v2_common_proto_msgTypes[2]
+ mi := &file_common_v2_common_v2_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -186,7 +186,7 @@ func (x *BatchHeader) String() string {
func (*BatchHeader) ProtoMessage() {}
func (x *BatchHeader) ProtoReflect() protoreflect.Message {
- mi := &file_common_v2_common_proto_msgTypes[2]
+ mi := &file_common_v2_common_v2_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -199,7 +199,7 @@ func (x *BatchHeader) ProtoReflect() protoreflect.Message {
// Deprecated: Use BatchHeader.ProtoReflect.Descriptor instead.
func (*BatchHeader) Descriptor() ([]byte, []int) {
- return file_common_v2_common_proto_rawDescGZIP(), []int{2}
+ return file_common_v2_common_v2_proto_rawDescGZIP(), []int{2}
}
func (x *BatchHeader) GetBatchRoot() []byte {
@@ -231,7 +231,7 @@ type Batch struct {
func (x *Batch) Reset() {
*x = Batch{}
if protoimpl.UnsafeEnabled {
- mi := &file_common_v2_common_proto_msgTypes[3]
+ mi := &file_common_v2_common_v2_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -244,7 +244,7 @@ func (x *Batch) String() string {
func (*Batch) ProtoMessage() {}
func (x *Batch) ProtoReflect() protoreflect.Message {
- mi := &file_common_v2_common_proto_msgTypes[3]
+ mi := &file_common_v2_common_v2_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -257,7 +257,7 @@ func (x *Batch) ProtoReflect() protoreflect.Message {
// Deprecated: Use Batch.ProtoReflect.Descriptor instead.
func (*Batch) Descriptor() ([]byte, []int) {
- return file_common_v2_common_proto_rawDescGZIP(), []int{3}
+ return file_common_v2_common_v2_proto_rawDescGZIP(), []int{3}
}
func (x *Batch) GetHeader() *BatchHeader {
@@ -274,68 +274,68 @@ func (x *Batch) GetBlobCertificates() []*BlobCertificate {
return nil
}
-var File_common_v2_common_proto protoreflect.FileDescriptor
-
-var file_common_v2_common_proto_rawDesc = []byte{
- 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x6d, 0x6d,
- 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
- 0x2e, 0x76, 0x32, 0x1a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d,
- 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe1, 0x01, 0x0a, 0x0a, 0x42, 0x6c, 0x6f,
- 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
- 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
- 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x6e, 0x75, 0x6d, 0x62,
- 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x71, 0x75, 0x6f, 0x72, 0x75,
- 0x6d, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x36, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
- 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63,
- 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
- 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74,
- 0x12, 0x3c, 0x0a, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64,
- 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
- 0x6e, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52,
- 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1c,
- 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
- 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x61, 0x0a, 0x0f,
- 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12,
- 0x36, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32,
- 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0a, 0x62, 0x6c, 0x6f,
- 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79,
- 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x22,
- 0x62, 0x0a, 0x0b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1d,
- 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x0c, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x34, 0x0a,
- 0x16, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
- 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x72,
- 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d,
- 0x62, 0x65, 0x72, 0x22, 0x80, 0x01, 0x0a, 0x05, 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, 0x47, 0x0a,
- 0x11, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
- 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 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, 0x10, 0x62, 0x6c, 0x6f, 0x62, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
- 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
- 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4c, 0x61, 0x79, 0x72, 0x2d, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x65,
- 0x69, 0x67, 0x65, 0x6e, 0x64, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f,
- 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
- 0x33,
+var File_common_v2_common_v2_proto protoreflect.FileDescriptor
+
+var file_common_v2_common_v2_proto_rawDesc = []byte{
+ 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x6f, 0x6e, 0x5f, 0x76, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x1a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63,
+ 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe1, 0x01, 0x0a, 0x0a,
+ 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65,
+ 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72,
+ 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x6e,
+ 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x71, 0x75,
+ 0x6f, 0x72, 0x75, 0x6d, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x36, 0x0a, 0x0a, 0x63,
+ 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d,
+ 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68,
+ 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64,
+ 0x65, 0x72, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65,
+ 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x05,
+ 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22,
+ 0x61, 0x0a, 0x0f, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65,
+ 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
+ 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0a,
+ 0x62, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65,
+ 0x6c, 0x61, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61,
+ 0x79, 0x73, 0x22, 0x62, 0x0a, 0x0b, 0x42, 0x61, 0x74, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x65,
+ 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x52, 0x6f, 0x6f, 0x74,
+ 0x12, 0x34, 0x0a, 0x16, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x62, 0x6c,
+ 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04,
+ 0x52, 0x14, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
+ 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x80, 0x01, 0x0a, 0x05, 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, 0x47, 0x0a, 0x11, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 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, 0x10, 0x62, 0x6c, 0x6f, 0x62, 0x43, 0x65, 0x72,
+ 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74,
+ 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4c, 0x61, 0x79, 0x72, 0x2d, 0x4c, 0x61, 0x62,
+ 0x73, 0x2f, 0x65, 0x69, 0x67, 0x65, 0x6e, 0x64, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x72,
+ 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x33,
}
var (
- file_common_v2_common_proto_rawDescOnce sync.Once
- file_common_v2_common_proto_rawDescData = file_common_v2_common_proto_rawDesc
+ file_common_v2_common_v2_proto_rawDescOnce sync.Once
+ file_common_v2_common_v2_proto_rawDescData = file_common_v2_common_v2_proto_rawDesc
)
-func file_common_v2_common_proto_rawDescGZIP() []byte {
- file_common_v2_common_proto_rawDescOnce.Do(func() {
- file_common_v2_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_v2_common_proto_rawDescData)
+func file_common_v2_common_v2_proto_rawDescGZIP() []byte {
+ file_common_v2_common_v2_proto_rawDescOnce.Do(func() {
+ file_common_v2_common_v2_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_v2_common_v2_proto_rawDescData)
})
- return file_common_v2_common_proto_rawDescData
+ return file_common_v2_common_v2_proto_rawDescData
}
-var file_common_v2_common_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
-var file_common_v2_common_proto_goTypes = []interface{}{
+var file_common_v2_common_v2_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_common_v2_common_v2_proto_goTypes = []interface{}{
(*BlobHeader)(nil), // 0: common.v2.BlobHeader
(*BlobCertificate)(nil), // 1: common.v2.BlobCertificate
(*BatchHeader)(nil), // 2: common.v2.BatchHeader
@@ -343,7 +343,7 @@ var file_common_v2_common_proto_goTypes = []interface{}{
(*common.BlobCommitment)(nil), // 4: common.BlobCommitment
(*common.PaymentHeader)(nil), // 5: common.PaymentHeader
}
-var file_common_v2_common_proto_depIdxs = []int32{
+var file_common_v2_common_v2_proto_depIdxs = []int32{
4, // 0: common.v2.BlobHeader.commitment:type_name -> common.BlobCommitment
5, // 1: common.v2.BlobHeader.payment_header:type_name -> common.PaymentHeader
0, // 2: common.v2.BlobCertificate.blob_header:type_name -> common.v2.BlobHeader
@@ -356,13 +356,13 @@ var file_common_v2_common_proto_depIdxs = []int32{
0, // [0:5] is the sub-list for field type_name
}
-func init() { file_common_v2_common_proto_init() }
-func file_common_v2_common_proto_init() {
- if File_common_v2_common_proto != nil {
+func init() { file_common_v2_common_v2_proto_init() }
+func file_common_v2_common_v2_proto_init() {
+ if File_common_v2_common_v2_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
- file_common_v2_common_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ file_common_v2_common_v2_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BlobHeader); i {
case 0:
return &v.state
@@ -374,7 +374,7 @@ func file_common_v2_common_proto_init() {
return nil
}
}
- file_common_v2_common_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ file_common_v2_common_v2_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BlobCertificate); i {
case 0:
return &v.state
@@ -386,7 +386,7 @@ func file_common_v2_common_proto_init() {
return nil
}
}
- file_common_v2_common_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ file_common_v2_common_v2_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BatchHeader); i {
case 0:
return &v.state
@@ -398,7 +398,7 @@ func file_common_v2_common_proto_init() {
return nil
}
}
- file_common_v2_common_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ file_common_v2_common_v2_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Batch); i {
case 0:
return &v.state
@@ -415,18 +415,18 @@ func file_common_v2_common_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
- RawDescriptor: file_common_v2_common_proto_rawDesc,
+ RawDescriptor: file_common_v2_common_v2_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
- GoTypes: file_common_v2_common_proto_goTypes,
- DependencyIndexes: file_common_v2_common_proto_depIdxs,
- MessageInfos: file_common_v2_common_proto_msgTypes,
+ GoTypes: file_common_v2_common_v2_proto_goTypes,
+ DependencyIndexes: file_common_v2_common_v2_proto_depIdxs,
+ MessageInfos: file_common_v2_common_v2_proto_msgTypes,
}.Build()
- File_common_v2_common_proto = out.File
- file_common_v2_common_proto_rawDesc = nil
- file_common_v2_common_proto_goTypes = nil
- file_common_v2_common_proto_depIdxs = nil
+ File_common_v2_common_v2_proto = out.File
+ file_common_v2_common_v2_proto_rawDesc = nil
+ file_common_v2_common_v2_proto_goTypes = nil
+ file_common_v2_common_v2_proto_depIdxs = nil
}
diff --git a/api/grpc/disperser/v2/disperser_v2.pb.go b/api/grpc/disperser/v2/disperser_v2.pb.go
index 4fb9718f65..13cdbf9287 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"`
+ PeriodRecords []*PeriodRecord `protobuf:"bytes,2,rep,name=period_records,json=periodRecords,proto3" json:"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) GetPeriodRecords() []*PeriodRecord {
if x != nil {
- return x.BinRecords
+ return x.PeriodRecords
}
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
+// PeriodRecord 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 PeriodRecord 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 *PeriodRecord) Reset() {
+ *x = PeriodRecord{}
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 *PeriodRecord) String() string {
return protoimpl.X.MessageStringOf(x)
}
-func (*BinRecord) ProtoMessage() {}
+func (*PeriodRecord) ProtoMessage() {}
-func (x *BinRecord) ProtoReflect() protoreflect.Message {
+func (x *PeriodRecord) 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 PeriodRecord.ProtoReflect.Descriptor instead.
+func (*PeriodRecord) Descriptor() ([]byte, []int) {
return file_disperser_v2_disperser_v2_proto_rawDescGZIP(), []int{13}
}
-func (x *BinRecord) GetIndex() uint32 {
+func (x *PeriodRecord) GetIndex() uint32 {
if x != nil {
return x.Index
}
return 0
}
-func (x *BinRecord) GetUsage() uint64 {
+func (x *PeriodRecord) GetUsage() uint64 {
if x != nil {
return x.Usage
}
@@ -1005,173 +1005,174 @@ var file_disperser_v2_disperser_v2_proto_rawDesc = []byte{
0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x5f, 0x76, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x1a,
0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70,
- 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x16, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f,
- 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x61, 0x0a, 0x13,
- 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x36, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x62, 0x5f,
- 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63,
- 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61,
- 0x64, 0x65, 0x72, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22,
- 0x60, 0x0a, 0x11, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52,
- 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 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, 0x06,
- 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b,
- 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x65,
- 0x79, 0x22, 0x2e, 0x0a, 0x11, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b,
- 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x65,
- 0x79, 0x22, 0xdb, 0x01, 0x0a, 0x0f, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
- 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 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,
- 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3c, 0x0a, 0x0c, 0x73, 0x69, 0x67, 0x6e, 0x65,
- 0x64, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e,
- 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x69, 0x67,
- 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64,
- 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x58, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x76, 0x65,
- 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18,
- 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65,
- 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x56,
- 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x22,
- 0x2b, 0x0a, 0x15, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e,
- 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x56, 0x0a, 0x13,
- 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65,
- 0x70, 0x6c, 0x79, 0x12, 0x3f, 0x0a, 0x0f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x63, 0x6f, 0x6d, 0x6d,
- 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63,
- 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
- 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x62, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
- 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x55, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65,
- 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d,
- 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1c, 0x0a,
- 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
- 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xd1, 0x02, 0x0a, 0x14,
- 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52,
- 0x65, 0x70, 0x6c, 0x79, 0x12, 0x55, 0x0a, 0x15, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f,
- 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e,
- 0x76, 0x32, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
- 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x13, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x47,
- 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x62,
- 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
- 0x32, 0x17, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e,
- 0x42, 0x69, 0x6e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x0a, 0x62, 0x69, 0x6e, 0x52, 0x65,
- 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73,
- 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69,
- 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65,
- 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11,
- 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e,
- 0x74, 0x12, 0x3c, 0x0a, 0x1a, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x63, 0x75, 0x6d,
- 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18,
- 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x18, 0x6f, 0x6e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x75,
- 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x22,
- 0x7a, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x2e,
- 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
- 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68,
- 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x3b,
- 0x0a, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e,
- 0x76, 0x32, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b,
- 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa5, 0x01, 0x0a, 0x14,
- 0x42, 0x6c, 0x6f, 0x62, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x45, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x63, 0x65, 0x72,
- 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
- 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x43,
- 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x62,
- 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62,
- 0x6c, 0x6f, 0x62, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
- 0x09, 0x62, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e,
- 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x03, 0x20,
- 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72,
- 0x6f, 0x6f, 0x66, 0x22, 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,
+ 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f,
+ 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x5f, 0x76, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
+ 0x61, 0x0a, 0x13, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x36, 0x0a, 0x0b, 0x62, 0x6c,
+ 0x6f, 0x62, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x15, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62,
+ 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64,
+ 0x65, 0x72, 0x22, 0x60, 0x0a, 0x11, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x42, 0x6c,
+ 0x6f, 0x62, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c,
+ 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 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, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f,
+ 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x6c, 0x6f,
+ 0x62, 0x4b, 0x65, 0x79, 0x22, 0x2e, 0x0a, 0x11, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74,
+ 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f,
+ 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x6c, 0x6f,
+ 0x62, 0x4b, 0x65, 0x79, 0x22, 0xdb, 0x01, 0x0a, 0x0f, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x74, 0x61,
+ 0x74, 0x75, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74,
+ 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 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, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3c, 0x0a, 0x0c, 0x73, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e,
+ 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x0b, 0x73, 0x69, 0x67,
+ 0x6e, 0x65, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x58, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x62,
+ 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e,
+ 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65,
+ 0x72, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x56, 0x65, 0x72, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x14, 0x62, 0x6c,
+ 0x6f, 0x62, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e,
+ 0x66, 0x6f, 0x22, 0x2b, 0x0a, 0x15, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64,
+ 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22,
+ 0x56, 0x0a, 0x13, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e,
+ 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3f, 0x0a, 0x0f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x63,
+ 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x62, 0x6c, 0x6f, 0x62, 0x43, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x55, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x50, 0x61,
+ 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64,
+ 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xda,
+ 0x02, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61,
+ 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x55, 0x0a, 0x15, 0x70, 0x61, 0x79, 0x6d, 0x65,
+ 0x6e, 0x74, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73,
+ 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x47, 0x6c, 0x6f,
+ 0x62, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x13, 0x70, 0x61, 0x79, 0x6d, 0x65,
+ 0x6e, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x41,
+ 0x0a, 0x0e, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73,
+ 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73,
+ 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x52, 0x65, 0x63, 0x6f,
+ 0x72, 0x64, 0x52, 0x0d, 0x70, 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, 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,
+ 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, 0x3a, 0x0a, 0x0c, 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 (
@@ -1203,7 +1204,7 @@ var file_disperser_v2_disperser_v2_proto_goTypes = []interface{}{
(*Attestation)(nil), // 11: disperser.v2.Attestation
(*PaymentGlobalParams)(nil), // 12: disperser.v2.PaymentGlobalParams
(*Reservation)(nil), // 13: disperser.v2.Reservation
- (*BinRecord)(nil), // 14: disperser.v2.BinRecord
+ (*PeriodRecord)(nil), // 14: disperser.v2.PeriodRecord
(*v2.BlobHeader)(nil), // 15: common.v2.BlobHeader
(*common.BlobCommitment)(nil), // 16: common.BlobCommitment
(*v2.BatchHeader)(nil), // 17: common.v2.BatchHeader
@@ -1217,7 +1218,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.period_records:type_name -> disperser.v2.PeriodRecord
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 +1401,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.(*PeriodRecord); i {
case 0:
return &v.state
case 1:
diff --git a/api/grpc/node/v2/node_v2.pb.go b/api/grpc/node/v2/node_v2.pb.go
index 98c98ee40c..0d613615d1 100644
--- a/api/grpc/node/v2/node_v2.pb.go
+++ b/api/grpc/node/v2/node_v2.pb.go
@@ -21,6 +21,7 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
+// Request that the Node store a batch of chunks.
type StoreChunksRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -28,6 +29,36 @@ type StoreChunksRequest struct {
// batch of blobs to store
Batch *v2.Batch `protobuf:"bytes,1,opt,name=batch,proto3" json:"batch,omitempty"`
+ // ID of the disperser that is requesting the storage of the batch.
+ DisperserID uint32 `protobuf:"varint,2,opt,name=disperserID,proto3" json:"disperserID,omitempty"`
+ // Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature
+ // is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing.
+ //
+ // Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned).
+ // A reference implementation (golang) can be found at
+ // https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go
+ //
+ // 1. digest batch.BatchHeader.BatchRoot
+ // 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian)
+ // 3. for each certificate in batch.BlobCertificates:
+ // a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian)
+ // b. for each quorum_number in certificate.BlobHeader.QuorumNumbers:
+ // i. digest quorum_number (4 bytes, unsigned big endian)
+ // c. digest certificate.BlobHeader.Commitment.Commitment
+ // d. digest certificate.BlobHeader.Commitment.LengthCommitment
+ // e. digest certificate.BlobHeader.Commitment.LengthProof
+ // f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian)
+ // g. digest certificate.BlobHeader.PaymentHeader.AccountId
+ // h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian)
+ // i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment
+ // j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian)
+ // k. digest certificate.BlobHeader.Signature
+ // l. for each relay in certificate.Relays:
+ // i. digest relay (4 bytes, unsigned big endian)
+ // 4. digest disperserID (4 bytes, unsigned big endian)
+ //
+ // Note that this signature is not included in the hash for obvious reasons.
+ Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"`
}
func (x *StoreChunksRequest) Reset() {
@@ -69,6 +100,20 @@ func (x *StoreChunksRequest) GetBatch() *v2.Batch {
return nil
}
+func (x *StoreChunksRequest) GetDisperserID() uint32 {
+ if x != nil {
+ return x.DisperserID
+ }
+ return 0
+}
+
+func (x *StoreChunksRequest) GetSignature() []byte {
+ if x != nil {
+ return x.Signature
+ }
+ return nil
+}
+
type StoreChunksReply struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -346,54 +391,58 @@ var File_node_v2_node_v2_proto protoreflect.FileDescriptor
var file_node_v2_node_v2_proto_rawDesc = []byte{
0x0a, 0x15, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x76, 0x32, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x76,
0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32,
- 0x1a, 0x16, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x6d, 0x6d,
- 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3c, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x72,
- 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26,
- 0x0a, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e,
- 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52,
- 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x22, 0x30, 0x0a, 0x10, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43,
- 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69,
- 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73,
- 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x4a, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43,
- 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08,
- 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07,
- 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, 0x72, 0x75,
- 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x6f, 0x72,
- 0x75, 0x6d, 0x49, 0x64, 0x22, 0x28, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b,
- 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73,
- 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x22, 0x11,
- 0x0a, 0x0f, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x22, 0x81, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65,
- 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x61,
- 0x72, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x63, 0x68, 0x12,
- 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x73, 0x12,
- 0x17, 0x0a, 0x07, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d,
- 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x43, 0x70, 0x75, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f,
- 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x65, 0x6d,
- 0x42, 0x79, 0x74, 0x65, 0x73, 0x32, 0x94, 0x01, 0x0a, 0x09, 0x44, 0x69, 0x73, 0x70, 0x65, 0x72,
- 0x73, 0x61, 0x6c, 0x12, 0x47, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e,
- 0x6b, 0x73, 0x12, 0x1b, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x6f,
- 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
- 0x19, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43,
- 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x08,
- 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e,
- 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64,
- 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x32, 0x8e, 0x01, 0x0a,
- 0x09, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x61, 0x6c, 0x12, 0x41, 0x0a, 0x09, 0x47, 0x65,
- 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x19, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76,
- 0x32, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74,
- 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a,
- 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6e, 0x6f, 0x64, 0x65,
- 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f,
- 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x2f, 0x5a,
- 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4c, 0x61, 0x79, 0x72,
- 0x2d, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x69, 0x67, 0x65, 0x6e, 0x64, 0x61, 0x2f, 0x61, 0x70,
- 0x69, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x76, 0x32, 0x62, 0x06,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x1a, 0x19, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x6f, 0x6e, 0x5f, 0x76, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7c, 0x0a, 0x12, 0x53,
+ 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x12, 0x26, 0x0a, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x10, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x74,
+ 0x63, 0x68, 0x52, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, 0x73,
+ 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b,
+ 0x64, 0x69, 0x73, 0x70, 0x65, 0x72, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x73,
+ 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09,
+ 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x30, 0x0a, 0x10, 0x53, 0x74, 0x6f,
+ 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a,
+ 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
+ 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x4a, 0x0a, 0x10, 0x47,
+ 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+ 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0c, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75,
+ 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71,
+ 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x49, 0x64, 0x22, 0x28, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x68,
+ 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x75,
+ 0x6e, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x68, 0x75, 0x6e, 0x6b,
+ 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x22, 0x81, 0x01, 0x0a, 0x0d, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66,
+ 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6d, 0x76, 0x65, 0x72, 0x12, 0x12,
+ 0x0a, 0x04, 0x61, 0x72, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72,
+ 0x63, 0x68, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
+ 0x6f, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x70, 0x75, 0x18, 0x04, 0x20,
+ 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x43, 0x70, 0x75, 0x12, 0x1b, 0x0a, 0x09, 0x6d,
+ 0x65, 0x6d, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08,
+ 0x6d, 0x65, 0x6d, 0x42, 0x79, 0x74, 0x65, 0x73, 0x32, 0x94, 0x01, 0x0a, 0x09, 0x44, 0x69, 0x73,
+ 0x70, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x12, 0x47, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43,
+ 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x1b, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e,
+ 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x6f,
+ 0x72, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12,
+ 0x3e, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6e, 0x6f,
+ 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e,
+ 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x32,
+ 0x8e, 0x01, 0x0a, 0x09, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x61, 0x6c, 0x12, 0x41, 0x0a,
+ 0x09, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x12, 0x19, 0x2e, 0x6e, 0x6f, 0x64,
+ 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e,
+ 0x47, 0x65, 0x74, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00,
+ 0x12, 0x3e, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6e,
+ 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x76, 0x32,
+ 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00,
+ 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4c,
+ 0x61, 0x79, 0x72, 0x2d, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x65, 0x69, 0x67, 0x65, 0x6e, 0x64, 0x61,
+ 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x76,
+ 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/api/grpc/retriever/v2/retriever.pb.go b/api/grpc/retriever/v2/retriever_v2.pb.go
similarity index 51%
rename from api/grpc/retriever/v2/retriever.pb.go
rename to api/grpc/retriever/v2/retriever_v2.pb.go
index d9ef75061c..4b48654f7a 100644
--- a/api/grpc/retriever/v2/retriever.pb.go
+++ b/api/grpc/retriever/v2/retriever_v2.pb.go
@@ -2,7 +2,7 @@
// versions:
// protoc-gen-go v1.28.1
// protoc v4.23.4
-// source: retriever/v2/retriever.proto
+// source: retriever/v2/retriever_v2.proto
package v2
@@ -38,7 +38,7 @@ type BlobRequest struct {
func (x *BlobRequest) Reset() {
*x = BlobRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_retriever_v2_retriever_proto_msgTypes[0]
+ mi := &file_retriever_v2_retriever_v2_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -51,7 +51,7 @@ func (x *BlobRequest) String() string {
func (*BlobRequest) ProtoMessage() {}
func (x *BlobRequest) ProtoReflect() protoreflect.Message {
- mi := &file_retriever_v2_retriever_proto_msgTypes[0]
+ mi := &file_retriever_v2_retriever_v2_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -64,7 +64,7 @@ func (x *BlobRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use BlobRequest.ProtoReflect.Descriptor instead.
func (*BlobRequest) Descriptor() ([]byte, []int) {
- return file_retriever_v2_retriever_proto_rawDescGZIP(), []int{0}
+ return file_retriever_v2_retriever_v2_proto_rawDescGZIP(), []int{0}
}
func (x *BlobRequest) GetBlobHeader() *v2.BlobHeader {
@@ -100,7 +100,7 @@ type BlobReply struct {
func (x *BlobReply) Reset() {
*x = BlobReply{}
if protoimpl.UnsafeEnabled {
- mi := &file_retriever_v2_retriever_proto_msgTypes[1]
+ mi := &file_retriever_v2_retriever_v2_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -113,7 +113,7 @@ func (x *BlobReply) String() string {
func (*BlobReply) ProtoMessage() {}
func (x *BlobReply) ProtoReflect() protoreflect.Message {
- mi := &file_retriever_v2_retriever_proto_msgTypes[1]
+ mi := &file_retriever_v2_retriever_v2_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -126,7 +126,7 @@ func (x *BlobReply) ProtoReflect() protoreflect.Message {
// Deprecated: Use BlobReply.ProtoReflect.Descriptor instead.
func (*BlobReply) Descriptor() ([]byte, []int) {
- return file_retriever_v2_retriever_proto_rawDescGZIP(), []int{1}
+ return file_retriever_v2_retriever_v2_proto_rawDescGZIP(), []int{1}
}
func (x *BlobReply) GetData() []byte {
@@ -136,56 +136,56 @@ func (x *BlobReply) GetData() []byte {
return nil
}
-var File_retriever_v2_retriever_proto protoreflect.FileDescriptor
-
-var file_retriever_v2_retriever_proto_rawDesc = []byte{
- 0x0a, 0x1c, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x72,
- 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c,
- 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x1a, 0x16, 0x63, 0x6f,
- 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70,
- 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x98, 0x01, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x68, 0x65, 0x61,
- 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
- 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
- 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16,
- 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f,
- 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x72, 0x65,
- 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62,
- 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x5f, 0x69, 0x64, 0x18,
- 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x6f, 0x72, 0x75, 0x6d, 0x49, 0x64, 0x22,
- 0x1f, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04,
- 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61,
- 0x32, 0x51, 0x0a, 0x09, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x72, 0x12, 0x44, 0x0a,
- 0x0c, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x19, 0x2e,
- 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f,
- 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x72, 0x65, 0x74, 0x72, 0x69,
- 0x65, 0x76, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 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, 0x72, 0x65, 0x74,
- 0x72, 0x69, 0x65, 0x76, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
- 0x33,
+var File_retriever_v2_retriever_v2_proto protoreflect.FileDescriptor
+
+var file_retriever_v2_retriever_v2_proto_rawDesc = []byte{
+ 0x0a, 0x1f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x72,
+ 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x72, 0x5f, 0x76, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x12, 0x0c, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x1a,
+ 0x19, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
+ 0x6e, 0x5f, 0x76, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x98, 0x01, 0x0a, 0x0b, 0x42,
+ 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x0b, 0x62, 0x6c,
+ 0x6f, 0x62, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x15, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62,
+ 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x62, 0x48, 0x65, 0x61, 0x64,
+ 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f,
+ 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x0d, 0x52, 0x14, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x71, 0x75, 0x6f, 0x72,
+ 0x75, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x6f,
+ 0x72, 0x75, 0x6d, 0x49, 0x64, 0x22, 0x1f, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x70,
+ 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
+ 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0x51, 0x0a, 0x09, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65,
+ 0x76, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x0c, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x42,
+ 0x6c, 0x6f, 0x62, 0x12, 0x19, 0x2e, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x72, 0x2e,
+ 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17,
+ 0x2e, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c,
+ 0x6f, 0x62, 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, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x62,
+ 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
- file_retriever_v2_retriever_proto_rawDescOnce sync.Once
- file_retriever_v2_retriever_proto_rawDescData = file_retriever_v2_retriever_proto_rawDesc
+ file_retriever_v2_retriever_v2_proto_rawDescOnce sync.Once
+ file_retriever_v2_retriever_v2_proto_rawDescData = file_retriever_v2_retriever_v2_proto_rawDesc
)
-func file_retriever_v2_retriever_proto_rawDescGZIP() []byte {
- file_retriever_v2_retriever_proto_rawDescOnce.Do(func() {
- file_retriever_v2_retriever_proto_rawDescData = protoimpl.X.CompressGZIP(file_retriever_v2_retriever_proto_rawDescData)
+func file_retriever_v2_retriever_v2_proto_rawDescGZIP() []byte {
+ file_retriever_v2_retriever_v2_proto_rawDescOnce.Do(func() {
+ file_retriever_v2_retriever_v2_proto_rawDescData = protoimpl.X.CompressGZIP(file_retriever_v2_retriever_v2_proto_rawDescData)
})
- return file_retriever_v2_retriever_proto_rawDescData
+ return file_retriever_v2_retriever_v2_proto_rawDescData
}
-var file_retriever_v2_retriever_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
-var file_retriever_v2_retriever_proto_goTypes = []interface{}{
+var file_retriever_v2_retriever_v2_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_retriever_v2_retriever_v2_proto_goTypes = []interface{}{
(*BlobRequest)(nil), // 0: retriever.v2.BlobRequest
(*BlobReply)(nil), // 1: retriever.v2.BlobReply
(*v2.BlobHeader)(nil), // 2: common.v2.BlobHeader
}
-var file_retriever_v2_retriever_proto_depIdxs = []int32{
+var file_retriever_v2_retriever_v2_proto_depIdxs = []int32{
2, // 0: retriever.v2.BlobRequest.blob_header:type_name -> common.v2.BlobHeader
0, // 1: retriever.v2.Retriever.RetrieveBlob:input_type -> retriever.v2.BlobRequest
1, // 2: retriever.v2.Retriever.RetrieveBlob:output_type -> retriever.v2.BlobReply
@@ -196,13 +196,13 @@ var file_retriever_v2_retriever_proto_depIdxs = []int32{
0, // [0:1] is the sub-list for field type_name
}
-func init() { file_retriever_v2_retriever_proto_init() }
-func file_retriever_v2_retriever_proto_init() {
- if File_retriever_v2_retriever_proto != nil {
+func init() { file_retriever_v2_retriever_v2_proto_init() }
+func file_retriever_v2_retriever_v2_proto_init() {
+ if File_retriever_v2_retriever_v2_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
- file_retriever_v2_retriever_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ file_retriever_v2_retriever_v2_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BlobRequest); i {
case 0:
return &v.state
@@ -214,7 +214,7 @@ func file_retriever_v2_retriever_proto_init() {
return nil
}
}
- file_retriever_v2_retriever_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ file_retriever_v2_retriever_v2_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BlobReply); i {
case 0:
return &v.state
@@ -231,18 +231,18 @@ func file_retriever_v2_retriever_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
- RawDescriptor: file_retriever_v2_retriever_proto_rawDesc,
+ RawDescriptor: file_retriever_v2_retriever_v2_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
- GoTypes: file_retriever_v2_retriever_proto_goTypes,
- DependencyIndexes: file_retriever_v2_retriever_proto_depIdxs,
- MessageInfos: file_retriever_v2_retriever_proto_msgTypes,
+ GoTypes: file_retriever_v2_retriever_v2_proto_goTypes,
+ DependencyIndexes: file_retriever_v2_retriever_v2_proto_depIdxs,
+ MessageInfos: file_retriever_v2_retriever_v2_proto_msgTypes,
}.Build()
- File_retriever_v2_retriever_proto = out.File
- file_retriever_v2_retriever_proto_rawDesc = nil
- file_retriever_v2_retriever_proto_goTypes = nil
- file_retriever_v2_retriever_proto_depIdxs = nil
+ File_retriever_v2_retriever_v2_proto = out.File
+ file_retriever_v2_retriever_v2_proto_rawDesc = nil
+ file_retriever_v2_retriever_v2_proto_goTypes = nil
+ file_retriever_v2_retriever_v2_proto_depIdxs = nil
}
diff --git a/api/grpc/retriever/v2/retriever_grpc.pb.go b/api/grpc/retriever/v2/retriever_v2_grpc.pb.go
similarity index 97%
rename from api/grpc/retriever/v2/retriever_grpc.pb.go
rename to api/grpc/retriever/v2/retriever_v2_grpc.pb.go
index 31fa39a90b..5bf6d7e4b2 100644
--- a/api/grpc/retriever/v2/retriever_grpc.pb.go
+++ b/api/grpc/retriever/v2/retriever_v2_grpc.pb.go
@@ -2,7 +2,7 @@
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v4.23.4
-// source: retriever/v2/retriever.proto
+// source: retriever/v2/retriever_v2.proto
package v2
@@ -109,5 +109,5 @@ var Retriever_ServiceDesc = grpc.ServiceDesc{
},
},
Streams: []grpc.StreamDesc{},
- Metadata: "retriever/v2/retriever.proto",
+ Metadata: "retriever/v2/retriever_v2.proto",
}
diff --git a/api/hashing/node_hashing.go b/api/hashing/node_hashing.go
new file mode 100644
index 0000000000..fafb118724
--- /dev/null
+++ b/api/hashing/node_hashing.go
@@ -0,0 +1,60 @@
+package hashing
+
+import (
+ commonv1 "github.com/Layr-Labs/eigenda/api/grpc/common"
+ common "github.com/Layr-Labs/eigenda/api/grpc/common/v2"
+ grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2"
+ "golang.org/x/crypto/sha3"
+ "hash"
+)
+
+// This file contains code for hashing gRPC messages that are sent to the DA node.
+
+// HashStoreChunksRequest hashes the given StoreChunksRequest.
+func HashStoreChunksRequest(request *grpc.StoreChunksRequest) []byte {
+ hasher := sha3.NewLegacyKeccak256()
+
+ hashBatchHeader(hasher, request.Batch.Header)
+ for _, blobCertificate := range request.Batch.BlobCertificates {
+ hashBlobCertificate(hasher, blobCertificate)
+ }
+ hashUint32(hasher, request.DisperserID)
+
+ return hasher.Sum(nil)
+}
+
+func hashBlobCertificate(hasher hash.Hash, blobCertificate *common.BlobCertificate) {
+ hashBlobHeader(hasher, blobCertificate.BlobHeader)
+ for _, relayID := range blobCertificate.Relays {
+ hashUint32(hasher, relayID)
+ }
+}
+
+func hashBlobHeader(hasher hash.Hash, header *common.BlobHeader) {
+ hashUint32(hasher, header.Version)
+ for _, quorum := range header.QuorumNumbers {
+ hashUint32(hasher, quorum)
+ }
+ hashBlobCommitment(hasher, header.Commitment)
+ hashPaymentHeader(hasher, header.PaymentHeader)
+ hasher.Write(header.Signature)
+}
+
+func hashBatchHeader(hasher hash.Hash, header *common.BatchHeader) {
+ hasher.Write(header.BatchRoot)
+ hashUint64(hasher, header.ReferenceBlockNumber)
+}
+
+func hashBlobCommitment(hasher hash.Hash, commitment *commonv1.BlobCommitment) {
+ hasher.Write(commitment.Commitment)
+ hasher.Write(commitment.LengthCommitment)
+ hasher.Write(commitment.LengthProof)
+ hashUint32(hasher, commitment.Length)
+}
+
+func hashPaymentHeader(hasher hash.Hash, header *commonv1.PaymentHeader) {
+ hasher.Write([]byte(header.AccountId))
+ hashUint32(hasher, header.ReservationPeriod)
+ hasher.Write(header.CumulativePayment)
+ hashUint32(hasher, header.Salt)
+}
diff --git a/api/hashing/relay_hashing.go b/api/hashing/relay_hashing.go
new file mode 100644
index 0000000000..2dad5ecade
--- /dev/null
+++ b/api/hashing/relay_hashing.go
@@ -0,0 +1,38 @@
+package hashing
+
+import (
+ pb "github.com/Layr-Labs/eigenda/api/grpc/relay"
+ "golang.org/x/crypto/sha3"
+)
+
+// This file contains code for hashing gRPC messages that are sent to the relay.
+
+var (
+ iByte = []byte{0x69}
+ rByte = []byte{0x72}
+)
+
+// HashGetChunksRequest hashes the given GetChunksRequest.
+func HashGetChunksRequest(request *pb.GetChunksRequest) []byte {
+ hasher := sha3.NewLegacyKeccak256()
+
+ hasher.Write(request.GetOperatorId())
+ for _, chunkRequest := range request.GetChunkRequests() {
+ if chunkRequest.GetByIndex() != nil {
+ getByIndex := chunkRequest.GetByIndex()
+ hasher.Write(iByte)
+ hasher.Write(getByIndex.BlobKey)
+ for _, index := range getByIndex.ChunkIndices {
+ hashUint32(hasher, index)
+ }
+ } else {
+ getByRange := chunkRequest.GetByRange()
+ hasher.Write(rByte)
+ hasher.Write(getByRange.BlobKey)
+ hashUint32(hasher, getByRange.StartIndex)
+ hashUint32(hasher, getByRange.EndIndex)
+ }
+ }
+
+ return hasher.Sum(nil)
+}
diff --git a/api/hashing/utils.go b/api/hashing/utils.go
new file mode 100644
index 0000000000..bc6f8d289e
--- /dev/null
+++ b/api/hashing/utils.go
@@ -0,0 +1,20 @@
+package hashing
+
+import (
+ "encoding/binary"
+ "hash"
+)
+
+// hashUint32 hashes the given uint32 value.
+func hashUint32(hasher hash.Hash, value uint32) {
+ bytes := make([]byte, 4)
+ binary.BigEndian.PutUint32(bytes, value)
+ hasher.Write(bytes)
+}
+
+// hashUint64 hashes the given uint64 value.
+func hashUint64(hasher hash.Hash, value uint64) {
+ bytes := make([]byte, 8)
+ binary.BigEndian.PutUint64(bytes, value)
+ hasher.Write(bytes)
+}
diff --git a/api/proto/common/v2/common.proto b/api/proto/common/v2/common_v2.proto
similarity index 100%
rename from api/proto/common/v2/common.proto
rename to api/proto/common/v2/common_v2.proto
diff --git a/api/proto/disperser/v2/disperser_v2.proto b/api/proto/disperser/v2/disperser_v2.proto
index 0cae0f0d1b..151c7523fe 100644
--- a/api/proto/disperser/v2/disperser_v2.proto
+++ b/api/proto/disperser/v2/disperser_v2.proto
@@ -1,7 +1,7 @@
syntax = "proto3";
package disperser.v2;
import "common/common.proto";
-import "common/v2/common.proto";
+import "common/v2/common_v2.proto";
option go_package = "github.com/Layr-Labs/eigenda/api/grpc/disperser/v2";
// WARNING: the following RPCs are experimental and subject to change.
@@ -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 PeriodRecord 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
+// PeriodRecord 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 PeriodRecord {
uint32 index = 1;
uint64 usage = 2;
}
diff --git a/api/proto/node/v2/node_v2.proto b/api/proto/node/v2/node_v2.proto
index 7a13378be4..c65de838f9 100644
--- a/api/proto/node/v2/node_v2.proto
+++ b/api/proto/node/v2/node_v2.proto
@@ -1,6 +1,6 @@
syntax = "proto3";
package node.v2;
-import "common/v2/common.proto";
+import "common/v2/common_v2.proto";
option go_package = "github.com/Layr-Labs/eigenda/api/grpc/node/v2";
// The EigenDA Node implements two services, Dispersal and Retrieval, as defined below,
@@ -21,9 +21,42 @@ service Retrieval {
// Requests and replies
+// Request that the Node store a batch of chunks.
message StoreChunksRequest {
// batch of blobs to store
common.v2.Batch batch = 1;
+
+ // ID of the disperser that is requesting the storage of the batch.
+ uint32 disperserID = 2;
+
+ // Signature using the disperser's ECDSA key over keccak hash of the batch. The purpose of this signature
+ // is to prevent hooligans from tricking DA nodes into storing data that they shouldn't be storing.
+ //
+ // Algorithm for computing the hash is as follows. All integer values are serialized in big-endian order (unsigned).
+ // A reference implementation (golang) can be found at
+ // https://github.com/Layr-Labs/eigenda/blob/master/disperser/auth/request_signing.go
+ //
+ // 1. digest batch.BatchHeader.BatchRoot
+ // 2. digest batch.BatchHeader.ReferenceBlockNumber (8 bytes, unsigned big endian)
+ // 3. for each certificate in batch.BlobCertificates:
+ // a. digest certificate.BlobHeader.Version (4 bytes, unsigned big endian)
+ // b. for each quorum_number in certificate.BlobHeader.QuorumNumbers:
+ // i. digest quorum_number (4 bytes, unsigned big endian)
+ // c. digest certificate.BlobHeader.Commitment.Commitment
+ // d. digest certificate.BlobHeader.Commitment.LengthCommitment
+ // e. digest certificate.BlobHeader.Commitment.LengthProof
+ // f. digest certificate.BlobHeader.Commitment.Length (4 bytes, unsigned big endian)
+ // g. digest certificate.BlobHeader.PaymentHeader.AccountId
+ // h. digest certificate.BlobHeader.PaymentHeader.ReservationPeriod (4 bytes, unsigned big endian)
+ // i. digest certificate.BlobHeader.PaymentHeader.CumulativePayment
+ // j. digest certificate.BlobHeader.PaymentHeader.Salt (4 bytes, unsigned big endian)
+ // k. digest certificate.BlobHeader.Signature
+ // l. for each relay in certificate.Relays:
+ // i. digest relay (4 bytes, unsigned big endian)
+ // 4. digest disperserID (4 bytes, unsigned big endian)
+ //
+ // Note that this signature is not included in the hash for obvious reasons.
+ bytes signature = 3;
}
message StoreChunksReply {
diff --git a/api/proto/retriever/v2/retriever.proto b/api/proto/retriever/v2/retriever_v2.proto
similarity index 97%
rename from api/proto/retriever/v2/retriever.proto
rename to api/proto/retriever/v2/retriever_v2.proto
index 87a758ec12..efb092e549 100644
--- a/api/proto/retriever/v2/retriever.proto
+++ b/api/proto/retriever/v2/retriever_v2.proto
@@ -1,6 +1,6 @@
syntax = "proto3";
-import "common/v2/common.proto";
+import "common/v2/common_v2.proto";
option go_package = "github.com/Layr-Labs/eigenda/api/grpc/retriever/v2";
package retriever.v2;
diff --git a/common/aws/kms.go b/common/aws/kms.go
new file mode 100644
index 0000000000..b195210c1c
--- /dev/null
+++ b/common/aws/kms.go
@@ -0,0 +1,167 @@
+package aws
+
+import (
+ "bytes"
+ "context"
+ "crypto/ecdsa"
+ "encoding/asn1"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "github.com/aws/aws-sdk-go-v2/aws"
+ "github.com/aws/aws-sdk-go-v2/service/kms"
+ "github.com/aws/aws-sdk-go-v2/service/kms/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/secp256k1"
+ "math/big"
+)
+
+// This file contains utility methods for working with AWS KMS using ecdsa on the KeySpecEccSecgP256k1 curve.
+// This code was adapted from code in https://github.com/Layr-Labs/eigensdk-go/tree/dev/signerv2
+
+var secp256k1N = crypto.S256().Params().N
+var secp256k1HalfN = new(big.Int).Div(secp256k1N, big.NewInt(2))
+
+type asn1EcPublicKey struct {
+ EcPublicKeyInfo asn1EcPublicKeyInfo
+ PublicKey asn1.BitString
+}
+
+type asn1EcPublicKeyInfo struct {
+ Algorithm asn1.ObjectIdentifier
+ Parameters asn1.ObjectIdentifier
+}
+
+type asn1EcSig struct {
+ R asn1.RawValue
+ S asn1.RawValue
+}
+
+// LoadPublicKeyKMS loads the public key from AWS KMS.
+func LoadPublicKeyKMS(
+ ctx context.Context,
+ client *kms.Client,
+ keyId string) (*ecdsa.PublicKey, error) {
+
+ getPubKeyOutput, err := client.GetPublicKey(ctx, &kms.GetPublicKeyInput{
+ KeyId: aws.String(keyId),
+ })
+ if err != nil {
+ return nil, fmt.Errorf("failed to get public key for KeyId=%s: %w", keyId, err)
+ }
+
+ key, err := ParsePublicKeyKMS(getPubKeyOutput.PublicKey)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse public key for KeyId=%s: %w", keyId, err)
+ }
+
+ return key, nil
+}
+
+// ParsePublicKeyKMS parses the public key from AWS KMS format into an ecdsa.PublicKey.
+func ParsePublicKeyKMS(bytes []byte) (*ecdsa.PublicKey, error) {
+ var asn1pubk asn1EcPublicKey
+ _, err := asn1.Unmarshal(bytes, &asn1pubk)
+ if err != nil {
+ return nil, fmt.Errorf("asn1.Uunmarshal failed: %w", err)
+ }
+
+ key, err := crypto.UnmarshalPubkey(asn1pubk.PublicKey.Bytes)
+ if err != nil {
+ return nil, fmt.Errorf("crypto.UnmarshalPubkey failed: %w", err)
+ }
+
+ return key, nil
+}
+
+func adjustSignatureLength(buffer []byte) []byte {
+
+ if len(buffer) > 32 {
+ buffer = buffer[len(buffer)-32:] // Take last 32 bytes
+ }
+
+ buffer = bytes.TrimLeft(buffer, "\x00")
+ for len(buffer) < 32 {
+ zeroBuf := []byte{0}
+ buffer = append(zeroBuf, buffer...)
+ }
+ return buffer
+}
+
+// SignKMS signs a hash using the provided public using AWS KMS.
+// The signature is returned in the 65-byte format used by Ethereum.
+func SignKMS(
+ ctx context.Context,
+ client *kms.Client,
+ keyId string,
+ publicKey *ecdsa.PublicKey,
+ hash []byte) ([]byte, error) {
+
+ signOutput, err := client.Sign(ctx, &kms.SignInput{
+ KeyId: aws.String(keyId),
+ SigningAlgorithm: types.SigningAlgorithmSpecEcdsaSha256,
+ MessageType: types.MessageTypeDigest,
+ Message: hash,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("failed to sign hash: %w", err)
+ }
+
+ signature, err := ParseSignatureKMS(publicKey, hash, signOutput.Signature)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse signature: %w", err)
+ }
+
+ return signature, nil
+}
+
+// ParseSignatureKMS parses a signature (KeySpecEccSecgP256k1) in the format returned by amazon KMS into the
+// 65-byte format used by Ethereum.
+func ParseSignatureKMS(
+ publicKey *ecdsa.PublicKey,
+ hash []byte,
+ bytes []byte) ([]byte, error) {
+
+ if !secp256k1.S256().IsOnCurve(publicKey.X, publicKey.Y) {
+ return nil, errors.New("public key is not on curve")
+ }
+
+ publicKeyBytes := secp256k1.S256().Marshal(publicKey.X, publicKey.Y)
+
+ var sigAsn1 asn1EcSig
+ _, err := asn1.Unmarshal(bytes, &sigAsn1)
+ if err != nil {
+ return nil, fmt.Errorf("asn1.Unmarshal failed: %w", err)
+ }
+
+ r := sigAsn1.R.Bytes
+ s := sigAsn1.S.Bytes
+
+ // Adjust S value from signature according to Ethereum standard
+ sBigInt := new(big.Int).SetBytes(s)
+ if sBigInt.Cmp(secp256k1HalfN) > 0 {
+ s = new(big.Int).Sub(secp256k1N, sBigInt).Bytes()
+ }
+
+ rsSignature := append(adjustSignatureLength(r), adjustSignatureLength(s)...)
+ signature := append(rsSignature, []byte{0}...)
+
+ recoveredPublicKeyBytes, err := crypto.Ecrecover(hash, signature)
+ if err != nil {
+ return nil, err
+ }
+
+ if hex.EncodeToString(recoveredPublicKeyBytes) != hex.EncodeToString(publicKeyBytes) {
+ signature = append(rsSignature, []byte{1}...)
+ recoveredPublicKeyBytes, err = crypto.Ecrecover(hash, signature)
+ if err != nil {
+ return nil, err
+ }
+
+ if hex.EncodeToString(recoveredPublicKeyBytes) != hex.EncodeToString(publicKeyBytes) {
+ return nil, errors.New("can not reconstruct public key from sig")
+ }
+ }
+
+ return signature, nil
+}
diff --git a/common/aws/kms_fuzz_test.go b/common/aws/kms_fuzz_test.go
new file mode 100644
index 0000000000..a609e9128a
--- /dev/null
+++ b/common/aws/kms_fuzz_test.go
@@ -0,0 +1,245 @@
+package aws
+
+import (
+ "bytes"
+ "crypto/ecdsa"
+ "crypto/sha256"
+ "encoding/asn1"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/crypto"
+)
+
+// ecdsaSignature defines the ASN.1 structure for ECDSA signatures.
+type ecdsaSignature struct {
+ R, S *big.Int
+}
+
+// generateValidSignature generates a valid ECDSA signature and returns the public key, hash, and DER signature.
+func generateValidSignature() (*ecdsa.PublicKey, []byte, []byte, error) {
+ // Generate a secp256k1 ECDSA key pair.
+ privateKey, err := crypto.GenerateKey()
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ publicKey := &privateKey.PublicKey
+
+ // Define a message and compute its SHA-256 hash.
+ message := "Test message for ECDSA signature"
+ hash := sha256.Sum256([]byte(message))
+
+ // Sign the hash using the private key.
+ signatureBytes, err := crypto.Sign(hash[:], privateKey)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ // Convert the signature to DER format.
+ r := new(big.Int).SetBytes(signatureBytes[:32])
+ s := new(big.Int).SetBytes(signatureBytes[32:64])
+
+ // Marshal R and S into ASN.1 DER format.
+ derSignature, err := asn1.Marshal(ecdsaSignature{R: r, S: s})
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ return publicKey, hash[:], derSignature, nil
+}
+
+// defineEdgeCases returns a slice of tuples containing publicKeyBytes, hashBytes, derSignatureBytes
+func defineEdgeCases() [][3][]byte {
+ var edgeCases [][3][]byte
+
+ // Helper: Generate a valid signature to obtain a public key.
+ pubKeyValidBytes, hashValid, derSigValid, err := generateValidSignature()
+ if err != nil {
+ panic("Failed to generate valid signature for edge cases")
+ }
+ publicKeyValid := crypto.FromECDSAPub(pubKeyValidBytes)
+
+ // 1. Malformed Public Keys
+
+ // a. Incorrect length (too short)
+ publicKeyShort := []byte{0x04, 0x01, 0x02}
+ derSignatureValid := derSigValid
+ edgeCases = append(edgeCases, [3][]byte{publicKeyShort, hashValid, derSignatureValid})
+
+ // b. Incorrect prefix
+ publicKeyBadPrefix := make([]byte, 65)
+ publicKeyBadPrefix[0] = 0x05 // Invalid prefix
+ copy(publicKeyBadPrefix[1:], bytes.Repeat([]byte{0x01}, 64))
+ edgeCases = append(edgeCases, [3][]byte{publicKeyBadPrefix, hashValid, derSignatureValid})
+
+ // c. Coordinates not on curve (invalid X, Y)
+ publicKeyInvalidXY := make([]byte, 65)
+ publicKeyInvalidXY[0] = 0x04
+ // Set X and Y to values that are not on the curve
+ copy(publicKeyInvalidXY[1:], bytes.Repeat([]byte{0xFF}, 64))
+ edgeCases = append(edgeCases, [3][]byte{publicKeyInvalidXY, hashValid, derSignatureValid})
+
+ // 2. Malformed Signatures
+
+ // a. Invalid DER encoding (truncated)
+ derSignatureInvalidDER := []byte{0x30, 0x00} // Incomplete DER
+ edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureInvalidDER})
+
+ // b. R too long (33 bytes with leading zero)
+ derSignatureRTooLong := []byte{
+ 0x30, 0x46, // SEQUENCE, length 70
+ 0x02, 0x21, // INTEGER, length 33
+ 0x00, // Leading zero
+ }
+ derSignatureRTooLong = append(derSignatureRTooLong, bytes.Repeat([]byte{0x01}, 32)...) // R
+ derSignatureRTooLong = append(derSignatureRTooLong, 0x02, 0x20) // S INTEGER, length 32
+ derSignatureRTooLong = append(derSignatureRTooLong, bytes.Repeat([]byte{0x02}, 32)...) // S
+ edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureRTooLong})
+
+ // c. S too short (31 bytes)
+ derSignatureSTooShort := []byte{
+ 0x30, 0x44, // SEQUENCE, length 68
+ 0x02, 0x20, // INTEGER, length 32
+ }
+ derSignatureSTooShort = append(derSignatureSTooShort, bytes.Repeat([]byte{0x03}, 32)...) // R
+ derSignatureSTooShort = append(derSignatureSTooShort, 0x02, 0x1F) // S INTEGER, length 31
+ derSignatureSTooShort = append(derSignatureSTooShort, bytes.Repeat([]byte{0x04}, 31)...) // S
+ edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureSTooShort})
+
+ // 3. Invalid Hashes
+
+ // a. Incorrect hash length (too short)
+ hashTooShort := make([]byte, 16)
+ edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashTooShort, derSignatureValid})
+
+ // b. Empty hash
+ hashEmpty := []byte{}
+ edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashEmpty, derSignatureValid})
+
+ // 4. Random Data
+
+ // a. Completely random bytes
+ randomPublicKey := bytes.Repeat([]byte{0xAB}, 65)
+ randomHash := bytes.Repeat([]byte{0xCD}, 32)
+ randomSignature := bytes.Repeat([]byte{0xEF}, 70)
+ edgeCases = append(edgeCases, [3][]byte{randomPublicKey, randomHash, randomSignature})
+
+ // 5. Boundary Conditions
+
+ // a. R equals zero
+ derSignatureRZero, _ := asn1.Marshal(ecdsaSignature{R: big.NewInt(0), S: big.NewInt(1)})
+ edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureRZero})
+
+ // b. S equals N (curve order)
+ secp256k1N := crypto.S256().Params().N
+ derSignatureSEqualsN, _ := asn1.Marshal(ecdsaSignature{R: big.NewInt(1), S: new(big.Int).Set(secp256k1N)})
+ edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureSEqualsN})
+
+ // c. S just above N/2
+ secp256k1HalfN := new(big.Int).Div(crypto.S256().Params().N, big.NewInt(2))
+ sAboveHalfN := new(big.Int).Add(secp256k1HalfN, big.NewInt(1))
+ derSignatureSAboveHalfN, _ := asn1.Marshal(ecdsaSignature{R: big.NewInt(1), S: sAboveHalfN})
+ edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureSAboveHalfN})
+
+ // d. S just below N/2
+ sBelowHalfN := new(big.Int).Sub(secp256k1HalfN, big.NewInt(1))
+ derSignatureSBelowHalfN, _ := asn1.Marshal(ecdsaSignature{R: big.NewInt(1), S: sBelowHalfN})
+ edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureSBelowHalfN})
+
+ // 6. Extra Data
+
+ // a. Extra bytes appended to the signature
+ derSignatureExtra := append(derSignatureValid, 0x00, 0x01, 0x02)
+ edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureExtra})
+
+ // b. Missing bytes in the signature
+ if len(derSignatureValid) > 2 {
+ derSignatureMissing := derSignatureValid[:len(derSignatureValid)-2]
+ edgeCases = append(edgeCases, [3][]byte{publicKeyValid, hashValid, derSignatureMissing})
+ }
+
+ return edgeCases
+}
+
+// FuzzParseSignatureKMS tests the ParseSignatureKMS function with various inputs, including edge cases.
+func FuzzParseSignatureKMS(f *testing.F) {
+ // Generate multiple valid seed inputs
+ for i := 0; i < 5; i++ {
+ publicKey, hash, derSignature, err := generateValidSignature()
+ if err != nil {
+ f.Fatalf("Failed to generate valid signature: %v", err)
+ }
+ publicKeyBytes := crypto.FromECDSAPub(publicKey)
+ f.Add(publicKeyBytes, hash, derSignature)
+ }
+
+ // Incorporate edge cases into the fuzz corpus
+ edgeCases := defineEdgeCases()
+ for _, ec := range edgeCases {
+ f.Add(ec[0], ec[1], ec[2])
+ }
+
+ // Define the fuzzing function
+ f.Fuzz(func(t *testing.T, publicKeyBytes []byte, hashBytes []byte, derSignatureBytes []byte) {
+ // Skip iteration if publicKeyBytes is not the correct length
+ if len(publicKeyBytes) != 65 {
+ return
+ }
+
+ // Attempt to parse the public key
+ pubKey, err := ParsePublicKeyKMS(publicKeyBytes)
+ if err != nil {
+ // Invalid public key; acceptable for fuzzing
+ return
+ }
+
+ // Attempt to parse the signature
+ signature, err := ParseSignatureKMS(pubKey, hashBytes, derSignatureBytes)
+ if err != nil {
+ // Parsing failed; acceptable for fuzzing
+ return
+ }
+
+ // Validate that the signature is exactly 65 bytes
+ if len(signature) != 65 {
+ t.Errorf("Expected signature length 65 bytes, got %d bytes", len(signature))
+ }
+
+ // if the code made it this far, then the pubkey and signature are valid so recovery must work.
+ recoveredPubBytes, err := crypto.Ecrecover(hashBytes, signature)
+ if err != nil {
+ t.Errorf("Ecrecover failed: %v", err)
+ return
+ }
+
+ // Compare the recovered public key with the original
+ if !bytes.Equal(recoveredPubBytes, publicKeyBytes) {
+ // Attempt with the possible V values
+ signatureCheck := false
+ if signature[64] == 27 {
+ recoveredPubBytes, err = crypto.Ecrecover(hashBytes, signature)
+ if err != nil {
+ t.Errorf("Ecrecover failed with V=27: %v", err)
+ } else if !bytes.Equal(recoveredPubBytes, publicKeyBytes) {
+ t.Errorf("Recovered public key does not match original")
+ } else {
+ signatureCheck = true
+ }
+ }
+
+ if !signatureCheck {
+ signature[64] = 28
+ recoveredPubBytes, err = crypto.Ecrecover(hashBytes, signature)
+ if err != nil {
+ t.Errorf("Ecrecover failed with V=28: %v", err)
+ return
+ }
+
+ if !bytes.Equal(recoveredPubBytes, publicKeyBytes) {
+ t.Errorf("Recovered public key does not match original")
+ return
+ }
+ }
+ }
+ })
+}
diff --git a/common/logger_config.go b/common/logger_config.go
index 8ad7e0675c..3bf9d95c6a 100644
--- a/common/logger_config.go
+++ b/common/logger_config.go
@@ -59,6 +59,7 @@ func DefaultLoggerConfig() LoggerConfig {
HandlerOpts: logging.SLoggerOptions{
AddSource: true,
Level: slog.LevelDebug,
+ NoColor: true,
},
}
}
diff --git a/common/testutils/test_utils.go b/common/testutils/test_utils.go
index ad06ecabb1..38daa774b2 100644
--- a/common/testutils/test_utils.go
+++ b/common/testutils/test_utils.go
@@ -86,7 +86,7 @@ func ExecuteWithTimeout(f func(), duration time.Duration, debugInfo ...any) {
}
// RandomBytes generates a random byte slice of a given length.
-// Deprecated: use TestRandom.RandomBytes instead
+// Deprecated: use TestRandom.Bytes instead
func RandomBytes(length int) []byte {
bytes := make([]byte, length)
_, err := rand.Read(bytes)
@@ -97,13 +97,13 @@ func RandomBytes(length int) []byte {
}
// RandomTime generates a random time.
-// Deprecated: use TestRandom.RandomTime instead
+// Deprecated: use TestRandom.Time instead
func RandomTime() time.Time {
return time.Unix(int64(rand.Int31()), 0)
}
// RandomString generates a random string out of printable ASCII characters.
-// Deprecated: use TestRandom.RandomString instead
+// Deprecated: use TestRandom.String instead
func RandomString(length int) string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, length)
diff --git a/contracts/bindings/EigenDABlobVerifier/conversion_utils.go b/contracts/bindings/EigenDABlobVerifier/conversion_utils.go
new file mode 100644
index 0000000000..9789387ffd
--- /dev/null
+++ b/contracts/bindings/EigenDABlobVerifier/conversion_utils.go
@@ -0,0 +1,231 @@
+package contractEigenDABlobVerifier
+
+import (
+ "fmt"
+ "math"
+ "math/big"
+
+ "github.com/Layr-Labs/eigenda/api/grpc/common"
+ commonv2 "github.com/Layr-Labs/eigenda/api/grpc/common/v2"
+ disperserv2 "github.com/Layr-Labs/eigenda/api/grpc/disperser/v2"
+ "github.com/Layr-Labs/eigenda/core"
+ "github.com/consensys/gnark-crypto/ecc/bn254"
+)
+
+func ConvertSignedBatch(inputBatch *disperserv2.SignedBatch) (*SignedBatch, error) {
+ convertedBatchHeader, err := convertBatchHeader(inputBatch.GetHeader())
+ if err != nil {
+ return nil, fmt.Errorf("convert batch header: %s", err)
+ }
+
+ convertedAttestation, err := convertAttestation(inputBatch.GetAttestation())
+ if err != nil {
+ return nil, fmt.Errorf("convert attestation: %s", err)
+ }
+
+ outputSignedBatch := &SignedBatch{
+ BatchHeader: *convertedBatchHeader,
+ Attestation: *convertedAttestation,
+ }
+
+ return outputSignedBatch, nil
+}
+
+func convertBatchHeader(inputHeader *commonv2.BatchHeader) (*BatchHeaderV2, error) {
+ var outputBatchRoot [32]byte
+
+ inputBatchRoot := inputHeader.GetBatchRoot()
+ if len(inputBatchRoot) != 32 {
+ return nil, fmt.Errorf("BatchRoot must be 32 bytes (length was %d)", len(inputBatchRoot))
+ }
+ copy(outputBatchRoot[:], inputBatchRoot[:])
+
+ inputReferenceBlockNumber := inputHeader.GetReferenceBlockNumber()
+ if inputReferenceBlockNumber > math.MaxUint32 {
+ return nil, fmt.Errorf(
+ "ReferenceBlockNumber overflow: value was %d, but max allowable value is %d",
+ inputReferenceBlockNumber,
+ math.MaxUint32)
+ }
+
+ convertedHeader := &BatchHeaderV2{
+ BatchRoot: outputBatchRoot,
+ ReferenceBlockNumber: uint32(inputReferenceBlockNumber),
+ }
+
+ return convertedHeader, nil
+}
+
+func convertAttestation(inputAttestation *disperserv2.Attestation) (*Attestation, error) {
+ nonSignerPubkeys, err := repeatedBytesToG1Points(inputAttestation.GetNonSignerPubkeys())
+ if err != nil {
+ return nil, fmt.Errorf("convert non signer pubkeys to g1 points: %s", err)
+ }
+
+ quorumApks, err := repeatedBytesToG1Points(inputAttestation.GetQuorumApks())
+ if err != nil {
+ return nil, fmt.Errorf("convert quorum apks to g1 points: %s", err)
+ }
+
+ sigma, err := bytesToBN254G1Point(inputAttestation.GetSigma())
+ if err != nil {
+ return nil, fmt.Errorf("convert sigma to g1 point: %s", err)
+ }
+
+ apkG2, err := bytesToBN254G2Point(inputAttestation.GetApkG2())
+ if err != nil {
+ return nil, fmt.Errorf("convert apk g2 to g2 point: %s", err)
+ }
+
+ convertedAttestation := &Attestation{
+ NonSignerPubkeys: nonSignerPubkeys,
+ QuorumApks: quorumApks,
+ Sigma: *sigma,
+ ApkG2: *apkG2,
+ QuorumNumbers: inputAttestation.GetQuorumNumbers(),
+ }
+
+ return convertedAttestation, nil
+}
+
+func ConvertVerificationProof(inputVerificationInfo *disperserv2.BlobVerificationInfo) (*BlobVerificationProofV2, error) {
+ convertedBlobCertificate, err := convertBlobCertificate(inputVerificationInfo.GetBlobCertificate())
+
+ if err != nil {
+ return nil, fmt.Errorf("convert blob certificate: %s", err)
+ }
+
+ return &BlobVerificationProofV2{
+ BlobCertificate: *convertedBlobCertificate,
+ BlobIndex: inputVerificationInfo.GetBlobIndex(),
+ InclusionProof: inputVerificationInfo.GetInclusionProof(),
+ }, nil
+}
+
+func convertBlobCertificate(inputCertificate *commonv2.BlobCertificate) (*BlobCertificate, error) {
+ convertedBlobHeader, err := convertBlobHeader(inputCertificate.GetBlobHeader())
+ if err != nil {
+ return nil, fmt.Errorf("convert blob header: %s", err)
+ }
+
+ return &BlobCertificate{
+ BlobHeader: *convertedBlobHeader,
+ RelayKeys: inputCertificate.GetRelays(),
+ }, nil
+}
+
+func convertBlobHeader(inputHeader *commonv2.BlobHeader) (*BlobHeaderV2, error) {
+ inputVersion := inputHeader.GetVersion()
+ if inputVersion > math.MaxUint16 {
+ return nil, fmt.Errorf(
+ "version overflow: value was %d, but max allowable value is %d",
+ inputVersion,
+ math.MaxUint16)
+ }
+
+ var quorumNumbers []byte
+ for _, quorumNumber := range inputHeader.GetQuorumNumbers() {
+ if quorumNumber > math.MaxUint8 {
+ return nil, fmt.Errorf(
+ "quorum number overflow: value was %d, but max allowable value is %d",
+ quorumNumber,
+ uint8(math.MaxUint8))
+ }
+
+ quorumNumbers = append(quorumNumbers, byte(quorumNumber))
+ }
+
+ convertedBlobCommitment, err := convertBlobCommitment(inputHeader.GetCommitment())
+ if err != nil {
+ return nil, fmt.Errorf("convert blob commitment: %s", err)
+ }
+
+ paymentHeaderHash, err := core.ConvertToPaymentMetadata(inputHeader.GetPaymentHeader()).Hash()
+ if err != nil {
+ return nil, fmt.Errorf("hash payment header: %s", err)
+ }
+
+ return &BlobHeaderV2{
+ Version: uint16(inputVersion),
+ QuorumNumbers: quorumNumbers,
+ Commitment: *convertedBlobCommitment,
+ PaymentHeaderHash: paymentHeaderHash,
+ }, nil
+}
+
+func convertBlobCommitment(inputCommitment *common.BlobCommitment) (*BlobCommitment, error) {
+ convertedCommitment, err := bytesToBN254G1Point(inputCommitment.GetCommitment())
+ if err != nil {
+ return nil, fmt.Errorf("convert commitment to g1 point: %s", err)
+ }
+
+ convertedLengthCommitment, err := bytesToBN254G2Point(inputCommitment.GetLengthCommitment())
+ if err != nil {
+ return nil, fmt.Errorf("convert length commitment to g2 point: %s", err)
+ }
+
+ convertedLengthProof, err := bytesToBN254G2Point(inputCommitment.GetLengthProof())
+ if err != nil {
+ return nil, fmt.Errorf("convert length proof to g2 point: %s", err)
+ }
+
+ return &BlobCommitment{
+ Commitment: *convertedCommitment,
+ LengthCommitment: *convertedLengthCommitment,
+ LengthProof: *convertedLengthProof,
+ DataLength: inputCommitment.GetLength(),
+ }, nil
+}
+
+func bytesToBN254G1Point(bytes []byte) (*BN254G1Point, error) {
+ var g1Point bn254.G1Affine
+ _, err := g1Point.SetBytes(bytes)
+
+ if err != nil {
+ return nil, fmt.Errorf("deserialize g1 point: %s", err)
+ }
+
+ return &BN254G1Point{
+ X: g1Point.X.BigInt(new(big.Int)),
+ Y: g1Point.Y.BigInt(new(big.Int)),
+ }, nil
+}
+
+func bytesToBN254G2Point(bytes []byte) (*BN254G2Point, error) {
+ var g2Point bn254.G2Affine
+
+ // SetBytes checks that the result is in the correct subgroup
+ _, err := g2Point.SetBytes(bytes)
+
+ if err != nil {
+ return nil, fmt.Errorf("deserialize g2 point: %s", err)
+ }
+
+ var x, y [2]*big.Int
+ // Order is intentionally reversed when constructing BN254G2Point
+ // (see https://github.com/Layr-Labs/eigenlayer-middleware/blob/512ce7326f35e8060b9d46e23f9c159c0000b546/src/libraries/BN254.sol#L43)
+ x[0] = g2Point.X.A1.BigInt(new(big.Int))
+ x[1] = g2Point.X.A0.BigInt(new(big.Int))
+
+ y[0] = g2Point.Y.A1.BigInt(new(big.Int))
+ y[1] = g2Point.Y.A0.BigInt(new(big.Int))
+
+ return &BN254G2Point{
+ X: x,
+ Y: y,
+ }, nil
+}
+
+func repeatedBytesToG1Points(repeatedBytes [][]byte) ([]BN254G1Point, error) {
+ var outputPoints []BN254G1Point
+ for _, bytes := range repeatedBytes {
+ g1Point, err := bytesToBN254G1Point(bytes)
+ if err != nil {
+ return nil, fmt.Errorf("deserialize g1 point: %s", err)
+ }
+
+ outputPoints = append(outputPoints, *g1Point)
+ }
+
+ return outputPoints, nil
+}
diff --git a/contracts/script/EigenDADeployer.s.sol b/contracts/script/EigenDADeployer.s.sol
index 9e10f64124..8694c0aa2b 100644
--- a/contracts/script/EigenDADeployer.s.sol
+++ b/contracts/script/EigenDADeployer.s.sol
@@ -299,7 +299,7 @@ contract EigenDADeployer is DeployOpenEigenLayer {
eigenDAThresholdRegistryImplementation = new EigenDAThresholdRegistry();
VersionedBlobParams[] memory versionedBlobParams = new VersionedBlobParams[](0);
- SecurityThresholds memory defaultSecurityThresholds = SecurityThresholds(33, 55);
+ SecurityThresholds memory defaultSecurityThresholds = SecurityThresholds(55, 33);
eigenDAProxyAdmin.upgradeAndCall(
TransparentUpgradeableProxy(payable(address(eigenDAThresholdRegistry))),
diff --git a/contracts/script/SetUpEigenDA.s.sol b/contracts/script/SetUpEigenDA.s.sol
index ca6feb4c40..b7d5d2291d 100644
--- a/contracts/script/SetUpEigenDA.s.sol
+++ b/contracts/script/SetUpEigenDA.s.sol
@@ -211,6 +211,7 @@ contract SetupEigenDA is EigenDADeployer, EigenLayerUtils {
vm.serializeAddress(output, "operatorStateRetriever", address(operatorStateRetriever));
vm.serializeAddress(output, "blsApkRegistry" , address(apkRegistry));
vm.serializeAddress(output, "registryCoordinator", address(registryCoordinator));
+ vm.serializeAddress(output, "blobVerifier", address(eigenDABlobVerifier));
string memory finalJson = vm.serializeString(output, "object", output);
diff --git a/contracts/test/rollup/MockRollup.t.sol b/contracts/test/rollup/MockRollup.t.sol
index 8f9a24d7f9..e28310aee7 100644
--- a/contracts/test/rollup/MockRollup.t.sol
+++ b/contracts/test/rollup/MockRollup.t.sol
@@ -43,7 +43,7 @@ contract MockRollupTest is BLSMockAVSDeployer {
bytes quorumAdversaryThresholdPercentages = hex"212121";
bytes quorumConfirmationThresholdPercentages = hex"373737";
bytes quorumNumbersRequired = hex"0001";
- SecurityThresholds defaultSecurityThresholds = SecurityThresholds(33, 55);
+ SecurityThresholds defaultSecurityThresholds = SecurityThresholds(55, 33);
uint8 defaultCodingRatioPercentage = 10;
uint32 defaultReferenceBlockNumber = 100;
diff --git a/contracts/test/unit/EigenDABlobUtils.t.sol b/contracts/test/unit/EigenDABlobUtils.t.sol
index 25f5c032b3..4f0f82be21 100644
--- a/contracts/test/unit/EigenDABlobUtils.t.sol
+++ b/contracts/test/unit/EigenDABlobUtils.t.sol
@@ -47,7 +47,7 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer {
bytes quorumAdversaryThresholdPercentages = hex"212121";
bytes quorumConfirmationThresholdPercentages = hex"373737";
bytes quorumNumbersRequired = hex"0001";
- SecurityThresholds defaultSecurityThresholds = SecurityThresholds(33, 55);
+ SecurityThresholds defaultSecurityThresholds = SecurityThresholds(55, 33);
uint32 defaultReferenceBlockNumber = 100;
uint32 defaultConfirmationBlockNumber = 1000;
diff --git a/contracts/test/unit/EigenDAServiceManagerUnit.t.sol b/contracts/test/unit/EigenDAServiceManagerUnit.t.sol
index 8c14a98c0c..3f6f60f477 100644
--- a/contracts/test/unit/EigenDAServiceManagerUnit.t.sol
+++ b/contracts/test/unit/EigenDAServiceManagerUnit.t.sol
@@ -40,7 +40,7 @@ contract EigenDAServiceManagerUnit is BLSMockAVSDeployer {
bytes quorumAdversaryThresholdPercentages = hex"212121";
bytes quorumConfirmationThresholdPercentages = hex"373737";
bytes quorumNumbersRequired = hex"0001";
- SecurityThresholds defaultSecurityThresholds = SecurityThresholds(33, 55);
+ SecurityThresholds defaultSecurityThresholds = SecurityThresholds(55, 33);
uint256 feePerBytePerTime = 0;
diff --git a/core/chainio.go b/core/chainio.go
index e28572832a..c95e11380a 100644
--- a/core/chainio.go
+++ b/core/chainio.go
@@ -132,6 +132,9 @@ type Reader interface {
// GetRelayURLs returns the relay URL addresses for all relays.
GetRelayURLs(ctx context.Context) (map[uint32]string, error)
+
+ // GetDisperserAddress returns the disperser address with the given ID.
+ GetDisperserAddress(ctx context.Context, disperserID uint32) (gethcommon.Address, error)
}
type Writer interface {
diff --git a/core/eth/reader.go b/core/eth/reader.go
index a0c4364c1a..c523124128 100644
--- a/core/eth/reader.go
+++ b/core/eth/reader.go
@@ -3,6 +3,7 @@ package eth
import (
"context"
"crypto/ecdsa"
+ "fmt"
"math/big"
"strings"
@@ -10,6 +11,7 @@ import (
avsdir "github.com/Layr-Labs/eigenda/contracts/bindings/AVSDirectory"
blsapkreg "github.com/Layr-Labs/eigenda/contracts/bindings/BLSApkRegistry"
delegationmgr "github.com/Layr-Labs/eigenda/contracts/bindings/DelegationManager"
+ disperserreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDADisperserRegistry"
relayreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDARelayRegistry"
eigendasrvmg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAServiceManager"
thresholdreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAThresholdRegistry"
@@ -45,6 +47,7 @@ type ContractBindings struct {
PaymentVault *paymentvault.ContractPaymentVault
RelayRegistry *relayreg.ContractEigenDARelayRegistry
ThresholdRegistry *thresholdreg.ContractEigenDAThresholdRegistry
+ DisperserRegistry *disperserreg.ContractEigenDADisperserRegistry
}
type Reader struct {
@@ -224,6 +227,21 @@ func (t *Reader) updateContractBindings(blsOperatorStateRetrieverAddr, eigenDASe
}
}
+ var contractEigenDADisperserRegistry *disperserreg.ContractEigenDADisperserRegistry
+ disperserRegistryAddr, err := contractEigenDAServiceManager.EigenDADisperserRegistry(&bind.CallOpts{})
+ if err != nil {
+ t.logger.Error("Failed to fetch EigenDADisperserRegistry address", "err", err)
+ // TODO(cody-littley): return err when the contract is deployed
+ // return err
+ } else {
+ contractEigenDADisperserRegistry, err =
+ disperserreg.NewContractEigenDADisperserRegistry(disperserRegistryAddr, t.ethClient)
+ if err != nil {
+ t.logger.Error("Failed to fetch EigenDADisperserRegistry contract", "err", err)
+ return err
+ }
+ }
+
t.bindings = &ContractBindings{
ServiceManagerAddr: eigenDAServiceManagerAddr,
RegCoordinatorAddr: registryCoordinatorAddr,
@@ -240,6 +258,7 @@ func (t *Reader) updateContractBindings(blsOperatorStateRetrieverAddr, eigenDASe
RelayRegistry: contractRelayRegistry,
PaymentVault: contractPaymentVault,
ThresholdRegistry: contractThresholdRegistry,
+ DisperserRegistry: contractEigenDADisperserRegistry,
}
return nil
}
@@ -415,8 +434,8 @@ func (t *Reader) GetOperatorStakesForQuorums(ctx context.Context, quorums []core
Context: ctx,
}, t.bindings.RegCoordinatorAddr, quorumBytes, blockNumber)
if err != nil {
- t.logger.Error("Failed to fetch operator state", "err", err)
- return nil, err
+ t.logger.Errorf("Failed to fetch operator state: %s", err)
+ return nil, fmt.Errorf("failed to fetch operator state: %w", err)
}
state := make(core.OperatorStakes, len(state_))
@@ -896,10 +915,10 @@ func (t *Reader) GetRelayURLs(ctx context.Context) (map[uint32]string, error) {
}
res := make(map[uint32]string)
- for relayKey := uint32(0); relayKey < uint32(numRelays); relayKey++ {
+ for relayKey := uint32(0); relayKey < numRelays; relayKey++ {
url, err := t.bindings.RelayRegistry.RelayKeyToUrl(&bind.CallOpts{
Context: ctx,
- }, uint32(relayKey))
+ }, relayKey)
if err != nil && strings.Contains(err.Error(), "execution reverted") {
break
@@ -916,3 +935,26 @@ func (t *Reader) GetRelayURLs(ctx context.Context) (map[uint32]string, error) {
return res, nil
}
+
+func (t *Reader) GetDisperserAddress(ctx context.Context, disperserID uint32) (gethcommon.Address, error) {
+ registry := t.bindings.DisperserRegistry
+ if registry == nil {
+ return gethcommon.Address{}, errors.New("disperser registry not deployed")
+ }
+
+ address, err := registry.DisperserKeyToAddress(
+ &bind.CallOpts{
+ Context: ctx,
+ },
+ disperserID)
+
+ var defaultAddress gethcommon.Address
+ if err != nil {
+ return defaultAddress, fmt.Errorf("failed to get disperser address: %w", err)
+ }
+ if address == defaultAddress {
+ return defaultAddress, fmt.Errorf("disperser with id %d not found", disperserID)
+ }
+
+ return address, nil
+}
diff --git a/core/eth/writer.go b/core/eth/writer.go
index d088b6871f..5e8770f5d8 100644
--- a/core/eth/writer.go
+++ b/core/eth/writer.go
@@ -6,6 +6,9 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
+ "github.com/Layr-Labs/eigenda/api"
+ dreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDADisperserRegistry"
+ "log"
"math/big"
"github.com/Layr-Labs/eigenda/api/grpc/churner"
@@ -329,3 +332,39 @@ func (t *Writer) ConfirmBatch(ctx context.Context, batchHeader *core.BatchHeader
}
return receipt, nil
}
+
+// SetDisperserAddress sets the address of the disperser. Since there is currently only one disperser, this function
+// can only be used to set the address of that disperser.
+func (t *Writer) SetDisperserAddress(ctx context.Context, address gethcommon.Address) error {
+ registry := t.bindings.DisperserRegistry
+ if registry == nil {
+ log.Printf("disperser registry not deployed")
+ return errors.New("disperser registry not deployed")
+ }
+
+ log.Printf("Setting disperser %d address to %s", api.EigenLabsDisperserID, address.String())
+
+ options, err := t.ethClient.GetNoSendTransactOpts()
+ if err != nil {
+ t.logger.Error("Failed to generate transact opts", "err", err)
+ return err
+ }
+ options.Context = ctx
+
+ transaction, err := registry.SetDisperserInfo(
+ options,
+ api.EigenLabsDisperserID,
+ dreg.DisperserInfo{
+ DisperserAddress: address,
+ })
+ if err != nil {
+ return fmt.Errorf("failed to create transaction for setting disperser address: %w", err)
+ }
+
+ err = t.ethClient.SendTransaction(ctx, transaction)
+ if err != nil {
+ return fmt.Errorf("failed to set disperser address: %w", err)
+ }
+
+ return nil
+}
diff --git a/core/meterer/meterer.go b/core/meterer/meterer.go
index 55c2712119..c4673bb991 100644
--- a/core/meterer/meterer.go
+++ b/core/meterer/meterer.go
@@ -54,7 +54,7 @@ func NewMeterer(
// Start starts to periodically refreshing the on-chain state
func (m *Meterer) Start(ctx context.Context) {
go func() {
- ticker := time.NewTicker(m.UpdateInterval)
+ ticker := time.NewTicker(m.Config.UpdateInterval)
defer ticker.Stop()
for {
@@ -63,6 +63,7 @@ func (m *Meterer) Start(ctx context.Context) {
if err := m.ChainPaymentState.RefreshOnchainPaymentState(ctx); err != nil {
m.logger.Error("Failed to refresh on-chain state", "error", err)
}
+ m.logger.Debug("Refreshed on-chain state")
case <-ctx.Done():
return
}
diff --git a/core/meterer/offchain_store.go b/core/meterer/offchain_store.go
index f651c0206e..14fd0bbc0e 100644
--- a/core/meterer/offchain_store.go
+++ b/core/meterer/offchain_store.go
@@ -249,7 +249,7 @@ 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) {
+func (s *OffchainStore) GetPeriodRecords(ctx context.Context, accountID string, reservationPeriod uint32) ([MinNumBins]*pb.PeriodRecord, error) {
// Fetch the 3 bins start from the current bin
queryInput := &dynamodb.QueryInput{
TableName: aws.String(s.reservationTableName),
@@ -263,16 +263,16 @@ func (s *OffchainStore) GetBinRecords(ctx context.Context, accountID string, res
}
bins, err := s.dynamoClient.QueryWithInput(ctx, queryInput)
if err != nil {
- return [MinNumBins]*pb.BinRecord{}, fmt.Errorf("failed to query payments for account: %w", err)
+ return [MinNumBins]*pb.PeriodRecord{}, fmt.Errorf("failed to query payments for account: %w", err)
}
- records := [MinNumBins]*pb.BinRecord{}
+ records := [MinNumBins]*pb.PeriodRecord{}
for i := 0; i < len(bins) && i < int(MinNumBins); i++ {
- binRecord, err := parseBinRecord(bins[i])
+ periodRecord, err := parsePeriodRecord(bins[i])
if err != nil {
- return [MinNumBins]*pb.BinRecord{}, fmt.Errorf("failed to parse bin %d record: %w", i, err)
+ return [MinNumBins]*pb.PeriodRecord{}, fmt.Errorf("failed to parse bin %d record: %w", i, err)
}
- records[i] = binRecord
+ records[i] = periodRecord
}
return records, nil
@@ -318,7 +318,7 @@ func (s *OffchainStore) GetLargestCumulativePayment(ctx context.Context, account
return payment, nil
}
-func parseBinRecord(bin map[string]types.AttributeValue) (*pb.BinRecord, error) {
+func parsePeriodRecord(bin map[string]types.AttributeValue) (*pb.PeriodRecord, error) {
reservationPeriod, ok := bin["ReservationPeriod"]
if !ok {
return nil, errors.New("ReservationPeriod is not present in the response")
@@ -349,7 +349,7 @@ func parseBinRecord(bin map[string]types.AttributeValue) (*pb.BinRecord, error)
return nil, fmt.Errorf("failed to parse BinUsage: %w", err)
}
- return &pb.BinRecord{
+ return &pb.PeriodRecord{
Index: uint32(reservationPeriodValue),
Usage: uint64(binUsageValue),
}, nil
diff --git a/core/mock/writer.go b/core/mock/writer.go
index b28f88b5f1..f5bb433b91 100644
--- a/core/mock/writer.go
+++ b/core/mock/writer.go
@@ -284,3 +284,14 @@ func (t *MockWriter) GetRelayURLs(ctx context.Context) (map[uint32]string, error
return result.(map[uint32]string), args.Error(1)
}
+
+func (t *MockWriter) GetDisperserAddress(ctx context.Context, disperserID uint32) (gethcommon.Address, error) {
+ args := t.Called(disperserID)
+ result := args.Get(0)
+ if result == nil {
+ var zeroValue gethcommon.Address
+ return zeroValue, args.Error(1)
+ }
+
+ return result.(gethcommon.Address), args.Error(1)
+}
diff --git a/disperser/api/grpc/encoder/v2/encoder.pb.go b/disperser/api/grpc/encoder/v2/encoder.pb.go
index 182c97bfc9..eadd1fb51c 100644
--- a/disperser/api/grpc/encoder/v2/encoder.pb.go
+++ b/disperser/api/grpc/encoder/v2/encoder.pb.go
@@ -29,6 +29,7 @@ type EncodeBlobRequest struct {
BlobKey []byte `protobuf:"bytes,1,opt,name=blob_key,json=blobKey,proto3" json:"blob_key,omitempty"`
EncodingParams *EncodingParams `protobuf:"bytes,2,opt,name=encoding_params,json=encodingParams,proto3" json:"encoding_params,omitempty"`
+ BlobSize uint64 `protobuf:"varint,3,opt,name=blob_size,json=blobSize,proto3" json:"blob_size,omitempty"`
}
func (x *EncodeBlobRequest) Reset() {
@@ -77,6 +78,13 @@ func (x *EncodeBlobRequest) GetEncodingParams() *EncodingParams {
return nil
}
+func (x *EncodeBlobRequest) GetBlobSize() uint64 {
+ if x != nil {
+ return x.BlobSize
+ }
+ return 0
+}
+
// EncodingParams specifies how the blob should be encoded into chunks
type EncodingParams struct {
state protoimpl.MessageState
@@ -242,41 +250,43 @@ var File_encoder_v2_encoder_proto protoreflect.FileDescriptor
var file_encoder_v2_encoder_proto_rawDesc = []byte{
0x0a, 0x18, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x65, 0x6e, 0x63,
0x6f, 0x64, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x65, 0x6e, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x22, 0x73, 0x0a, 0x11, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65,
- 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62,
- 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62,
- 0x6c, 0x6f, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x43, 0x0a, 0x0f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69,
- 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x1a, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x6e, 0x63,
- 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0e, 0x65, 0x6e, 0x63,
- 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x52, 0x0a, 0x0e, 0x45,
- 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x21, 0x0a,
- 0x0c, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x04, 0x52, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68,
- 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x22,
- 0x73, 0x0a, 0x0c, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12,
- 0x33, 0x0a, 0x16, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x73,
- 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
- 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x42,
- 0x79, 0x74, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74,
- 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x0d, 0x52, 0x11, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x42,
- 0x79, 0x74, 0x65, 0x73, 0x22, 0x50, 0x0a, 0x0f, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x6c,
- 0x6f, 0x62, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3d, 0x0a, 0x0d, 0x66, 0x72, 0x61, 0x67, 0x6d,
- 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18,
- 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x46, 0x72, 0x61, 0x67,
- 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65,
- 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x32, 0x55, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65,
- 0x72, 0x12, 0x4a, 0x0a, 0x0a, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12,
- 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x6e, 0x63,
- 0x6f, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b,
- 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x6e, 0x63, 0x6f,
- 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x32, 0x5a,
- 0x30, 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, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76,
- 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x64, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x22, 0x90, 0x01, 0x0a, 0x11, 0x45, 0x6e, 0x63, 0x6f, 0x64,
+ 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08,
+ 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07,
+ 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x43, 0x0a, 0x0f, 0x65, 0x6e, 0x63, 0x6f, 0x64,
+ 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x1a, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x6e,
+ 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0e, 0x65, 0x6e,
+ 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09,
+ 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52,
+ 0x08, 0x62, 0x6c, 0x6f, 0x62, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x52, 0x0a, 0x0e, 0x45, 0x6e, 0x63,
+ 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63,
+ 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x04, 0x52, 0x0b, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1d,
+ 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x04, 0x52, 0x09, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x22, 0x73, 0x0a,
+ 0x0c, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x33, 0x0a,
+ 0x16, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x73, 0x69, 0x7a,
+ 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x74,
+ 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74,
+ 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73,
+ 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
+ 0x11, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74,
+ 0x65, 0x73, 0x22, 0x50, 0x0a, 0x0f, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x62,
+ 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x3d, 0x0a, 0x0d, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e,
+ 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x65,
+ 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65,
+ 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74,
+ 0x49, 0x6e, 0x66, 0x6f, 0x32, 0x55, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12,
+ 0x4a, 0x0a, 0x0a, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x1d, 0x2e,
+ 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64,
+ 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x65,
+ 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65,
+ 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x32, 0x5a, 0x30, 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, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x62,
+ 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/disperser/api/proto/encoder/v2/encoder.proto b/disperser/api/proto/encoder/v2/encoder.proto
index 679e716be4..20a8fcfe1c 100644
--- a/disperser/api/proto/encoder/v2/encoder.proto
+++ b/disperser/api/proto/encoder/v2/encoder.proto
@@ -15,6 +15,7 @@ service Encoder {
message EncodeBlobRequest {
bytes blob_key = 1;
EncodingParams encoding_params = 2;
+ uint64 blob_size = 3;
}
// EncodingParams specifies how the blob should be encoded into chunks
diff --git a/disperser/apiserver/metrics_v2.go b/disperser/apiserver/metrics_v2.go
index 9273aa9d52..3405f6cd4d 100644
--- a/disperser/apiserver/metrics_v2.go
+++ b/disperser/apiserver/metrics_v2.go
@@ -1,12 +1,20 @@
package apiserver
import (
+ "context"
+ "fmt"
+ "net/http"
+ "time"
+
"github.com/Layr-Labs/eigenda/common"
+ "github.com/Layr-Labs/eigenda/disperser"
+ "github.com/Layr-Labs/eigensdk-go/logging"
grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
"github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promauto"
+ "github.com/prometheus/client_golang/prometheus/promhttp"
"google.golang.org/grpc"
- "time"
)
const namespace = "eigenda_disperser_api"
@@ -22,12 +30,18 @@ type metricsV2 struct {
validateDispersalRequestLatency *prometheus.SummaryVec
storeBlobLatency *prometheus.SummaryVec
getBlobStatusLatency *prometheus.SummaryVec
+
+ registry *prometheus.Registry
+ httpPort string
+ logger logging.Logger
}
// newAPIServerV2Metrics creates a new metricsV2 instance.
-func newAPIServerV2Metrics(registry *prometheus.Registry) *metricsV2 {
+func newAPIServerV2Metrics(registry *prometheus.Registry, metricsConfig disperser.MetricsConfig, logger logging.Logger) *metricsV2 {
grpcMetrics := grpcprom.NewServerMetrics()
registry.MustRegister(grpcMetrics)
+ registry.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
+ registry.MustRegister(collectors.NewGoCollector())
grpcServerOption := grpc.UnaryInterceptor(
grpcMetrics.UnaryServerInterceptor(),
@@ -113,9 +127,28 @@ func newAPIServerV2Metrics(registry *prometheus.Registry) *metricsV2 {
validateDispersalRequestLatency: validateDispersalRequestLatency,
storeBlobLatency: storeBlobLatency,
getBlobStatusLatency: getBlobStatusLatency,
+ registry: registry,
+ httpPort: metricsConfig.HTTPPort,
+ logger: logger.With("component", "DisperserV2Metrics"),
}
}
+// Start the metrics server
+func (m *metricsV2) Start(ctx context.Context) {
+ m.logger.Info("Starting metrics server at ", "port", m.httpPort)
+ addr := fmt.Sprintf(":%s", m.httpPort)
+ go func() {
+ log := m.logger
+ mux := http.NewServeMux()
+ mux.Handle("/metrics", promhttp.HandlerFor(
+ m.registry,
+ promhttp.HandlerOpts{},
+ ))
+ err := http.ListenAndServe(addr, mux)
+ log.Error("Prometheus server failed", "err", err)
+ }()
+}
+
func (m *metricsV2) reportGetBlobCommitmentLatency(duration time.Duration) {
m.getBlobCommitmentLatency.WithLabelValues().Observe(common.ToMilliseconds(duration))
}
diff --git a/disperser/apiserver/server_v2.go b/disperser/apiserver/server_v2.go
index efe5566500..2a50ebc6db 100644
--- a/disperser/apiserver/server_v2.go
+++ b/disperser/apiserver/server_v2.go
@@ -58,7 +58,8 @@ type DispersalServerV2 struct {
maxNumSymbolsPerBlob uint64
onchainStateRefreshInterval time.Duration
- metrics *metricsV2
+ metricsConfig disperser.MetricsConfig
+ metrics *metricsV2
}
// NewDispersalServerV2 creates a new Server struct with the provided parameters.
@@ -74,6 +75,7 @@ func NewDispersalServerV2(
onchainStateRefreshInterval time.Duration,
_logger logging.Logger,
registry *prometheus.Registry,
+ metricsConfig disperser.MetricsConfig,
) (*DispersalServerV2, error) {
if serverConfig.GrpcPort == "" {
return nil, errors.New("grpc port is required")
@@ -116,11 +118,17 @@ func NewDispersalServerV2(
maxNumSymbolsPerBlob: maxNumSymbolsPerBlob,
onchainStateRefreshInterval: onchainStateRefreshInterval,
- metrics: newAPIServerV2Metrics(registry),
+ metricsConfig: metricsConfig,
+ metrics: newAPIServerV2Metrics(registry, metricsConfig, logger),
}, nil
}
func (s *DispersalServerV2) Start(ctx context.Context) error {
+ // Start the metrics server
+ if s.metricsConfig.EnableMetrics {
+ s.metrics.Start(context.Background())
+ }
+
// Serve grpc requests
addr := fmt.Sprintf("%s:%s", disperser.Localhost, s.serverConfig.GrpcPort)
listener, err := net.Listen("tcp", addr)
@@ -281,7 +289,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)
+ periodRecords, err := s.meterer.OffchainStore.GetPeriodRecords(ctx, req.AccountId, currentReservationPeriod)
if err != nil {
s.logger.Debug("failed to get reservation records, use placeholders", "err", err, "accountID", accountID)
}
@@ -335,7 +343,7 @@ func (s *DispersalServerV2) GetPaymentState(ctx context.Context, req *pb.GetPaym
// build reply
reply := &pb.GetPaymentStateReply{
PaymentGlobalParams: &paymentGlobalParams,
- BinRecords: binRecords[:],
+ PeriodRecords: periodRecords[:],
Reservation: pbReservation,
CumulativePayment: largestCumulativePaymentBytes,
OnchainCumulativePayment: onchainCumulativePaymentBytes,
diff --git a/disperser/apiserver/server_v2_test.go b/disperser/apiserver/server_v2_test.go
index 305b6bd437..44a6924545 100644
--- a/disperser/apiserver/server_v2_test.go
+++ b/disperser/apiserver/server_v2_test.go
@@ -517,7 +517,12 @@ func newTestServerV2(t *testing.T) *testComponents {
10,
time.Hour,
logger,
- prometheus.NewRegistry())
+ prometheus.NewRegistry(),
+ disperser.MetricsConfig{
+ HTTPPort: "9094",
+ EnableMetrics: false,
+ },
+ )
assert.NoError(t, err)
err = s.RefreshOnchainState(context.Background())
diff --git a/disperser/cmd/apiserver/config.go b/disperser/cmd/apiserver/config.go
index a27bd0b793..fedb597dec 100644
--- a/disperser/cmd/apiserver/config.go
+++ b/disperser/cmd/apiserver/config.go
@@ -35,8 +35,7 @@ type Config struct {
EncodingConfig kzg.KzgConfig
EnableRatelimiter bool
EnablePaymentMeterer bool
- UpdateInterval int
- ChainReadTimeout int
+ ChainReadTimeout time.Duration
ReservationsTableName string
OnDemandTableName string
GlobalRateTableName string
@@ -119,8 +118,7 @@ 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),
- ChainReadTimeout: ctx.GlobalInt(flags.ChainReadTimeout.Name),
+ ChainReadTimeout: ctx.GlobalDuration(flags.ChainReadTimeout.Name),
EthClientConfig: geth.ReadEthClientConfigRPCOnly(ctx),
MaxBlobSize: ctx.GlobalInt(flags.MaxBlobSize.Name),
MaxNumSymbolsPerBlob: ctx.GlobalUint(flags.MaxNumSymbolsPerBlob.Name),
diff --git a/disperser/cmd/apiserver/flags/flags.go b/disperser/cmd/apiserver/flags/flags.go
index af31d87ff9..99b3e828ca 100644
--- a/disperser/cmd/apiserver/flags/flags.go
+++ b/disperser/cmd/apiserver/flags/flags.go
@@ -107,14 +107,7 @@ var (
Value: "global_rate",
EnvVar: common.PrefixEnvVar(envVarPrefix, "GLOBAL_RATE_TABLE_NAME"),
}
- UpdateInterval = cli.DurationFlag{
- Name: common.PrefixFlag(FlagPrefix, "update-interval"),
- Usage: "update interval for refreshing the on-chain state",
- Value: 1 * time.Second,
- EnvVar: common.PrefixEnvVar(envVarPrefix, "UPDATE_INTERVAL"),
- Required: false,
- }
- ChainReadTimeout = cli.UintFlag{
+ ChainReadTimeout = cli.DurationFlag{
Name: common.PrefixFlag(FlagPrefix, "chain-read-timeout"),
Usage: "timeout for reading from the chain",
Value: 10,
@@ -146,7 +139,7 @@ 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: 1 * time.Minute,
}
MaxNumSymbolsPerBlob = cli.UintFlag{
Name: common.PrefixFlag(FlagPrefix, "max-num-symbols-per-blob"),
diff --git a/disperser/cmd/apiserver/main.go b/disperser/cmd/apiserver/main.go
index 2659f0c87b..1f5de7e125 100644
--- a/disperser/cmd/apiserver/main.go
+++ b/disperser/cmd/apiserver/main.go
@@ -99,8 +99,8 @@ 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: config.ChainReadTimeout,
+ UpdateInterval: config.OnchainStateRefreshInterval,
}
paymentChainState, err := mt.NewOnchainPaymentState(context.Background(), transactor)
@@ -129,6 +129,7 @@ func RunDisperserServer(ctx *cli.Context) error {
logger,
// metrics.NewNoopMetrics(),
)
+ meterer.Start(context.Background())
}
var ratelimiter common.RateLimiter
@@ -182,6 +183,7 @@ func RunDisperserServer(ctx *cli.Context) error {
config.OnchainStateRefreshInterval,
logger,
reg,
+ config.MetricsConfig,
)
if err != nil {
return err
@@ -212,7 +214,6 @@ func RunDisperserServer(ctx *cli.Context) error {
// Enable Metrics Block
if config.MetricsConfig.EnableMetrics {
httpSocket := fmt.Sprintf(":%s", config.MetricsConfig.HTTPPort)
- // TODO(cody-littley): once we deprecate v1, move all remaining metrics functionality to metrics_v2.go
metrics.Start(context.Background())
logger.Info("Enabled metrics for Disperser", "socket", httpSocket)
}
diff --git a/disperser/cmd/controller/config.go b/disperser/cmd/controller/config.go
index 62000b765b..5a81b5f51f 100644
--- a/disperser/cmd/controller/config.go
+++ b/disperser/cmd/controller/config.go
@@ -25,12 +25,14 @@ type Config struct {
DynamoDBTableName string
- EthClientConfig geth.EthClientConfig
- AwsClientConfig aws.ClientConfig
- LoggerConfig common.LoggerConfig
- IndexerConfig indexer.Config
- ChainStateConfig thegraph.Config
- UseGraph bool
+ EthClientConfig geth.EthClientConfig
+ AwsClientConfig aws.ClientConfig
+ DisperserStoreChunksSigningDisabled bool
+ DisperserKMSKeyID string
+ LoggerConfig common.LoggerConfig
+ IndexerConfig indexer.Config
+ ChainStateConfig thegraph.Config
+ UseGraph bool
BLSOperatorStateRetrieverAddr string
EigenDAServiceManagerAddr string
@@ -60,10 +62,12 @@ func NewConfig(ctx *cli.Context) (Config, error) {
relays[i] = corev2.RelayKey(relay)
}
config := Config{
- DynamoDBTableName: ctx.GlobalString(flags.DynamoDBTableNameFlag.Name),
- EthClientConfig: ethClientConfig,
- AwsClientConfig: aws.ReadClientConfig(ctx, flags.FlagPrefix),
- LoggerConfig: *loggerConfig,
+ DynamoDBTableName: ctx.GlobalString(flags.DynamoDBTableNameFlag.Name),
+ EthClientConfig: ethClientConfig,
+ AwsClientConfig: aws.ReadClientConfig(ctx, flags.FlagPrefix),
+ DisperserStoreChunksSigningDisabled: ctx.GlobalBool(flags.DisperserStoreChunksSigningDisabledFlag.Name),
+ DisperserKMSKeyID: ctx.GlobalString(flags.DisperserKMSKeyIDFlag.Name),
+ LoggerConfig: *loggerConfig,
EncodingManagerConfig: controller.EncodingManagerConfig{
PullInterval: ctx.GlobalDuration(flags.EncodingPullIntervalFlag.Name),
EncodingRequestTimeout: ctx.GlobalDuration(flags.EncodingRequestTimeoutFlag.Name),
@@ -93,5 +97,8 @@ func NewConfig(ctx *cli.Context) (Config, error) {
EigenDAServiceManagerAddr: ctx.GlobalString(flags.EigenDAServiceManagerFlag.Name),
MetricsPort: ctx.GlobalInt(flags.MetricsPortFlag.Name),
}
+ if !config.DisperserStoreChunksSigningDisabled && config.DisperserKMSKeyID == "" {
+ return Config{}, fmt.Errorf("DisperserKMSKeyID is required when StoreChunks() signing is enabled")
+ }
return config, nil
}
diff --git a/disperser/cmd/controller/flags/flags.go b/disperser/cmd/controller/flags/flags.go
index 7b3055234b..96c7edc8b3 100644
--- a/disperser/cmd/controller/flags/flags.go
+++ b/disperser/cmd/controller/flags/flags.go
@@ -178,6 +178,18 @@ var (
EnvVar: common.PrefixEnvVar(envVarPrefix, "METRICS_PORT"),
Value: 9101,
}
+ DisperserStoreChunksSigningDisabledFlag = cli.BoolFlag{
+ Name: common.PrefixFlag(FlagPrefix, "disperser-store-chunks-signing-disabled"),
+ Usage: "Whether to disable signing of store chunks requests",
+ Required: false,
+ EnvVar: common.PrefixEnvVar(envVarPrefix, "DISPERSER_STORE_CHUNKS_SIGNING_DISABLED"),
+ }
+ DisperserKMSKeyIDFlag = cli.StringFlag{
+ Name: common.PrefixFlag(FlagPrefix, "disperser-kms-key-id"),
+ Usage: "Name of the key used to sign disperser requests (key must be stored in AWS KMS under this name)",
+ Required: false,
+ EnvVar: common.PrefixEnvVar(envVarPrefix, "DISPERSER_KMS_KEY_ID"),
+ }
)
var requiredFlags = []cli.Flag{
@@ -210,6 +222,8 @@ var optionalFlags = []cli.Flag{
NodeClientCacheNumEntriesFlag,
MaxBatchSizeFlag,
MetricsPortFlag,
+ DisperserStoreChunksSigningDisabledFlag,
+ DisperserKMSKeyIDFlag,
}
var Flags []cli.Flag
diff --git a/disperser/cmd/controller/main.go b/disperser/cmd/controller/main.go
index 2725c3fd45..c6d4b5f7da 100644
--- a/disperser/cmd/controller/main.go
+++ b/disperser/cmd/controller/main.go
@@ -3,6 +3,7 @@ package main
import (
"context"
"fmt"
+ "github.com/Layr-Labs/eigenda/api/clients/v2"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -148,7 +149,22 @@ func RunController(ctx *cli.Context) error {
return err
}
}
- nodeClientManager, err := controller.NewNodeClientManager(config.NodeClientCacheSize, logger)
+
+ var requestSigner clients.DispersalRequestSigner
+ if config.DisperserStoreChunksSigningDisabled {
+ logger.Warn("StoreChunks() signing is disabled")
+ } else {
+ requestSigner, err = clients.NewDispersalRequestSigner(
+ context.Background(),
+ config.AwsClientConfig.Region,
+ config.AwsClientConfig.EndpointURL,
+ config.DisperserKMSKeyID)
+ if err != nil {
+ return fmt.Errorf("failed to create request signer: %v", err)
+ }
+ }
+
+ nodeClientManager, err := controller.NewNodeClientManager(config.NodeClientCacheSize, requestSigner, logger)
if err != nil {
return fmt.Errorf("failed to create node client manager: %v", err)
}
diff --git a/disperser/cmd/encoder/config.go b/disperser/cmd/encoder/config.go
index d69c2e6857..038712ad31 100644
--- a/disperser/cmd/encoder/config.go
+++ b/disperser/cmd/encoder/config.go
@@ -56,6 +56,7 @@ func NewConfig(ctx *cli.Context) (Config, error) {
GrpcPort: ctx.GlobalString(flags.GrpcPortFlag.Name),
MaxConcurrentRequests: ctx.GlobalInt(flags.MaxConcurrentRequestsFlag.Name),
RequestPoolSize: ctx.GlobalInt(flags.RequestPoolSizeFlag.Name),
+ RequestQueueSize: ctx.GlobalInt(flags.RequestQueueSizeFlag.Name),
EnableGnarkChunkEncoding: ctx.Bool(flags.EnableGnarkChunkEncodingFlag.Name),
PreventReencoding: ctx.Bool(flags.PreventReencodingFlag.Name),
Backend: ctx.String(flags.BackendFlag.Name),
diff --git a/disperser/cmd/encoder/flags/flags.go b/disperser/cmd/encoder/flags/flags.go
index dedb228d6b..a291b064fc 100644
--- a/disperser/cmd/encoder/flags/flags.go
+++ b/disperser/cmd/encoder/flags/flags.go
@@ -62,6 +62,13 @@ var (
Value: 32,
EnvVar: common.PrefixEnvVar(envVarPrefix, "REQUEST_POOL_SIZE"),
}
+ RequestQueueSizeFlag = cli.IntFlag{
+ Name: common.PrefixFlag(FlagPrefix, "request-queue-size"),
+ Usage: "maximum number of requests in the request queue",
+ Required: false,
+ Value: 32,
+ EnvVar: common.PrefixEnvVar(envVarPrefix, "REQUEST_QUEUE_SIZE"),
+ }
EnableGnarkChunkEncodingFlag = cli.BoolFlag{
Name: common.PrefixFlag(FlagPrefix, "enable-gnark-chunk-encoding"),
Usage: "if true, will produce chunks in Gnark, instead of Gob",
@@ -111,6 +118,7 @@ var optionalFlags = []cli.Flag{
EnableMetrics,
MaxConcurrentRequestsFlag,
RequestPoolSizeFlag,
+ RequestQueueSizeFlag,
EnableGnarkChunkEncodingFlag,
EncoderVersionFlag,
S3BucketNameFlag,
diff --git a/disperser/cmd/encoder/main.go b/disperser/cmd/encoder/main.go
index 3382deedfa..6d9645e35d 100644
--- a/disperser/cmd/encoder/main.go
+++ b/disperser/cmd/encoder/main.go
@@ -115,6 +115,7 @@ func RunEncoderServer(ctx *cli.Context) error {
logger,
prover,
metrics,
+ grpcMetrics,
)
return server.Start()
diff --git a/disperser/common/v2/blobstore/dynamo_metadata_store.go b/disperser/common/v2/blobstore/dynamo_metadata_store.go
index 0fff046cae..b33c2adb08 100644
--- a/disperser/common/v2/blobstore/dynamo_metadata_store.go
+++ b/disperser/common/v2/blobstore/dynamo_metadata_store.go
@@ -480,6 +480,35 @@ func (s *BlobMetadataStore) GetDispersalResponse(ctx context.Context, batchHeade
return res, nil
}
+func (s *BlobMetadataStore) GetDispersalResponses(ctx context.Context, batchHeaderHash [32]byte) ([]*corev2.DispersalResponse, error) {
+ items, err := s.dynamoDBClient.Query(ctx, s.tableName, "PK = :pk AND begins_with(SK, :prefix)", commondynamodb.ExpressionValues{
+ ":pk": &types.AttributeValueMemberS{
+ Value: dispersalKeyPrefix + hex.EncodeToString(batchHeaderHash[:]),
+ },
+ ":prefix": &types.AttributeValueMemberS{
+ Value: dispersalResponseSKPrefix,
+ },
+ })
+
+ if err != nil {
+ return nil, err
+ }
+
+ if len(items) == 0 {
+ return nil, fmt.Errorf("%w: dispersal responses not found for batch header hash %x", common.ErrMetadataNotFound, batchHeaderHash)
+ }
+
+ responses := make([]*corev2.DispersalResponse, len(items))
+ for i, item := range items {
+ responses[i], err = UnmarshalDispersalResponse(item)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return responses, nil
+}
+
func (s *BlobMetadataStore) PutBatchHeader(ctx context.Context, batchHeader *corev2.BatchHeader) error {
item, err := MarshalBatchHeader(batchHeader)
if err != nil {
diff --git a/disperser/common/v2/blobstore/dynamo_metadata_store_test.go b/disperser/common/v2/blobstore/dynamo_metadata_store_test.go
index 7feb001ea3..7fe957b064 100644
--- a/disperser/common/v2/blobstore/dynamo_metadata_store_test.go
+++ b/disperser/common/v2/blobstore/dynamo_metadata_store_test.go
@@ -336,15 +336,52 @@ func TestBlobMetadataStoreDispersals(t *testing.T) {
err = blobMetadataStore.PutDispersalResponse(ctx, dispersalResponse)
assert.ErrorIs(t, err, common.ErrAlreadyExists)
+ // the other operator's response for the same batch
+ opID2 := core.OperatorID{2, 3}
+ dispersalRequest2 := &corev2.DispersalRequest{
+ OperatorID: opID2,
+ OperatorAddress: gethcommon.HexToAddress("0x2234567"),
+ Socket: "socket",
+ DispersedAt: uint64(time.Now().UnixNano()),
+ BatchHeader: corev2.BatchHeader{
+ BatchRoot: [32]byte{1, 2, 3},
+ ReferenceBlockNumber: 100,
+ },
+ }
+ err = blobMetadataStore.PutDispersalRequest(ctx, dispersalRequest2)
+ assert.NoError(t, err)
+ dispersalResponse2 := &corev2.DispersalResponse{
+ DispersalRequest: dispersalRequest2,
+ RespondedAt: uint64(time.Now().UnixNano()),
+ Signature: [32]byte{1, 1, 1},
+ Error: "",
+ }
+ err = blobMetadataStore.PutDispersalResponse(ctx, dispersalResponse2)
+ assert.NoError(t, err)
+
+ responses, err := blobMetadataStore.GetDispersalResponses(ctx, bhh)
+ assert.NoError(t, err)
+ assert.Equal(t, 2, len(responses))
+ assert.Equal(t, dispersalResponse, responses[0])
+ assert.Equal(t, dispersalResponse2, responses[1])
+
deleteItems(t, []commondynamodb.Key{
{
"PK": &types.AttributeValueMemberS{Value: "BatchHeader#" + hex.EncodeToString(bhh[:])},
"SK": &types.AttributeValueMemberS{Value: "DispersalRequest#" + opID.Hex()},
},
+ {
+ "PK": &types.AttributeValueMemberS{Value: "BatchHeader#" + hex.EncodeToString(bhh[:])},
+ "SK": &types.AttributeValueMemberS{Value: "DispersalRequest#" + opID2.Hex()},
+ },
{
"PK": &types.AttributeValueMemberS{Value: "BatchHeader#" + hex.EncodeToString(bhh[:])},
"SK": &types.AttributeValueMemberS{Value: "DispersalResponse#" + opID.Hex()},
},
+ {
+ "PK": &types.AttributeValueMemberS{Value: "BatchHeader#" + hex.EncodeToString(bhh[:])},
+ "SK": &types.AttributeValueMemberS{Value: "DispersalResponse#" + opID2.Hex()},
+ },
})
}
diff --git a/disperser/controller/dispatcher.go b/disperser/controller/dispatcher.go
index a6fe5e2ac7..31cdffe086 100644
--- a/disperser/controller/dispatcher.go
+++ b/disperser/controller/dispatcher.go
@@ -50,6 +50,7 @@ type batchData struct {
Batch *corev2.Batch
BatchHeaderHash [32]byte
BlobKeys []corev2.BlobKey
+ Metadata map[corev2.BlobKey]*v2.BlobMetadata
OperatorState *core.IndexedOperatorState
}
@@ -351,6 +352,7 @@ func (d *Dispatcher) NewBatch(ctx context.Context, referenceBlockNumber uint64)
}
keys := make([]corev2.BlobKey, len(blobMetadatas))
+ metadataMap := make(map[corev2.BlobKey]*v2.BlobMetadata, len(blobMetadatas))
for i, metadata := range blobMetadatas {
if metadata == nil || metadata.BlobHeader == nil {
return nil, fmt.Errorf("invalid blob metadata")
@@ -360,6 +362,7 @@ func (d *Dispatcher) NewBatch(ctx context.Context, referenceBlockNumber uint64)
return nil, fmt.Errorf("failed to get blob key: %w", err)
}
keys[i] = blobKey
+ metadataMap[blobKey] = metadata
}
certs, _, err := d.blobMetadataStore.GetBlobCertificates(ctx, keys)
@@ -471,6 +474,7 @@ func (d *Dispatcher) NewBatch(ctx context.Context, referenceBlockNumber uint64)
},
BatchHeaderHash: batchHeaderHash,
BlobKeys: keys,
+ Metadata: metadataMap,
OperatorState: state,
}, nil
}
@@ -517,6 +521,9 @@ func (d *Dispatcher) updateBatchStatus(ctx context.Context, batch *batchData, qu
if err != nil {
multierr = multierror.Append(multierr, fmt.Errorf("failed to update blob status for blob %s to failed: %w", blobKey.Hex(), err))
}
+ if metadata, ok := batch.Metadata[blobKey]; ok {
+ d.metrics.reportCompletedBlob(int(metadata.BlobSize), v2.Failed)
+ }
continue
}
@@ -534,6 +541,9 @@ func (d *Dispatcher) updateBatchStatus(ctx context.Context, batch *batchData, qu
if err != nil {
multierr = multierror.Append(multierr, fmt.Errorf("failed to update blob status for blob %s to failed: %w", blobKey.Hex(), err))
}
+ if metadata, ok := batch.Metadata[blobKey]; ok {
+ d.metrics.reportCompletedBlob(int(metadata.BlobSize), v2.InsufficientSignatures)
+ }
continue
}
@@ -541,6 +551,11 @@ func (d *Dispatcher) updateBatchStatus(ctx context.Context, batch *batchData, qu
if err != nil {
multierr = multierror.Append(multierr, fmt.Errorf("failed to update blob status for blob %s to certified: %w", blobKey.Hex(), err))
}
+ if metadata, ok := batch.Metadata[blobKey]; ok {
+ requestedAt := time.Unix(0, int64(metadata.RequestedAt))
+ d.metrics.reportE2EDispersalLatency(time.Since(requestedAt))
+ d.metrics.reportCompletedBlob(int(metadata.BlobSize), v2.Certified)
+ }
}
return multierr
@@ -553,6 +568,9 @@ func (d *Dispatcher) failBatch(ctx context.Context, batch *batchData) error {
if err != nil {
multierr = multierror.Append(multierr, fmt.Errorf("failed to update blob status for blob %s to failed: %w", blobKey.Hex(), err))
}
+ if metadata, ok := batch.Metadata[blobKey]; ok {
+ d.metrics.reportCompletedBlob(int(metadata.BlobSize), v2.Failed)
+ }
}
return multierr
diff --git a/disperser/controller/dispatcher_metrics.go b/disperser/controller/dispatcher_metrics.go
index 00dc79e5a3..8b94b8878b 100644
--- a/disperser/controller/dispatcher_metrics.go
+++ b/disperser/controller/dispatcher_metrics.go
@@ -3,7 +3,8 @@ package controller
import (
"time"
- "github.com/Layr-Labs/eigenda/common"
+ common "github.com/Layr-Labs/eigenda/common"
+ dispv2 "github.com/Layr-Labs/eigenda/disperser/common/v2"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
@@ -26,17 +27,17 @@ type dispatcherMetrics struct {
sendChunksLatency *prometheus.SummaryVec
sendChunksRetryCount *prometheus.GaugeVec
putDispersalResponseLatency *prometheus.SummaryVec
-
- handleSignaturesLatency *prometheus.SummaryVec
- receiveSignaturesLatency *prometheus.SummaryVec
- aggregateSignaturesLatency *prometheus.SummaryVec
- putAttestationLatency *prometheus.SummaryVec
- updateBatchStatusLatency *prometheus.SummaryVec
+ handleSignaturesLatency *prometheus.SummaryVec
+ receiveSignaturesLatency *prometheus.SummaryVec
+ aggregateSignaturesLatency *prometheus.SummaryVec
+ putAttestationLatency *prometheus.SummaryVec
+ updateBatchStatusLatency *prometheus.SummaryVec
+ blobE2EDispersalLatency *prometheus.SummaryVec
+ completedBlobs *prometheus.CounterVec
}
// NewDispatcherMetrics sets up metrics for the dispatcher.
func newDispatcherMetrics(registry *prometheus.Registry) *dispatcherMetrics {
-
objectives := map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}
handleBatchLatency := promauto.With(registry).NewSummaryVec(
@@ -228,6 +229,25 @@ func newDispatcherMetrics(registry *prometheus.Registry) *dispatcherMetrics {
[]string{},
)
+ blobE2EDispersalLatency := promauto.With(registry).NewSummaryVec(
+ prometheus.SummaryOpts{
+ Namespace: dispatcherNamespace,
+ Name: "e2e_dispersal_latency_ms",
+ Help: "The time required to disperse a blob end-to-end.",
+ Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
+ },
+ []string{},
+ )
+
+ completedBlobs := promauto.With(registry).NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: dispatcherNamespace,
+ Name: "completed_blobs_total",
+ Help: "The number and size of completed blobs by status.",
+ },
+ []string{"state", "data"},
+ )
+
return &dispatcherMetrics{
handleBatchLatency: handleBatchLatency,
newBatchLatency: newBatchLatency,
@@ -248,6 +268,8 @@ func newDispatcherMetrics(registry *prometheus.Registry) *dispatcherMetrics {
aggregateSignaturesLatency: aggregateSignaturesLatency,
putAttestationLatency: putAttestationLatency,
updateBatchStatusLatency: updateBatchStatusLatency,
+ blobE2EDispersalLatency: blobE2EDispersalLatency,
+ completedBlobs: completedBlobs,
}
}
@@ -326,3 +348,26 @@ func (m *dispatcherMetrics) reportPutAttestationLatency(duration time.Duration)
func (m *dispatcherMetrics) reportUpdateBatchStatusLatency(duration time.Duration) {
m.updateBatchStatusLatency.WithLabelValues().Observe(common.ToMilliseconds(duration))
}
+
+func (m *dispatcherMetrics) reportE2EDispersalLatency(duration time.Duration) {
+ m.blobE2EDispersalLatency.WithLabelValues().Observe(common.ToMilliseconds(duration))
+}
+
+func (m *dispatcherMetrics) reportCompletedBlob(size int, status dispv2.BlobStatus) {
+ switch status {
+ case dispv2.Certified:
+ m.completedBlobs.WithLabelValues("certified", "number").Inc()
+ m.completedBlobs.WithLabelValues("certified", "size").Add(float64(size))
+ case dispv2.Failed:
+ m.completedBlobs.WithLabelValues("failed", "number").Inc()
+ m.completedBlobs.WithLabelValues("failed", "size").Add(float64(size))
+ case dispv2.InsufficientSignatures:
+ m.completedBlobs.WithLabelValues("insufficient_signature", "number").Inc()
+ m.completedBlobs.WithLabelValues("insufficient_signature", "size").Add(float64(size))
+ default:
+ return
+ }
+
+ m.completedBlobs.WithLabelValues("total", "number").Inc()
+ m.completedBlobs.WithLabelValues("total", "size").Add(float64(size))
+}
diff --git a/disperser/controller/encoding_manager.go b/disperser/controller/encoding_manager.go
index 2dd6fa85ff..85fc6d7f15 100644
--- a/disperser/controller/encoding_manager.go
+++ b/disperser/controller/encoding_manager.go
@@ -20,6 +20,7 @@ import (
"github.com/Layr-Labs/eigenda/disperser/common/v2/blobstore"
"github.com/Layr-Labs/eigenda/encoding"
"github.com/Layr-Labs/eigensdk-go/logging"
+ "google.golang.org/grpc/metadata"
)
var errNoBlobsToEncode = errors.New("no blobs to encode")
@@ -245,6 +246,10 @@ func (e *EncodingManager) HandleBatch(ctx context.Context) error {
e.metrics.reportUpdateBlobStatusLatency(
finishedUpdateBlobStatusTime.Sub(finishedPutBlobCertificateTime))
e.metrics.reportBlobHandleLatency(time.Since(start))
+
+ requestedAt := time.Unix(0, int64(blob.RequestedAt))
+ e.metrics.reportE2EEncodingLatency(time.Since(requestedAt))
+ e.metrics.reportCompletedBlob(int(blob.BlobSize), v2.Encoded)
} else {
e.metrics.reportFailedSubmission()
storeCtx, cancel := context.WithTimeout(ctx, e.StoreTimeout)
@@ -254,6 +259,7 @@ func (e *EncodingManager) HandleBatch(ctx context.Context) error {
e.logger.Error("failed to update blob status to Failed", "blobKey", blobKey.Hex(), "err", err)
return
}
+ e.metrics.reportCompletedBlob(int(blob.BlobSize), v2.Failed)
}
})
}
@@ -269,11 +275,18 @@ func (e *EncodingManager) HandleBatch(ctx context.Context) error {
}
func (e *EncodingManager) encodeBlob(ctx context.Context, blobKey corev2.BlobKey, blob *v2.BlobMetadata, blobParams *core.BlobVersionParameters) (*encoding.FragmentInfo, error) {
+ // Add headers for routing
+ md := metadata.New(map[string]string{
+ "content-type": "application/grpc",
+ "x-blob-size": fmt.Sprintf("%d", blob.BlobSize),
+ })
+ ctx = metadata.NewOutgoingContext(ctx, md)
+
encodingParams, err := blob.BlobHeader.GetEncodingParams(blobParams)
if err != nil {
return nil, fmt.Errorf("failed to get encoding params: %w", err)
}
- return e.encodingClient.EncodeBlob(ctx, blobKey, encodingParams)
+ return e.encodingClient.EncodeBlob(ctx, blobKey, encodingParams, blob.BlobSize)
}
func (e *EncodingManager) refreshBlobVersionParams(ctx context.Context) error {
@@ -291,7 +304,8 @@ func GetRelayKeys(numAssignment uint16, availableRelays []corev2.RelayKey) ([]co
if int(numAssignment) > len(availableRelays) {
return nil, fmt.Errorf("numAssignment (%d) cannot be greater than numRelays (%d)", numAssignment, len(availableRelays))
}
- relayKeys := availableRelays
+ relayKeys := make([]corev2.RelayKey, len(availableRelays))
+ copy(relayKeys, availableRelays)
// shuffle relay keys
for i := len(relayKeys) - 1; i > 0; i-- {
j := rand.Intn(i + 1)
diff --git a/disperser/controller/encoding_manager_metrics.go b/disperser/controller/encoding_manager_metrics.go
index 7911932039..ad13724577 100644
--- a/disperser/controller/encoding_manager_metrics.go
+++ b/disperser/controller/encoding_manager_metrics.go
@@ -1,10 +1,12 @@
package controller
import (
+ "time"
+
common "github.com/Layr-Labs/eigenda/common"
+ dispv2 "github.com/Layr-Labs/eigenda/disperser/common/v2"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
- "time"
)
const encodingManagerNamespace = "eigenda_encoding_manager"
@@ -16,10 +18,12 @@ type encodingManagerMetrics struct {
encodingLatency *prometheus.SummaryVec
putBlobCertLatency *prometheus.SummaryVec
updateBlobStatusLatency *prometheus.SummaryVec
+ blobE2EEncodingLatency *prometheus.SummaryVec
batchSize *prometheus.GaugeVec
batchDataSize *prometheus.GaugeVec
batchRetryCount *prometheus.GaugeVec
failedSubmissionCount *prometheus.CounterVec
+ completedBlobs *prometheus.CounterVec
}
// NewEncodingManagerMetrics sets up metrics for the encoding manager.
@@ -74,6 +78,16 @@ func newEncodingManagerMetrics(registry *prometheus.Registry) *encodingManagerMe
[]string{},
)
+ blobE2EEncodingLatency := promauto.With(registry).NewSummaryVec(
+ prometheus.SummaryOpts{
+ Namespace: encodingManagerNamespace,
+ Name: "e2e_encoding_latency_ms",
+ Help: "The time required to encode a blob end-to-end.",
+ Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
+ },
+ []string{},
+ )
+
batchSize := promauto.With(registry).NewGaugeVec(
prometheus.GaugeOpts{
Namespace: encodingManagerNamespace,
@@ -110,16 +124,27 @@ func newEncodingManagerMetrics(registry *prometheus.Registry) *encodingManagerMe
[]string{},
)
+ completedBlobs := promauto.With(registry).NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: encodingManagerNamespace,
+ Name: "completed_blobs_total",
+ Help: "The number and size of completed blobs by status.",
+ },
+ []string{"state", "data"},
+ )
+
return &encodingManagerMetrics{
batchSubmissionLatency: batchSubmissionLatency,
blobHandleLatency: blobHandleLatency,
encodingLatency: encodingLatency,
putBlobCertLatency: putBlobCertLatency,
updateBlobStatusLatency: updateBlobStatusLatency,
+ blobE2EEncodingLatency: blobE2EEncodingLatency,
batchSize: batchSize,
batchDataSize: batchDataSize,
batchRetryCount: batchRetryCount,
failedSubmissionCount: failSubmissionCount,
+ completedBlobs: completedBlobs,
}
}
@@ -143,6 +168,10 @@ func (m *encodingManagerMetrics) reportUpdateBlobStatusLatency(duration time.Dur
m.updateBlobStatusLatency.WithLabelValues().Observe(common.ToMilliseconds(duration))
}
+func (m *encodingManagerMetrics) reportE2EEncodingLatency(duration time.Duration) {
+ m.blobE2EEncodingLatency.WithLabelValues().Observe(common.ToMilliseconds(duration))
+}
+
func (m *encodingManagerMetrics) reportBatchSize(size int) {
m.batchSize.WithLabelValues().Set(float64(size))
}
@@ -158,3 +187,19 @@ func (m *encodingManagerMetrics) reportBatchRetryCount(count int) {
func (m *encodingManagerMetrics) reportFailedSubmission() {
m.failedSubmissionCount.WithLabelValues().Inc()
}
+
+func (m *encodingManagerMetrics) reportCompletedBlob(size int, status dispv2.BlobStatus) {
+ switch status {
+ case dispv2.Encoded:
+ m.completedBlobs.WithLabelValues("encoded", "number").Inc()
+ m.completedBlobs.WithLabelValues("encoded", "size").Add(float64(size))
+ case dispv2.Failed:
+ m.completedBlobs.WithLabelValues("failed", "number").Inc()
+ m.completedBlobs.WithLabelValues("failed", "size").Add(float64(size))
+ default:
+ return
+ }
+
+ m.completedBlobs.WithLabelValues("total", "number").Inc()
+ m.completedBlobs.WithLabelValues("total", "size").Add(float64(size))
+}
diff --git a/disperser/controller/encoding_manager_test.go b/disperser/controller/encoding_manager_test.go
index 129da59644..e6fde1107a 100644
--- a/disperser/controller/encoding_manager_test.go
+++ b/disperser/controller/encoding_manager_test.go
@@ -56,6 +56,12 @@ func TestGetRelayKeys(t *testing.T) {
availableRelays: []corev2.RelayKey{0},
err: nil,
},
+ {
+ name: "All relays",
+ numRelays: 2,
+ availableRelays: []corev2.RelayKey{0, 1},
+ err: nil,
+ },
{
name: "Choose 1 from multiple relays",
numRelays: 3,
@@ -78,6 +84,10 @@ func TestGetRelayKeys(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
+
+ availableRelaysCopy := make([]corev2.RelayKey, len(tt.availableRelays))
+ copy(availableRelaysCopy, tt.availableRelays)
+
got, err := controller.GetRelayKeys(tt.numRelays, tt.availableRelays)
if err != nil {
require.Error(t, err)
@@ -90,6 +100,8 @@ func TestGetRelayKeys(t *testing.T) {
seen[relay] = struct{}{}
}
require.Equal(t, len(seen), len(got))
+ // GetRelayKeys should not modify the original list of available relays.
+ require.Equal(t, availableRelaysCopy, tt.availableRelays)
}
})
}
diff --git a/disperser/controller/node_client_manager.go b/disperser/controller/node_client_manager.go
index eb746eaf9e..a1669ba00b 100644
--- a/disperser/controller/node_client_manager.go
+++ b/disperser/controller/node_client_manager.go
@@ -14,14 +14,20 @@ type NodeClientManager interface {
type nodeClientManager struct {
// nodeClients is a cache of node clients keyed by socket address
- nodeClients *lru.Cache[string, clients.NodeClient]
- logger logging.Logger
+ nodeClients *lru.Cache[string, clients.NodeClient]
+ requestSigner clients.DispersalRequestSigner
+ logger logging.Logger
}
var _ NodeClientManager = (*nodeClientManager)(nil)
-func NewNodeClientManager(cacheSize int, logger logging.Logger) (*nodeClientManager, error) {
+func NewNodeClientManager(
+ cacheSize int,
+ requestSigner clients.DispersalRequestSigner,
+ logger logging.Logger) (NodeClientManager, error) {
+
closeClient := func(socket string, value clients.NodeClient) {
+
if err := value.Close(); err != nil {
logger.Error("failed to close node client", "err", err)
}
@@ -32,8 +38,9 @@ func NewNodeClientManager(cacheSize int, logger logging.Logger) (*nodeClientMana
}
return &nodeClientManager{
- nodeClients: nodeClients,
- logger: logger,
+ nodeClients: nodeClients,
+ requestSigner: requestSigner,
+ logger: logger,
}, nil
}
@@ -42,10 +49,12 @@ func (m *nodeClientManager) GetClient(host, port string) (clients.NodeClient, er
client, ok := m.nodeClients.Get(socket)
if !ok {
var err error
- client, err = clients.NewNodeClient(&clients.NodeClientConfig{
- Hostname: host,
- Port: port,
- })
+ client, err = clients.NewNodeClient(
+ &clients.NodeClientConfig{
+ Hostname: host,
+ Port: port,
+ },
+ m.requestSigner)
if err != nil {
return nil, fmt.Errorf("failed to create node client at %s: %w", socket, err)
}
diff --git a/disperser/controller/node_client_manager_test.go b/disperser/controller/node_client_manager_test.go
index ffe0dc5a6a..070ff1490f 100644
--- a/disperser/controller/node_client_manager_test.go
+++ b/disperser/controller/node_client_manager_test.go
@@ -1,6 +1,8 @@
package controller_test
import (
+ "github.com/Layr-Labs/eigenda/api/clients/mock"
+ "github.com/Layr-Labs/eigenda/common/testutils/random"
"testing"
"github.com/Layr-Labs/eigenda/disperser/controller"
@@ -8,7 +10,12 @@ import (
)
func TestNodeClientManager(t *testing.T) {
- m, err := controller.NewNodeClientManager(2, nil)
+ rand := random.NewTestRandom(t)
+
+ _, private := rand.ECDSA()
+ requestSigner := mock.NewStaticRequestSigner(private)
+
+ m, err := controller.NewNodeClientManager(2, requestSigner, nil)
require.NoError(t, err)
client0, err := m.GetClient("localhost", "0000")
diff --git a/disperser/dataapi/docs/v1/V1_docs.go b/disperser/dataapi/docs/v1/V1_docs.go
index 637b56b867..82362ff9b9 100644
--- a/disperser/dataapi/docs/v1/V1_docs.go
+++ b/disperser/dataapi/docs/v1/V1_docs.go
@@ -1126,12 +1126,6 @@ const docTemplateV1 = `{
"items": {
"type": "integer"
}
- },
- "y": {
- "type": "array",
- "items": {
- "type": "integer"
- }
}
}
},
@@ -1140,9 +1134,6 @@ const docTemplateV1 = `{
"properties": {
"x": {
"$ref": "#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2"
- },
- "y": {
- "$ref": "#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2"
}
}
},
@@ -1151,9 +1142,6 @@ const docTemplateV1 = `{
"properties": {
"x": {
"$ref": "#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2"
- },
- "y": {
- "$ref": "#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2"
}
}
},
@@ -1184,12 +1172,6 @@ const docTemplateV1 = `{
"items": {
"type": "integer"
}
- },
- "a1": {
- "type": "array",
- "items": {
- "type": "integer"
- }
}
}
},
diff --git a/disperser/dataapi/docs/v1/V1_swagger.json b/disperser/dataapi/docs/v1/V1_swagger.json
index 052eeafeb8..1fd39fb5ba 100644
--- a/disperser/dataapi/docs/v1/V1_swagger.json
+++ b/disperser/dataapi/docs/v1/V1_swagger.json
@@ -1122,12 +1122,6 @@
"items": {
"type": "integer"
}
- },
- "y": {
- "type": "array",
- "items": {
- "type": "integer"
- }
}
}
},
@@ -1136,9 +1130,6 @@
"properties": {
"x": {
"$ref": "#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2"
- },
- "y": {
- "$ref": "#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2"
}
}
},
@@ -1147,9 +1138,6 @@
"properties": {
"x": {
"$ref": "#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2"
- },
- "y": {
- "$ref": "#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2"
}
}
},
@@ -1180,12 +1168,6 @@
"items": {
"type": "integer"
}
- },
- "a1": {
- "type": "array",
- "items": {
- "type": "integer"
- }
}
}
},
diff --git a/disperser/dataapi/docs/v1/V1_swagger.yaml b/disperser/dataapi/docs/v1/V1_swagger.yaml
index ea9a47bd3a..bc94a6c866 100644
--- a/disperser/dataapi/docs/v1/V1_swagger.yaml
+++ b/disperser/dataapi/docs/v1/V1_swagger.yaml
@@ -253,24 +253,16 @@ definitions:
items:
type: integer
type: array
- "y":
- items:
- type: integer
- type: array
type: object
encoding.G2Commitment:
properties:
x:
$ref: '#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2'
- "y":
- $ref: '#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2'
type: object
encoding.LengthProof:
properties:
x:
$ref: '#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2'
- "y":
- $ref: '#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2'
type: object
github_com_Layr-Labs_eigenda_disperser.BlobStatus:
enum:
@@ -294,10 +286,6 @@ definitions:
items:
type: integer
type: array
- a1:
- items:
- type: integer
- type: array
type: object
semver.SemverMetrics:
properties:
diff --git a/disperser/dataapi/docs/v2/V2_docs.go b/disperser/dataapi/docs/v2/V2_docs.go
index 9df5d7464f..e5b5dee45a 100644
--- a/disperser/dataapi/docs/v2/V2_docs.go
+++ b/disperser/dataapi/docs/v2/V2_docs.go
@@ -445,7 +445,7 @@ const docTemplateV2 = `{
},
{
"type": "string",
- "description": "Operator ID in hex string",
+ "description": "Operator ID in hex string [default: all operators if unspecified]",
"name": "operator_id",
"in": "query"
}
@@ -454,7 +454,7 @@ const docTemplateV2 = `{
"200": {
"description": "OK",
"schema": {
- "$ref": "#/definitions/v2.OperatorDispersalResponse"
+ "$ref": "#/definitions/v2.OperatorDispersalResponses"
}
},
"400": {
@@ -817,6 +817,9 @@ const docTemplateV2 = `{
"blob_header": {
"$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobHeader"
},
+ "blob_key": {
+ "type": "string"
+ },
"blob_size_bytes": {
"type": "integer"
},
@@ -917,11 +920,14 @@ const docTemplateV2 = `{
}
}
},
- "v2.OperatorDispersalResponse": {
+ "v2.OperatorDispersalResponses": {
"type": "object",
"properties": {
- "operator_dispersal_response": {
- "$ref": "#/definitions/v2.DispersalResponse"
+ "operator_dispersal_responses": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/v2.DispersalResponse"
+ }
}
}
},
diff --git a/disperser/dataapi/docs/v2/V2_swagger.json b/disperser/dataapi/docs/v2/V2_swagger.json
index 3323d85129..af154b8114 100644
--- a/disperser/dataapi/docs/v2/V2_swagger.json
+++ b/disperser/dataapi/docs/v2/V2_swagger.json
@@ -442,7 +442,7 @@
},
{
"type": "string",
- "description": "Operator ID in hex string",
+ "description": "Operator ID in hex string [default: all operators if unspecified]",
"name": "operator_id",
"in": "query"
}
@@ -451,7 +451,7 @@
"200": {
"description": "OK",
"schema": {
- "$ref": "#/definitions/v2.OperatorDispersalResponse"
+ "$ref": "#/definitions/v2.OperatorDispersalResponses"
}
},
"400": {
@@ -814,6 +814,9 @@
"blob_header": {
"$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobHeader"
},
+ "blob_key": {
+ "type": "string"
+ },
"blob_size_bytes": {
"type": "integer"
},
@@ -914,11 +917,14 @@
}
}
},
- "v2.OperatorDispersalResponse": {
+ "v2.OperatorDispersalResponses": {
"type": "object",
"properties": {
- "operator_dispersal_response": {
- "$ref": "#/definitions/v2.DispersalResponse"
+ "operator_dispersal_responses": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/v2.DispersalResponse"
+ }
}
}
},
diff --git a/disperser/dataapi/docs/v2/V2_swagger.yaml b/disperser/dataapi/docs/v2/V2_swagger.yaml
index 8d0b918247..6a2b7c1985 100644
--- a/disperser/dataapi/docs/v2/V2_swagger.yaml
+++ b/disperser/dataapi/docs/v2/V2_swagger.yaml
@@ -232,6 +232,8 @@ definitions:
properties:
blob_header:
$ref: '#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobHeader'
+ blob_key:
+ type: string
blob_size_bytes:
type: integer
dispersed_at:
@@ -300,10 +302,12 @@ definitions:
$ref: '#/definitions/big.Int'
type: object
type: object
- v2.OperatorDispersalResponse:
+ v2.OperatorDispersalResponses:
properties:
- operator_dispersal_response:
- $ref: '#/definitions/v2.DispersalResponse'
+ operator_dispersal_responses:
+ items:
+ $ref: '#/definitions/v2.DispersalResponse'
+ type: array
type: object
v2.OperatorPortCheckResponse:
properties:
@@ -559,7 +563,7 @@ paths:
name: batch_header_hash
required: true
type: string
- - description: Operator ID in hex string
+ - description: 'Operator ID in hex string [default: all operators if unspecified]'
in: query
name: operator_id
type: string
@@ -569,7 +573,7 @@ paths:
"200":
description: OK
schema:
- $ref: '#/definitions/v2.OperatorDispersalResponse'
+ $ref: '#/definitions/v2.OperatorDispersalResponses'
"400":
description: 'error: Bad request'
schema:
diff --git a/disperser/dataapi/v2/server_v2.go b/disperser/dataapi/v2/server_v2.go
index 1b1258656b..44008b7a61 100644
--- a/disperser/dataapi/v2/server_v2.go
+++ b/disperser/dataapi/v2/server_v2.go
@@ -51,6 +51,7 @@ type (
}
BlobResponse struct {
+ BlobKey string `json:"blob_key"`
BlobHeader *corev2.BlobHeader `json:"blob_header"`
Status string `json:"status"`
DispersedAt uint64 `json:"dispersed_at"`
@@ -86,8 +87,9 @@ type (
StakeRankedOperators map[string][]*OperatorStake `json:"stake_ranked_operators"`
}
- OperatorDispersalResponse struct {
- Response *corev2.DispersalResponse `json:"operator_dispersal_response"`
+ // Operators' responses for a batch
+ OperatorDispersalResponses struct {
+ Responses []*corev2.DispersalResponse `json:"operator_dispersal_responses"`
}
OperatorPortCheckResponse struct {
@@ -194,7 +196,7 @@ func (s *ServerV2) Start() error {
operators.GET("/stake", s.FetchOperatorsStake)
operators.GET("/nodeinfo", s.FetchOperatorsNodeInfo)
operators.GET("/reachability", s.CheckOperatorsReachability)
- operators.GET("/response/:batch_header_hash", s.FetchOperatorResponse)
+ operators.GET("/response/:batch_header_hash", s.FetchOperatorsResponses)
}
metrics := v2.Group("/metrics")
{
@@ -321,7 +323,14 @@ func (s *ServerV2) FetchBlobHandler(c *gin.Context) {
errorResponse(c, err)
return
}
+ bk, err := metadata.BlobHeader.BlobKey()
+ if err != nil || bk != blobKey {
+ s.metrics.IncrementFailedRequestNum("FetchBlob")
+ errorResponse(c, err)
+ return
+ }
response := &BlobResponse{
+ BlobKey: bk.Hex(),
BlobHeader: metadata.BlobHeader,
Status: metadata.BlobStatus.String(),
DispersedAt: metadata.RequestedAt,
@@ -510,49 +519,62 @@ func (s *ServerV2) FetchOperatorsNodeInfo(c *gin.Context) {
c.JSON(http.StatusOK, report)
}
-// FetchOperatorResponse godoc
+// FetchOperatorsResponses godoc
//
// @Summary Fetch operator attestation response for a batch
// @Tags Operators
// @Produce json
// @Param batch_header_hash path string true "Batch header hash in hex string"
-// @Param operator_id query string false "Operator ID in hex string"
-// @Success 200 {object} OperatorDispersalResponse
+// @Param operator_id query string false "Operator ID in hex string [default: all operators if unspecified]"
+// @Success 200 {object} OperatorDispersalResponses
// @Failure 400 {object} ErrorResponse "error: Bad request"
// @Failure 404 {object} ErrorResponse "error: Not found"
// @Failure 500 {object} ErrorResponse "error: Server error"
// @Router /operators/{batch_header_hash} [get]
-func (s *ServerV2) FetchOperatorResponse(c *gin.Context) {
+func (s *ServerV2) FetchOperatorsResponses(c *gin.Context) {
timer := prometheus.NewTimer(prometheus.ObserverFunc(func(f float64) {
- s.metrics.ObserveLatency("FetchOperatorResponse", f*1000) // make milliseconds
+ s.metrics.ObserveLatency("FetchOperatorsResponses", f*1000) // make milliseconds
}))
defer timer.ObserveDuration()
batchHeaderHashHex := c.Param("batch_header_hash")
batchHeaderHash, err := dataapi.ConvertHexadecimalToBytes([]byte(batchHeaderHashHex))
if err != nil {
- s.metrics.IncrementInvalidArgRequestNum("FetchOperatorResponse")
+ s.metrics.IncrementInvalidArgRequestNum("FetchOperatorsResponses")
errorResponse(c, errors.New("invalid batch header hash"))
return
}
operatorIdStr := c.DefaultQuery("operator_id", "")
- operatorId, err := core.OperatorIDFromHex(operatorIdStr)
- if err != nil {
- s.metrics.IncrementInvalidArgRequestNum("FetchOperatorResponse")
- errorResponse(c, errors.New("invalid operatorId"))
- return
- }
- operatorResponse, err := s.blobMetadataStore.GetDispersalResponse(c.Request.Context(), batchHeaderHash, operatorId)
- if err != nil {
- s.metrics.IncrementFailedRequestNum("FetchOperatorResponse")
- errorResponse(c, err)
- return
+ operatorResponses := make([]*corev2.DispersalResponse, 0)
+ if operatorIdStr == "" {
+ res, err := s.blobMetadataStore.GetDispersalResponses(c.Request.Context(), batchHeaderHash)
+ if err != nil {
+ s.metrics.IncrementFailedRequestNum("FetchOperatorsResponses")
+ errorResponse(c, err)
+ return
+ }
+ operatorResponses = append(operatorResponses, res...)
+ } else {
+ operatorId, err := core.OperatorIDFromHex(operatorIdStr)
+ if err != nil {
+ s.metrics.IncrementInvalidArgRequestNum("FetchOperatorsResponses")
+ errorResponse(c, errors.New("invalid operatorId"))
+ return
+ }
+
+ res, err := s.blobMetadataStore.GetDispersalResponse(c.Request.Context(), batchHeaderHash, operatorId)
+ if err != nil {
+ s.metrics.IncrementFailedRequestNum("FetchOperatorsResponses")
+ errorResponse(c, err)
+ return
+ }
+ operatorResponses = append(operatorResponses, res)
}
- response := &OperatorDispersalResponse{
- Response: operatorResponse,
+ response := &OperatorDispersalResponses{
+ Responses: operatorResponses,
}
- s.metrics.IncrementSuccessfulRequestNum("FetchOperatorResponse")
+ s.metrics.IncrementSuccessfulRequestNum("FetchOperatorsResponses")
c.Writer.Header().Set(cacheControlParam, fmt.Sprintf("max-age=%d", maxOperatorResponseAge))
c.JSON(http.StatusOK, response)
}
diff --git a/disperser/dataapi/v2/server_v2_test.go b/disperser/dataapi/v2/server_v2_test.go
index 13d923dd01..1580c6c579 100644
--- a/disperser/dataapi/v2/server_v2_test.go
+++ b/disperser/dataapi/v2/server_v2_test.go
@@ -484,7 +484,7 @@ func TestCheckOperatorsReachability(t *testing.T) {
mockSubgraphApi.Calls = nil
}
-func TestFetchOperatorResponse(t *testing.T) {
+func TestFetchOperatorResponses(t *testing.T) {
r := setUpRouter()
ctx := context.Background()
@@ -515,7 +515,26 @@ func TestFetchOperatorResponse(t *testing.T) {
err = blobMetadataStore.PutDispersalResponse(ctx, dispersalResponse)
assert.NoError(t, err)
- r.GET("/v2/operators/response/:batch_header_hash", testDataApiServerV2.FetchOperatorResponse)
+ // Set up the other dispersal response in metadata store
+ operatorId2 := core.OperatorID{2, 3}
+ dispersalRequest2 := &corev2.DispersalRequest{
+ OperatorID: operatorId2,
+ OperatorAddress: gethcommon.HexToAddress("0x1234567"),
+ Socket: "socket",
+ DispersedAt: uint64(time.Now().UnixNano()),
+ BatchHeader: *batchHeader,
+ }
+ dispersalResponse2 := &corev2.DispersalResponse{
+ DispersalRequest: dispersalRequest2,
+ RespondedAt: uint64(time.Now().UnixNano()),
+ Signature: [32]byte{1, 1, 1},
+ Error: "",
+ }
+ err = blobMetadataStore.PutDispersalResponse(ctx, dispersalResponse2)
+ assert.NoError(t, err)
+
+ // Fetch response of a specific operator
+ r.GET("/v2/operators/response/:batch_header_hash", testDataApiServerV2.FetchOperatorsResponses)
w := httptest.NewRecorder()
reqStr := fmt.Sprintf("/v2/operators/response/%s?operator_id=%v", batchHeaderHash, operatorId.Hex())
req := httptest.NewRequest(http.MethodGet, reqStr, nil)
@@ -526,11 +545,31 @@ func TestFetchOperatorResponse(t *testing.T) {
data, err := io.ReadAll(res.Body)
assert.NoError(t, err)
- var response serverv2.OperatorDispersalResponse
+ var response serverv2.OperatorDispersalResponses
err = json.Unmarshal(data, &response)
assert.NoError(t, err)
assert.NotNil(t, response)
- assert.Equal(t, response.Response, dispersalResponse)
+ assert.Equal(t, 1, len(response.Responses))
+ assert.Equal(t, response.Responses[0], dispersalResponse)
+
+ // Fetch all operators' responses for a batch
+ reqStr2 := fmt.Sprintf("/v2/operators/response/%s", batchHeaderHash)
+ w2 := httptest.NewRecorder()
+ req2 := httptest.NewRequest(http.MethodGet, reqStr2, nil)
+ r.ServeHTTP(w2, req2)
+ assert.Equal(t, w2.Code, http.StatusOK)
+ res2 := w2.Result()
+ defer res2.Body.Close()
+ data2, err := io.ReadAll(res2.Body)
+ assert.NoError(t, err)
+
+ var response2 serverv2.OperatorDispersalResponses
+ err = json.Unmarshal(data2, &response2)
+ assert.NoError(t, err)
+ assert.NotNil(t, response2)
+ assert.Equal(t, 2, len(response2.Responses))
+ assert.Equal(t, response2.Responses[0], dispersalResponse)
+ assert.Equal(t, response2.Responses[1], dispersalResponse2)
}
func TestFetchOperatorsStake(t *testing.T) {
diff --git a/disperser/encoder/client_v2.go b/disperser/encoder/client_v2.go
index 815bc9f85e..b8db40f916 100644
--- a/disperser/encoder/client_v2.go
+++ b/disperser/encoder/client_v2.go
@@ -22,7 +22,7 @@ func NewEncoderClientV2(addr string) (disperser.EncoderClientV2, error) {
}, nil
}
-func (c *clientV2) EncodeBlob(ctx context.Context, blobKey corev2.BlobKey, encodingParams encoding.EncodingParams) (*encoding.FragmentInfo, error) {
+func (c *clientV2) EncodeBlob(ctx context.Context, blobKey corev2.BlobKey, encodingParams encoding.EncodingParams, blobSize uint64) (*encoding.FragmentInfo, error) {
// Establish connection
conn, err := grpc.NewClient(
c.addr,
@@ -43,6 +43,7 @@ func (c *clientV2) EncodeBlob(ctx context.Context, blobKey corev2.BlobKey, encod
ChunkLength: encodingParams.ChunkLength,
NumChunks: encodingParams.NumChunks,
},
+ BlobSize: blobSize,
}
// Make the RPC call
diff --git a/disperser/encoder/config.go b/disperser/encoder/config.go
index 168f4185dc..2ff7f4f18f 100644
--- a/disperser/encoder/config.go
+++ b/disperser/encoder/config.go
@@ -8,6 +8,7 @@ type ServerConfig struct {
GrpcPort string
MaxConcurrentRequests int
RequestPoolSize int
+ RequestQueueSize int
EnableGnarkChunkEncoding bool
PreventReencoding bool
Backend string
diff --git a/disperser/encoder/server.go b/disperser/encoder/server.go
index 9cab207a30..64e6b726e1 100644
--- a/disperser/encoder/server.go
+++ b/disperser/encoder/server.go
@@ -111,7 +111,7 @@ func (s *EncoderServer) EncodeBlob(ctx context.Context, req *pb.EncodeBlobReques
s.metrics.ObserveQueue(s.queueStats)
s.queueLock.Unlock()
default:
- s.metrics.IncrementRateLimitedBlobRequestNum(len(req.GetData()))
+ s.metrics.IncrementRateLimitedBlobRequestNum(blobSize)
s.logger.Warn("rate limiting as request pool is full", "requestPoolSize", s.config.RequestPoolSize, "maxConcurrentRequests", s.config.MaxConcurrentRequests)
return nil, errors.New("too many requests")
}
diff --git a/disperser/encoder/server_v2.go b/disperser/encoder/server_v2.go
index bc1b3d0d25..947ad20b29 100644
--- a/disperser/encoder/server_v2.go
+++ b/disperser/encoder/server_v2.go
@@ -2,20 +2,24 @@ package encoder
import (
"context"
+ "errors"
"fmt"
"log"
"net"
+ "sync"
"time"
"github.com/Layr-Labs/eigenda/common/healthcheck"
corev2 "github.com/Layr-Labs/eigenda/core/v2"
"github.com/Layr-Labs/eigenda/disperser"
pb "github.com/Layr-Labs/eigenda/disperser/api/grpc/encoder/v2"
+ "github.com/Layr-Labs/eigenda/disperser/common"
"github.com/Layr-Labs/eigenda/disperser/common/v2/blobstore"
"github.com/Layr-Labs/eigenda/encoding"
"github.com/Layr-Labs/eigenda/encoding/rs"
"github.com/Layr-Labs/eigenda/relay/chunkstore"
"github.com/Layr-Labs/eigensdk-go/logging"
+ grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/reflection"
@@ -31,23 +35,38 @@ type EncoderServerV2 struct {
logger logging.Logger
prover encoding.Prover
metrics *Metrics
+ grpcMetrics *grpcprom.ServerMetrics
close func()
runningRequests chan struct{}
- requestPool chan struct{}
+ requestQueue chan blobRequest
+
+ queueStats map[string]int
+ queueLock sync.Mutex
}
-func NewEncoderServerV2(config ServerConfig, blobStore *blobstore.BlobStore, chunkWriter chunkstore.ChunkWriter, logger logging.Logger, prover encoding.Prover, metrics *Metrics) *EncoderServerV2 {
- return &EncoderServerV2{
- config: config,
- blobStore: blobStore,
- chunkWriter: chunkWriter,
- logger: logger.With("component", "EncoderServer"),
- prover: prover,
- metrics: metrics,
+func NewEncoderServerV2(
+ config ServerConfig,
+ blobStore *blobstore.BlobStore,
+ chunkWriter chunkstore.ChunkWriter,
+ logger logging.Logger,
+ prover encoding.Prover,
+ metrics *Metrics,
+ grpcMetrics *grpcprom.ServerMetrics,
+) *EncoderServerV2 {
+ metrics.SetQueueCapacity(config.RequestQueueSize)
+ return &EncoderServerV2{
+ config: config,
+ blobStore: blobStore,
+ chunkWriter: chunkWriter,
+ logger: logger.With("component", "EncoderServerV2"),
+ prover: prover,
+ metrics: metrics,
+ grpcMetrics: grpcMetrics,
runningRequests: make(chan struct{}, config.MaxConcurrentRequests),
- requestPool: make(chan struct{}, config.RequestPoolSize),
+ requestQueue: make(chan blobRequest, config.RequestQueueSize),
+ queueStats: make(map[string]int),
}
}
@@ -59,9 +78,14 @@ func (s *EncoderServerV2) Start() error {
log.Fatalf("Could not start tcp listener: %v", err)
}
- gs := grpc.NewServer()
+ gs := grpc.NewServer(
+ grpc.UnaryInterceptor(
+ s.grpcMetrics.UnaryServerInterceptor(),
+ ),
+ )
reflection.Register(gs)
pb.RegisterEncoderServer(gs, s)
+ s.grpcMetrics.InitializeMetrics(gs)
// Register Server for Health Checks
name := pb.Encoder_ServiceDesc.ServiceName
@@ -85,30 +109,36 @@ func (s *EncoderServerV2) EncodeBlob(ctx context.Context, req *pb.EncodeBlobRequ
s.metrics.ObserveLatency("total", time.Since(totalStart))
}()
+ blobSize := req.GetBlobSize()
+ sizeBucket := common.BlobSizeBucket(int(blobSize))
+
// Rate limit
select {
- case s.requestPool <- struct{}{}:
+ case s.requestQueue <- blobRequest{blobSizeByte: int(blobSize)}:
+ s.queueLock.Lock()
+ s.queueStats[sizeBucket]++
+ s.metrics.ObserveQueue(s.queueStats)
+ s.queueLock.Unlock()
default:
- // TODO: Now that we no longer pass the data directly, should we pass in blob size as part of the request?
- s.metrics.IncrementRateLimitedBlobRequestNum(1)
- s.logger.Warn("rate limiting as request pool is full", "requestPoolSize", s.config.RequestPoolSize, "maxConcurrentRequests", s.config.MaxConcurrentRequests)
- return nil, status.Error(codes.ResourceExhausted, "request pool is full")
+ s.metrics.IncrementRateLimitedBlobRequestNum(int(blobSize))
+ s.logger.Warn("rate limiting as request queue is full", "requestQueueSize", s.config.RequestQueueSize, "maxConcurrentRequests", s.config.MaxConcurrentRequests)
+ return nil, errors.New("too many requests")
}
// Limit the number of concurrent requests
s.runningRequests <- struct{}{}
defer s.popRequest()
if ctx.Err() != nil {
- s.metrics.IncrementCanceledBlobRequestNum(1)
+ s.metrics.IncrementCanceledBlobRequestNum(int(blobSize))
return nil, status.Error(codes.Canceled, "request was canceled")
}
s.metrics.ObserveLatency("queuing", time.Since(totalStart))
reply, err := s.handleEncodingToChunkStore(ctx, req)
if err != nil {
- s.metrics.IncrementFailedBlobRequestNum(1)
+ s.metrics.IncrementFailedBlobRequestNum(int(blobSize))
} else {
- s.metrics.IncrementSuccessfulBlobRequestNum(1)
+ s.metrics.IncrementSuccessfulBlobRequestNum(int(blobSize))
}
return reply, err
@@ -146,6 +176,7 @@ func (s *EncoderServerV2) handleEncodingToChunkStore(ctx context.Context, req *p
if len(data) == 0 {
return nil, status.Error(codes.NotFound, "blob length is zero")
}
+ s.metrics.ObserveLatency("s3_download", time.Since(fetchStart))
s.logger.Info("fetched blob", "duration", time.Since(fetchStart).String())
// Encode the data
@@ -155,15 +186,19 @@ func (s *EncoderServerV2) handleEncodingToChunkStore(ctx context.Context, req *p
s.logger.Error("failed to encode frames", "error", err)
return nil, status.Errorf(codes.Internal, "encoding failed: %v", err)
}
+ s.metrics.ObserveLatency("encoding", time.Since(encodingStart))
s.logger.Info("encoding frames", "duration", time.Since(encodingStart).String())
- // Process and store results
return s.processAndStoreResults(ctx, blobKey, frames)
}
func (s *EncoderServerV2) popRequest() {
- <-s.requestPool
+ blobRequest := <-s.requestQueue
<-s.runningRequests
+ s.queueLock.Lock()
+ s.queueStats[common.BlobSizeBucket(blobRequest.blobSizeByte)]--
+ s.metrics.ObserveQueue(s.queueStats)
+ s.queueLock.Unlock()
}
func (s *EncoderServerV2) validateAndParseRequest(req *pb.EncodeBlobRequest) (corev2.BlobKey, encoding.EncodingParams, error) {
@@ -214,13 +249,17 @@ func (s *EncoderServerV2) validateAndParseRequest(req *pb.EncodeBlobRequest) (co
}
func (s *EncoderServerV2) processAndStoreResults(ctx context.Context, blobKey corev2.BlobKey, frames []*encoding.Frame) (*pb.EncodeBlobReply, error) {
- proofs, coeffs := extractProofsAndCoeffs(frames)
-
// Store proofs
storeStart := time.Now()
+ defer func() {
+ s.metrics.ObserveLatency("process_and_store_results", time.Since(storeStart))
+ }()
+
+ proofs, coeffs := extractProofsAndCoeffs(frames)
if err := s.chunkWriter.PutChunkProofs(ctx, blobKey, proofs); err != nil {
return nil, status.Errorf(codes.Internal, "failed to upload chunk proofs: %v", err)
}
+ s.metrics.ObserveLatency("s3_upload_proofs", time.Since(storeStart))
s.logger.Info("stored proofs", "duration", time.Since(storeStart).String())
// Store coefficients
@@ -229,6 +268,7 @@ func (s *EncoderServerV2) processAndStoreResults(ctx context.Context, blobKey co
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to upload chunk coefficients: %v", err)
}
+ s.metrics.ObserveLatency("s3_upload_coefficients", time.Since(coeffStart))
s.logger.Info("stored coefficients", "duration", time.Since(coeffStart).String())
return &pb.EncodeBlobReply{
diff --git a/disperser/encoder/server_v2_test.go b/disperser/encoder/server_v2_test.go
index 15d13a9065..ae334c0542 100644
--- a/disperser/encoder/server_v2_test.go
+++ b/disperser/encoder/server_v2_test.go
@@ -18,6 +18,7 @@ import (
"github.com/Layr-Labs/eigenda/encoding/kzg/prover"
"github.com/Layr-Labs/eigenda/encoding/utils/codec"
"github.com/Layr-Labs/eigenda/relay/chunkstore"
+ grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -209,7 +210,12 @@ func createTestComponents(t *testing.T) *testComponents {
t.Helper()
prover, err := makeTestProver(300000)
require.NoError(t, err, "Failed to create prover")
- metrics := encoder.NewMetrics(prometheus.NewRegistry(), "9000", logger)
+
+ registry := prometheus.NewRegistry()
+ metrics := encoder.NewMetrics(registry, "9000", logger)
+ grpcMetrics := grpcprom.NewServerMetrics()
+ registry.MustRegister(grpcMetrics)
+
s3Client := mock.NewS3Client()
dynamoDBClient := &mock.MockDynamoDBClient{}
blobStore := blobstore.NewBlobStore(s3BucketName, s3Client, logger)
@@ -218,9 +224,9 @@ func createTestComponents(t *testing.T) *testComponents {
encoderServer := encoder.NewEncoderServerV2(encoder.ServerConfig{
GrpcPort: "8080",
MaxConcurrentRequests: 10,
- RequestPoolSize: 5,
+ RequestQueueSize: 5,
PreventReencoding: true,
- }, blobStore, chunkStoreWriter, logger, prover, metrics)
+ }, blobStore, chunkStoreWriter, logger, prover, metrics, grpcMetrics)
return &testComponents{
encoderServer: encoderServer,
diff --git a/disperser/encoder_client_v2.go b/disperser/encoder_client_v2.go
index a88031996e..a24dcce008 100644
--- a/disperser/encoder_client_v2.go
+++ b/disperser/encoder_client_v2.go
@@ -8,5 +8,5 @@ import (
)
type EncoderClientV2 interface {
- EncodeBlob(ctx context.Context, blobKey corev2.BlobKey, encodingParams encoding.EncodingParams) (*encoding.FragmentInfo, error)
+ EncodeBlob(ctx context.Context, blobKey corev2.BlobKey, encodingParams encoding.EncodingParams, blobSize uint64) (*encoding.FragmentInfo, error)
}
diff --git a/disperser/mock/encoder_v2.go b/disperser/mock/encoder_v2.go
index 7ef5d668b7..403a397cad 100644
--- a/disperser/mock/encoder_v2.go
+++ b/disperser/mock/encoder_v2.go
@@ -19,7 +19,7 @@ func NewMockEncoderClientV2() *MockEncoderClientV2 {
return &MockEncoderClientV2{}
}
-func (m *MockEncoderClientV2) EncodeBlob(ctx context.Context, blobKey corev2.BlobKey, encodingParams encoding.EncodingParams) (*encoding.FragmentInfo, error) {
+func (m *MockEncoderClientV2) EncodeBlob(ctx context.Context, blobKey corev2.BlobKey, encodingParams encoding.EncodingParams, blobSize uint64) (*encoding.FragmentInfo, error) {
args := m.Called()
var fragmentInfo *encoding.FragmentInfo
if args.Get(0) != nil {
diff --git a/docs/spec/overview.md b/docs/spec/overview.md
index 9e325f98db..a6a3be8976 100644
--- a/docs/spec/overview.md
+++ b/docs/spec/overview.md
@@ -104,7 +104,7 @@ and any set of adversarial operators $U_a \subseteq U_q$ such
$$ \sum_{i \in U_a} S_i \le \alpha \sum_{i \in O}S_i$$
-we we can reconstruct the original data blob from the chunks held by $U_q \setminus U_a$.
+we can reconstruct the original data blob from the chunks held by $U_q \setminus U_a$.
### Encoding Module
diff --git a/go.mod b/go.mod
index f14a3556e6..b5de6d4e7b 100644
--- a/go.mod
+++ b/go.mod
@@ -10,6 +10,7 @@ require (
github.com/aws/aws-sdk-go-v2 v1.26.1
github.com/aws/aws-sdk-go-v2/credentials v1.17.11
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.13.12
+ github.com/aws/aws-sdk-go-v2/service/kms v1.31.0
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.6
github.com/consensys/gnark-crypto v0.12.1
github.com/emirpasic/gods v1.18.1
@@ -63,7 +64,6 @@ require (
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.4 // indirect
- github.com/aws/aws-sdk-go-v2/service/kms v1.31.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect
diff --git a/inabox/deploy/config.go b/inabox/deploy/config.go
index b0775814ac..718eb85cb5 100644
--- a/inabox/deploy/config.go
+++ b/inabox/deploy/config.go
@@ -136,6 +136,7 @@ func (env *Config) applyDefaults(c any, prefix, stub string, ind int) {
// Generates churner .env
func (env *Config) generateChurnerVars(ind int, graphUrl, logPath, grpcPort string) ChurnerVars {
v := ChurnerVars{
+ CHURNER_LOG_FORMAT: "text",
CHURNER_HOSTNAME: "",
CHURNER_GRPC_PORT: grpcPort,
CHURNER_BLS_OPERATOR_STATE_RETRIVER: env.EigenDA.OperatorStateRetreiver,
@@ -160,6 +161,7 @@ func (env *Config) generateChurnerVars(ind int, graphUrl, logPath, grpcPort stri
// Generates disperser .env
func (env *Config) generateDisperserVars(ind int, logPath, dbPath, grpcPort string) DisperserVars {
v := DisperserVars{
+ DISPERSER_SERVER_LOG_FORMAT: "text",
DISPERSER_SERVER_S3_BUCKET_NAME: "test-eigenda-blobstore",
DISPERSER_SERVER_DYNAMODB_TABLE_NAME: "test-BlobMetadata",
DISPERSER_SERVER_RATE_BUCKET_TABLE_NAME: "",
@@ -197,6 +199,7 @@ func (env *Config) generateDisperserVars(ind int, logPath, dbPath, grpcPort stri
func (env *Config) generateDisperserV2Vars(ind int, logPath, dbPath, grpcPort string) DisperserVars {
v := DisperserVars{
+ DISPERSER_SERVER_LOG_FORMAT: "text",
DISPERSER_SERVER_S3_BUCKET_NAME: "test-eigenda-blobstore",
DISPERSER_SERVER_DYNAMODB_TABLE_NAME: "test-BlobMetadata-v2",
DISPERSER_SERVER_RATE_BUCKET_TABLE_NAME: "",
@@ -240,6 +243,7 @@ func (env *Config) generateDisperserV2Vars(ind int, logPath, dbPath, grpcPort st
// Generates batcher .env
func (env *Config) generateBatcherVars(ind int, key, graphUrl, logPath string) BatcherVars {
v := BatcherVars{
+ BATCHER_LOG_FORMAT: "text",
BATCHER_S3_BUCKET_NAME: "test-eigenda-blobstore",
BATCHER_DYNAMODB_TABLE_NAME: "test-BlobMetadata",
BATCHER_ENABLE_METRICS: "true",
@@ -273,6 +277,7 @@ func (env *Config) generateBatcherVars(ind int, key, graphUrl, logPath string) B
func (env *Config) generateEncoderVars(ind int, grpcPort string) EncoderVars {
v := EncoderVars{
+ DISPERSER_ENCODER_LOG_FORMAT: "text",
DISPERSER_ENCODER_AWS_REGION: "",
DISPERSER_ENCODER_AWS_ACCESS_KEY_ID: "",
DISPERSER_ENCODER_AWS_SECRET_ACCESS_KEY: "",
@@ -288,6 +293,7 @@ func (env *Config) generateEncoderVars(ind int, grpcPort string) EncoderVars {
DISPERSER_ENCODER_NUM_WORKERS: fmt.Sprint(runtime.GOMAXPROCS(0)),
DISPERSER_ENCODER_MAX_CONCURRENT_REQUESTS: "16",
DISPERSER_ENCODER_REQUEST_POOL_SIZE: "32",
+ DISPERSER_ENCODER_REQUEST_QUEUE_SIZE: "32",
}
env.applyDefaults(&v, "DISPERSER_ENCODER", "enc", ind)
@@ -297,6 +303,7 @@ func (env *Config) generateEncoderVars(ind int, grpcPort string) EncoderVars {
func (env *Config) generateEncoderV2Vars(ind int, grpcPort string) EncoderVars {
v := EncoderVars{
+ DISPERSER_ENCODER_LOG_FORMAT: "text",
DISPERSER_ENCODER_AWS_REGION: "",
DISPERSER_ENCODER_AWS_ACCESS_KEY_ID: "",
DISPERSER_ENCODER_AWS_SECRET_ACCESS_KEY: "",
@@ -314,6 +321,7 @@ func (env *Config) generateEncoderV2Vars(ind int, grpcPort string) EncoderVars {
DISPERSER_ENCODER_REQUEST_POOL_SIZE: "32",
DISPERSER_ENCODER_ENCODER_VERSION: "2",
DISPERSER_ENCODER_S3_BUCKET_NAME: "test-eigenda-blobstore",
+ DISPERSER_ENCODER_REQUEST_QUEUE_SIZE: "32",
}
env.applyDefaults(&v, "DISPERSER_ENCODER", "enc", ind)
@@ -321,28 +329,34 @@ func (env *Config) generateEncoderV2Vars(ind int, grpcPort string) EncoderVars {
return v
}
-func (env *Config) generateControllerVars(ind int, graphUrl string) ControllerVars {
+func (env *Config) generateControllerVars(
+ ind int,
+ graphUrl string) ControllerVars {
+
v := ControllerVars{
- CONTROLLER_DYNAMODB_TABLE_NAME: "test-BlobMetadata-v2",
- CONTROLLER_BLS_OPERATOR_STATE_RETRIVER: env.EigenDA.OperatorStateRetreiver,
- CONTROLLER_EIGENDA_SERVICE_MANAGER: env.EigenDA.ServiceManager,
- CONTROLLER_USE_GRAPH: "true",
- CONTROLLER_GRAPH_URL: graphUrl,
- CONTROLLER_ENCODING_PULL_INTERVAL: "1s",
- CONTROLLER_AVAILABLE_RELAYS: "0,1,2,3",
- CONTROLLER_DISPATCHER_PULL_INTERVAL: "3s",
- CONTROLLER_NODE_REQUEST_TIMEOUT: "5s",
- CONTROLLER_NUM_CONNECTIONS_TO_NODES: "10",
- CONTROLLER_CHAIN_RPC: "",
- CONTROLLER_PRIVATE_KEY: "123",
- CONTROLLER_NUM_CONFIRMATIONS: "0",
- CONTROLLER_INDEXER_PULL_INTERVAL: "1s",
- CONTROLLER_AWS_REGION: "",
- CONTROLLER_AWS_ACCESS_KEY_ID: "",
- CONTROLLER_AWS_SECRET_ACCESS_KEY: "",
- CONTROLLER_AWS_ENDPOINT_URL: "",
- CONTROLLER_ENCODER_ADDRESS: "0.0.0.0:34001",
- CONTROLLER_FINALIZATION_BLOCK_DELAY: "0",
+ CONTROLLER_LOG_FORMAT: "text",
+ CONTROLLER_DYNAMODB_TABLE_NAME: "test-BlobMetadata-v2",
+ CONTROLLER_BLS_OPERATOR_STATE_RETRIVER: env.EigenDA.OperatorStateRetreiver,
+ CONTROLLER_EIGENDA_SERVICE_MANAGER: env.EigenDA.ServiceManager,
+ CONTROLLER_USE_GRAPH: "true",
+ CONTROLLER_GRAPH_URL: graphUrl,
+ CONTROLLER_ENCODING_PULL_INTERVAL: "1s",
+ CONTROLLER_AVAILABLE_RELAYS: "0,1,2,3",
+ CONTROLLER_DISPATCHER_PULL_INTERVAL: "3s",
+ CONTROLLER_NODE_REQUEST_TIMEOUT: "5s",
+ CONTROLLER_NUM_CONNECTIONS_TO_NODES: "10",
+ CONTROLLER_CHAIN_RPC: "",
+ CONTROLLER_PRIVATE_KEY: "123",
+ CONTROLLER_NUM_CONFIRMATIONS: "0",
+ CONTROLLER_INDEXER_PULL_INTERVAL: "1s",
+ CONTROLLER_AWS_REGION: "",
+ CONTROLLER_AWS_ACCESS_KEY_ID: "",
+ CONTROLLER_AWS_SECRET_ACCESS_KEY: "",
+ CONTROLLER_AWS_ENDPOINT_URL: "",
+ CONTROLLER_ENCODER_ADDRESS: "0.0.0.0:34001",
+ CONTROLLER_FINALIZATION_BLOCK_DELAY: "0",
+ CONTROLLER_DISPERSER_STORE_CHUNKS_SIGNING_DISABLED: "false",
+ CONTROLLER_DISPERSER_KMS_KEY_ID: env.DisperserKMSKeyID,
}
env.applyDefaults(&v, "CONTROLLER", "controller", ind)
@@ -351,6 +365,7 @@ func (env *Config) generateControllerVars(ind int, graphUrl string) ControllerVa
func (env *Config) generateRelayVars(ind int, graphUrl, grpcPort string) RelayVars {
v := RelayVars{
+ RELAY_LOG_FORMAT: "text",
RELAY_GRPC_PORT: grpcPort,
RELAY_BUCKET_NAME: "test-eigenda-blobstore",
RELAY_METADATA_TABLE_NAME: "test-BlobMetadata-v2",
@@ -390,46 +405,48 @@ func (env *Config) generateOperatorVars(ind int, name, key, churnerUrl, logPath,
ecdsaPassword := env.Pks.EcdsaMap[name].Password
v := OperatorVars{
- NODE_HOSTNAME: "",
- NODE_DISPERSAL_PORT: dispersalPort,
- NODE_RETRIEVAL_PORT: retrievalPort,
- NODE_INTERNAL_DISPERSAL_PORT: dispersalPort,
- NODE_INTERNAL_RETRIEVAL_PORT: retrievalPort,
- NODE_ENABLE_METRICS: "true",
- NODE_METRICS_PORT: metricsPort,
- NODE_ENABLE_NODE_API: "true",
- NODE_API_PORT: nodeApiPort,
- NODE_TIMEOUT: "10s",
- NODE_QUORUM_ID_LIST: "0,1",
- NODE_DB_PATH: dbPath,
- NODE_ENABLE_TEST_MODE: "false", // using encrypted key in inabox
- NODE_TEST_PRIVATE_BLS: blsKey,
- NODE_BLS_KEY_FILE: blsKeyFile,
- NODE_ECDSA_KEY_FILE: ecdsaKeyFile,
- NODE_BLS_KEY_PASSWORD: blsPassword,
- NODE_ECDSA_KEY_PASSWORD: ecdsaPassword,
- NODE_BLS_OPERATOR_STATE_RETRIVER: env.EigenDA.OperatorStateRetreiver,
- NODE_EIGENDA_SERVICE_MANAGER: env.EigenDA.ServiceManager,
- NODE_REGISTER_AT_NODE_START: "true",
- NODE_CHURNER_URL: churnerUrl,
- NODE_CHURNER_USE_SECURE_GRPC: "false",
- NODE_EXPIRATION_POLL_INTERVAL: "10",
- NODE_G1_PATH: "",
- NODE_G2_PATH: "",
- NODE_G2_POWER_OF_2_PATH: "",
- NODE_CACHE_PATH: "",
- NODE_SRS_ORDER: "",
- NODE_SRS_LOAD: "",
- NODE_NUM_WORKERS: fmt.Sprint(runtime.GOMAXPROCS(0)),
- NODE_VERBOSE: "true",
- NODE_CHAIN_RPC: "",
- NODE_PRIVATE_KEY: key[2:],
- NODE_NUM_BATCH_VALIDATORS: "128",
- NODE_PUBLIC_IP_PROVIDER: "mockip",
- NODE_PUBLIC_IP_CHECK_INTERVAL: "10s",
- NODE_NUM_CONFIRMATIONS: "0",
- NODE_ONCHAIN_METRICS_INTERVAL: "-1",
- NODE_ENABLE_V2: "true",
+ NODE_LOG_FORMAT: "text",
+ NODE_HOSTNAME: "",
+ NODE_DISPERSAL_PORT: dispersalPort,
+ NODE_RETRIEVAL_PORT: retrievalPort,
+ NODE_INTERNAL_DISPERSAL_PORT: dispersalPort,
+ NODE_INTERNAL_RETRIEVAL_PORT: retrievalPort,
+ NODE_ENABLE_METRICS: "true",
+ NODE_METRICS_PORT: metricsPort,
+ NODE_ENABLE_NODE_API: "true",
+ NODE_API_PORT: nodeApiPort,
+ NODE_TIMEOUT: "10s",
+ NODE_QUORUM_ID_LIST: "0,1",
+ NODE_DB_PATH: dbPath,
+ NODE_ENABLE_TEST_MODE: "false", // using encrypted key in inabox
+ NODE_TEST_PRIVATE_BLS: blsKey,
+ NODE_BLS_KEY_FILE: blsKeyFile,
+ NODE_ECDSA_KEY_FILE: ecdsaKeyFile,
+ NODE_BLS_KEY_PASSWORD: blsPassword,
+ NODE_ECDSA_KEY_PASSWORD: ecdsaPassword,
+ NODE_BLS_OPERATOR_STATE_RETRIVER: env.EigenDA.OperatorStateRetreiver,
+ NODE_EIGENDA_SERVICE_MANAGER: env.EigenDA.ServiceManager,
+ NODE_REGISTER_AT_NODE_START: "true",
+ NODE_CHURNER_URL: churnerUrl,
+ NODE_CHURNER_USE_SECURE_GRPC: "false",
+ NODE_EXPIRATION_POLL_INTERVAL: "10",
+ NODE_G1_PATH: "",
+ NODE_G2_PATH: "",
+ NODE_G2_POWER_OF_2_PATH: "",
+ NODE_CACHE_PATH: "",
+ NODE_SRS_ORDER: "",
+ NODE_SRS_LOAD: "",
+ NODE_NUM_WORKERS: fmt.Sprint(runtime.GOMAXPROCS(0)),
+ NODE_VERBOSE: "true",
+ NODE_CHAIN_RPC: "",
+ NODE_PRIVATE_KEY: key[2:],
+ NODE_NUM_BATCH_VALIDATORS: "128",
+ NODE_PUBLIC_IP_PROVIDER: "mockip",
+ NODE_PUBLIC_IP_CHECK_INTERVAL: "10s",
+ NODE_NUM_CONFIRMATIONS: "0",
+ NODE_ONCHAIN_METRICS_INTERVAL: "-1",
+ NODE_ENABLE_V2: "true",
+ NODE_DISABLE_DISPERSAL_AUTHENTICATION: "false",
}
env.applyDefaults(&v, "NODE", "opr", ind)
@@ -441,6 +458,7 @@ func (env *Config) generateOperatorVars(ind int, name, key, churnerUrl, logPath,
// Generates retriever .env
func (env *Config) generateRetrieverVars(ind int, key string, graphUrl, logPath, grpcPort string) RetrieverVars {
v := RetrieverVars{
+ RETRIEVER_LOG_FORMAT: "text",
RETRIEVER_HOSTNAME: "",
RETRIEVER_GRPC_PORT: grpcPort,
RETRIEVER_TIMEOUT: "10s",
@@ -463,8 +481,6 @@ func (env *Config) generateRetrieverVars(ind int, key string, graphUrl, logPath,
RETRIEVER_GRAPH_URL: graphUrl,
RETRIEVER_GRAPH_BACKOFF: "1s",
RETRIEVER_GRAPH_MAX_RETRIES: "3",
-
- RETRIEVER_INDEXER_PULL_INTERVAL: "1s",
}
v.RETRIEVER_G2_PATH = ""
@@ -571,15 +587,13 @@ func (env *Config) GenerateAllVariables() {
// hardcode graphurl for now
graphUrl := "http://localhost:8000/subgraphs/name/Layr-Labs/eigenda-operator-state"
+ env.localstackEndpoint = "http://localhost:4570"
+ env.localstackRegion = "us-east-1"
+
// Create envs directory
createDirectory(env.Path + "/envs")
changeDirectory(env.rootPath + "/inabox")
- // Gather keys
- // keyData := readFile(gethPrivateKeys)
- // keys := strings.Split(string(keyData), "\n")
- // id := 1
-
// Create compose file
composeFile := env.Path + "/docker-compose.yml"
servicesMap := make(map[string]map[string]interface{})
diff --git a/inabox/deploy/config_types.go b/inabox/deploy/config_types.go
index 2d1f04d5ef..970696b47a 100644
--- a/inabox/deploy/config_types.go
+++ b/inabox/deploy/config_types.go
@@ -2,6 +2,7 @@ package deploy
import (
"encoding/json"
+ "github.com/ethereum/go-ethereum/common"
"log"
"os"
"path/filepath"
@@ -103,6 +104,7 @@ type EigenDAContract struct {
OperatorStateRetreiver string `json:"operatorStateRetriever"`
BlsApkRegistry string `json:"blsApkRegistry"`
RegistryCoordinator string `json:"registryCoordinator"`
+ BlobVerifier string `json:"blobVerifier"`
}
type Stakes struct {
@@ -181,10 +183,19 @@ type Config struct {
Retriever RetrieverVars
Controller ControllerVars
Relays []RelayVars
+
+ localstackEndpoint string
+ localstackRegion string
+
+ // DisperserAddress is the address of disperser 0 (aka the only disperser at the current time)
+ DisperserAddress common.Address
+
+ // DisperserKMSKeyID is the KMS key ID used to encrypt disperser data
+ DisperserKMSKeyID string
}
-func (c Config) IsEigenDADeployed() bool {
- return c.EigenDA.ServiceManager != ""
+func (env *Config) IsEigenDADeployed() bool {
+ return env.EigenDA.ServiceManager != ""
}
func NewTestConfig(testName, rootPath string) (testEnv *Config) {
diff --git a/inabox/deploy/deploy.go b/inabox/deploy/deploy.go
index be81c26a31..516d6748fc 100644
--- a/inabox/deploy/deploy.go
+++ b/inabox/deploy/deploy.go
@@ -4,17 +4,24 @@ import (
"context"
"encoding/json"
"fmt"
+ "github.com/Layr-Labs/eigenda/common"
+ caws "github.com/Layr-Labs/eigenda/common/aws"
+ relayreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDARelayRegistry"
+ eigendasrvmg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAServiceManager"
+ thresholdreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAThresholdRegistry"
+ "github.com/Layr-Labs/eigenda/core/eth"
+ "github.com/aws/aws-sdk-go-v2/aws"
+ "github.com/aws/aws-sdk-go-v2/service/kms"
+ "github.com/aws/aws-sdk-go-v2/service/kms/types"
+ "github.com/ethereum/go-ethereum/crypto"
"io"
"log"
"math/big"
"os"
"path/filepath"
"strconv"
-
- "github.com/Layr-Labs/eigenda/common"
- relayreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDARelayRegistry"
- eigendasrvmg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAServiceManager"
- thresholdreg "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAThresholdRegistry"
+ "strings"
+ "time"
"github.com/Layr-Labs/eigenda/core"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
@@ -155,12 +162,108 @@ func (env *Config) DeployExperiment() {
env.deploySubgraphs(startBlock)
}
+ // Ideally these should be set in GenerateAllVariables, but they need to be used in GenerateDisperserKeypair
+ // which is called before GenerateAllVariables
+ env.localstackEndpoint = "http://localhost:4570"
+ env.localstackRegion = "us-east-1"
+
+ fmt.Println("Generating disperser keypair")
+ err = env.GenerateDisperserKeypair()
+ if err != nil {
+ log.Panicf("could not generate disperser keypair: %v", err)
+ }
+
fmt.Println("Generating variables")
env.GenerateAllVariables()
fmt.Println("Test environment has successfully deployed!")
}
+// GenerateDisperserKeypair generates a disperser keypair using AWS KMS.
+func (env *Config) GenerateDisperserKeypair() error {
+
+ // Generate a keypair in AWS KMS
+
+ keyManager := kms.New(kms.Options{
+ Region: env.localstackRegion,
+ BaseEndpoint: aws.String(env.localstackEndpoint),
+ })
+
+ createKeyOutput, err := keyManager.CreateKey(context.Background(), &kms.CreateKeyInput{
+ KeySpec: types.KeySpecEccSecgP256k1,
+ KeyUsage: types.KeyUsageTypeSignVerify,
+ })
+ if err != nil {
+ if strings.Contains(err.Error(), "connect: connection refused") {
+ log.Printf("Unable to reach local stack, skipping disperser keypair generation. Error: %v", err)
+ err = nil
+ }
+ return err
+ }
+
+ env.DisperserKMSKeyID = *createKeyOutput.KeyMetadata.KeyId
+
+ // Load the public key and convert it to an Ethereum address
+
+ key, err := caws.LoadPublicKeyKMS(context.Background(), keyManager, env.DisperserKMSKeyID)
+ if err != nil {
+ return fmt.Errorf("could not load public key: %v", err)
+ }
+
+ env.DisperserAddress = crypto.PubkeyToAddress(*key)
+ log.Printf("Generated disperser keypair: key ID: %s, address: %s",
+ env.DisperserKMSKeyID, env.DisperserAddress.Hex())
+
+ return nil
+}
+
+// RegisterDisperserKeypair registers the disperser's public key on-chain.
+func (env *Config) RegisterDisperserKeypair(ethClient common.EthClient) error {
+
+ // Write the disperser's public key to on-chain storage
+
+ loggerConfig := common.DefaultLoggerConfig()
+ logger, err := common.NewLogger(loggerConfig)
+ if err != nil {
+ return fmt.Errorf("could not create logger: %v", err)
+ }
+
+ writer, err := eth.NewWriter(
+ logger,
+ ethClient,
+ env.Retriever.RETRIEVER_BLS_OPERATOR_STATE_RETRIVER,
+ env.Retriever.RETRIEVER_EIGENDA_SERVICE_MANAGER)
+ if err != nil {
+ return fmt.Errorf("could not create writer: %v", err)
+ }
+
+ err = writer.SetDisperserAddress(context.Background(), env.DisperserAddress)
+ if err != nil {
+ return fmt.Errorf("could not set disperser address: %v", err)
+ }
+
+ // Read the disperser's public key from on-chain storage to verify it was written correctly
+
+ retryTimeout := time.Now().Add(1 * time.Minute)
+ ticker := time.NewTicker(1 * time.Second)
+
+ for time.Now().Before(retryTimeout) {
+ address, err := writer.GetDisperserAddress(context.Background(), 0)
+ if err != nil {
+ logger.Warnf("could not get disperser address: %v", err)
+ } else {
+ if address != env.DisperserAddress {
+ return fmt.Errorf("expected disperser address %s, got %s", env.DisperserAddress, address)
+ }
+ return nil
+ }
+
+ <-ticker.C
+ }
+
+ return fmt.Errorf("timed out waiting for disperser address to be set")
+}
+
func (env *Config) RegisterBlobVersionAndRelays(ethClient common.EthClient) map[uint32]string {
dasmAddr := gcommon.HexToAddress(env.EigenDA.ServiceManager)
contractEigenDAServiceManager, err := eigendasrvmg.NewContractEigenDAServiceManager(dasmAddr, ethClient)
@@ -203,10 +306,11 @@ func (env *Config) RegisterBlobVersionAndRelays(ethClient common.EthClient) map[
log.Panicf("Error: %s", err)
}
relays := map[uint32]string{}
+ ethAddr := ethClient.GetAccountAddress()
for i, relayVars := range env.Relays {
url := fmt.Sprintf("0.0.0.0:%s", relayVars.RELAY_GRPC_PORT)
txn, err := contractRelayRegistry.AddRelayInfo(opts, relayreg.RelayInfo{
- RelayAddress: gcommon.Address{0},
+ RelayAddress: ethAddr,
RelayURL: url,
})
if err != nil {
diff --git a/inabox/deploy/env_vars.go b/inabox/deploy/env_vars.go
index e55984eb01..52f3fd1167 100644
--- a/inabox/deploy/env_vars.go
+++ b/inabox/deploy/env_vars.go
@@ -265,12 +265,18 @@ type EncoderVars struct {
DISPERSER_ENCODER_REQUEST_POOL_SIZE string
+ DISPERSER_ENCODER_REQUEST_QUEUE_SIZE string
+
DISPERSER_ENCODER_ENABLE_GNARK_CHUNK_ENCODING string
DISPERSER_ENCODER_ENCODER_VERSION string
DISPERSER_ENCODER_S3_BUCKET_NAME string
+ DISPERSER_ENCODER_GPU_ENABLE string
+
+ DISPERSER_ENCODER_BACKEND string
+
DISPERSER_ENCODER_PREVENT_REENCODING string
DISPERSER_ENCODER_PPROF_HTTP_PORT string
@@ -416,10 +422,20 @@ type OperatorVars struct {
NODE_ONCHAIN_STATE_REFRESH_INTERVAL string
+ NODE_CHUNK_DOWNLOAD_TIMEOUT string
+
NODE_PPROF_HTTP_PORT string
NODE_ENABLE_PPROF string
+ NODE_DISABLE_DISPERSAL_AUTHENTICATION string
+
+ NODE_DISPERSAL_AUTHENTICATION_KEY_CACHE_SIZE string
+
+ NODE_DISPERSER_KEY_TIMEOUT string
+
+ NODE_DISPERSAL_AUTHENTICATION_TIMEOUT string
+
NODE_G1_PATH string
NODE_G2_PATH string
@@ -479,11 +495,9 @@ type RetrieverVars struct {
RETRIEVER_NUM_CONNECTIONS string
- RETRIEVER_DATA_DIR string
-
RETRIEVER_METRICS_HTTP_PORT string
- RETRIEVER_USE_GRAPH string
+ RETRIEVER_EIGENDA_VERSION string
RETRIEVER_G1_PATH string
@@ -521,8 +535,6 @@ type RetrieverVars struct {
RETRIEVER_LOG_FORMAT string
- RETRIEVER_INDEXER_PULL_INTERVAL string
-
RETRIEVER_GRAPH_URL string
RETRIEVER_GRAPH_BACKOFF string
@@ -637,6 +649,12 @@ type ControllerVars struct {
CONTROLLER_MAX_BATCH_SIZE string
+ CONTROLLER_METRICS_PORT string
+
+ CONTROLLER_DISPERSER_STORE_CHUNKS_SIGNING_DISABLED string
+
+ CONTROLLER_DISPERSER_KMS_KEY_ID string
+
CONTROLLER_CHAIN_RPC string
CONTROLLER_CHAIN_RPC_FALLBACK string
@@ -716,6 +734,8 @@ type RelayVars struct {
RELAY_CHUNK_MAX_CONCURRENCY string
+ RELAY_MAX_KEYS_PER_GET_CHUNKS_REQUEST string
+
RELAY_MAX_GET_BLOB_OPS_PER_SECOND string
RELAY_GET_BLOB_OPS_BURSTINESS string
@@ -746,8 +766,6 @@ type RelayVars struct {
RELAY_MAX_CONCURRENT_GET_CHUNK_OPS_CLIENT string
- RELAY_INDEXER_PULL_INTERVAL string
-
RELAY_AUTHENTICATION_KEY_CACHE_SIZE string
RELAY_AUTHENTICATION_TIMEOUT string
@@ -768,6 +786,8 @@ type RelayVars struct {
RELAY_ONCHAIN_STATE_REFRESH_INTERVAL string
+ RELAY_METRICS_PORT string
+
RELAY_LOG_LEVEL string
RELAY_LOG_PATH string
diff --git a/inabox/tests/integration_suite_test.go b/inabox/tests/integration_suite_test.go
index 9eb1703e87..85ac8b3642 100644
--- a/inabox/tests/integration_suite_test.go
+++ b/inabox/tests/integration_suite_test.go
@@ -14,6 +14,7 @@ import (
clientsv2 "github.com/Layr-Labs/eigenda/api/clients/v2"
"github.com/Layr-Labs/eigenda/common"
"github.com/Layr-Labs/eigenda/common/geth"
+ verifierbindings "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDABlobVerifier"
rollupbindings "github.com/Layr-Labs/eigenda/contracts/bindings/MockRollup"
"github.com/Layr-Labs/eigenda/core"
"github.com/Layr-Labs/eigenda/core/eth"
@@ -47,6 +48,7 @@ var (
ethClient common.EthClient
rpcClient common.RPCEthClient
mockRollup *rollupbindings.ContractMockRollup
+ verifierContract *verifierbindings.ContractEigenDABlobVerifier
retrievalClient clients.RetrievalClient
retrievalClientV2 clientsv2.RetrievalClient
numConfirmations int = 3
@@ -109,13 +111,13 @@ var _ = BeforeSuite(func() {
testConfig.StartGraphNode()
}
- fmt.Println("Deploying experiment")
- testConfig.DeployExperiment()
-
loggerConfig := common.DefaultLoggerConfig()
logger, err = common.NewLogger(loggerConfig)
Expect(err).To(BeNil())
+ fmt.Println("Deploying experiment")
+ testConfig.DeployExperiment()
+
pk := testConfig.Pks.EcdsaMap["default"].PrivateKey
pk = strings.TrimPrefix(pk, "0x")
pk = strings.TrimPrefix(pk, "0X")
@@ -126,17 +128,26 @@ var _ = BeforeSuite(func() {
NumRetries: numRetries,
}, gcommon.Address{}, logger)
Expect(err).To(BeNil())
+
rpcClient, err = ethrpc.Dial(testConfig.Deployers[0].RPC)
Expect(err).To(BeNil())
fmt.Println("Registering blob versions and relays")
relays = testConfig.RegisterBlobVersionAndRelays(ethClient)
+ fmt.Println("Registering disperser keypair")
+ err = testConfig.RegisterDisperserKeypair(ethClient)
+ if err != nil {
+ panic(err)
+ }
+
fmt.Println("Starting binaries")
testConfig.StartBinaries()
mockRollup, err = rollupbindings.NewContractMockRollup(gcommon.HexToAddress(testConfig.MockRollup), ethClient)
Expect(err).To(BeNil())
+ verifierContract, err = verifierbindings.NewContractEigenDABlobVerifier(gcommon.HexToAddress(testConfig.EigenDA.BlobVerifier), ethClient)
+ Expect(err).To(BeNil())
err = setupRetrievalClient(testConfig)
Expect(err).To(BeNil())
}
diff --git a/inabox/tests/integration_v2_test.go b/inabox/tests/integration_v2_test.go
index 97f299c492..443e445e30 100644
--- a/inabox/tests/integration_v2_test.go
+++ b/inabox/tests/integration_v2_test.go
@@ -4,16 +4,20 @@ import (
"bytes"
"context"
"crypto/rand"
+ "fmt"
+ "math/big"
"time"
"github.com/Layr-Labs/eigenda/api/clients/v2"
commonpb "github.com/Layr-Labs/eigenda/api/grpc/common/v2"
disperserpb "github.com/Layr-Labs/eigenda/api/grpc/disperser/v2"
+ verifierbindings "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDABlobVerifier"
"github.com/Layr-Labs/eigenda/core"
auth "github.com/Layr-Labs/eigenda/core/auth/v2"
corev2 "github.com/Layr-Labs/eigenda/core/v2"
dispv2 "github.com/Layr-Labs/eigenda/disperser/common/v2"
"github.com/Layr-Labs/eigenda/encoding/utils/codec"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/wealdtech/go-merkletree/v2"
@@ -51,7 +55,7 @@ var _ = Describe("Inabox v2 Integration", func() {
Expect(blobStatus1).To(Not(BeNil()))
Expect(*blobStatus1).To(Equal(dispv2.Queued))
- blobStatus2, key2, err := disp.DisperseBlob(ctx, paddedData2, 0, []uint8{0}, 0)
+ blobStatus2, key2, err := disp.DisperseBlob(ctx, paddedData2, 0, []uint8{0, 1}, 0)
Expect(err).To(BeNil())
Expect(key2).To(Not(BeNil()))
Expect(blobStatus2).To(Not(BeNil()))
@@ -66,6 +70,10 @@ var _ = Describe("Inabox v2 Integration", func() {
var blobCert2 *corev2.BlobCertificate
var batchHeader1 *commonpb.BatchHeader
var batchHeader2 *commonpb.BatchHeader
+ var signedBatch1 *disperserpb.SignedBatch
+ var signedBatch2 *disperserpb.SignedBatch
+ var blobVerification1 *disperserpb.BlobVerificationInfo
+ var blobVerification2 *disperserpb.BlobVerificationInfo
for loop := true; loop; {
select {
case <-ctx.Done():
@@ -87,7 +95,8 @@ var _ = Describe("Inabox v2 Integration", func() {
continue
}
- batchHeader1 = reply1.GetSignedBatch().GetHeader()
+ signedBatch1 = reply1.GetSignedBatch()
+ batchHeader1 = signedBatch1.GetHeader()
Expect(batchHeader1).To(Not(BeNil()))
Expect(batchHeader1.GetBatchRoot()).To(Not(BeNil()))
Expect(batchHeader1.GetReferenceBlockNumber()).To(BeNumerically(">", 0))
@@ -98,13 +107,13 @@ var _ = Describe("Inabox v2 Integration", func() {
Expect(attestation.ApkG2).To(Not(BeNil()))
Expect(len(attestation.QuorumApks)).To(Equal(2))
Expect(attestation.QuorumSignedPercentages).To(Equal([]byte{100, 100}))
- blobVerification := reply1.GetBlobVerificationInfo()
- Expect(blobVerification).To(Not(BeNil()))
- Expect(blobVerification.GetBlobCertificate()).To(Not(BeNil()))
- blobCert1, err = corev2.BlobCertificateFromProtobuf(blobVerification.GetBlobCertificate())
+ blobVerification1 = reply1.GetBlobVerificationInfo()
+ Expect(blobVerification1).To(Not(BeNil()))
+ Expect(blobVerification1.GetBlobCertificate()).To(Not(BeNil()))
+ blobCert1, err = corev2.BlobCertificateFromProtobuf(blobVerification1.GetBlobCertificate())
Expect(err).To(BeNil())
- inclusionProofBytes := blobVerification.GetInclusionProof()
- blobIndex := blobVerification.GetBlobIndex()
+ inclusionProofBytes := blobVerification1.GetInclusionProof()
+ blobIndex := blobVerification1.GetBlobIndex()
proof, err := core.DeserializeMerkleProof(inclusionProofBytes, uint64(blobIndex))
Expect(err).To(BeNil())
certHash, err := blobCert1.Hash()
@@ -115,39 +124,29 @@ var _ = Describe("Inabox v2 Integration", func() {
Expect(err).To(BeNil())
Expect(verified).To(BeTrue())
- batchHeader2 = reply2.GetSignedBatch().GetHeader()
+ signedBatch2 = reply2.GetSignedBatch()
+ batchHeader2 = signedBatch2.GetHeader()
Expect(batchHeader2).To(Not(BeNil()))
Expect(batchHeader2.GetBatchRoot()).To(Not(BeNil()))
Expect(batchHeader2.GetReferenceBlockNumber()).To(BeNumerically(">", 0))
attestation = reply2.GetSignedBatch().GetAttestation()
Expect(attestation).To(Not(BeNil()))
- if bytes.Equal(batchHeader2.BatchRoot, batchHeader1.BatchRoot) {
- // same batch
- attestation2 := reply2.GetSignedBatch().GetAttestation()
- Expect(attestation2).To(Not(BeNil()))
- Expect(attestation2.QuorumNumbers).To(Equal(attestation.QuorumNumbers))
- Expect(len(attestation2.NonSignerPubkeys)).To(Equal(len(attestation.NonSignerPubkeys)))
- Expect(attestation2.ApkG2).To(Equal(attestation.ApkG2))
- Expect(len(attestation2.QuorumApks)).To(Equal(len(attestation.QuorumApks)))
- Expect(attestation2.QuorumSignedPercentages).To(Equal(attestation.QuorumSignedPercentages))
- } else {
- attestation = reply2.GetSignedBatch().GetAttestation()
- Expect(attestation).To(Not(BeNil()))
- Expect(attestation.QuorumNumbers).To(ConsistOf([]uint32{0}))
- Expect(len(attestation.NonSignerPubkeys)).To(Equal(0))
- Expect(attestation.ApkG2).To(Not(BeNil()))
- Expect(len(attestation.QuorumApks)).To(Equal(1))
- Expect(attestation.QuorumSignedPercentages).To(Equal([]byte{100}))
- }
+ attestation2 := reply2.GetSignedBatch().GetAttestation()
+ Expect(attestation2).To(Not(BeNil()))
+ Expect(attestation2.QuorumNumbers).To(Equal(attestation.QuorumNumbers))
+ Expect(len(attestation2.NonSignerPubkeys)).To(Equal(len(attestation.NonSignerPubkeys)))
+ Expect(attestation2.ApkG2).To(Equal(attestation.ApkG2))
+ Expect(len(attestation2.QuorumApks)).To(Equal(len(attestation.QuorumApks)))
+ Expect(attestation2.QuorumSignedPercentages).To(Equal(attestation.QuorumSignedPercentages))
- blobVerification = reply2.GetBlobVerificationInfo()
- Expect(blobVerification).To(Not(BeNil()))
- Expect(blobVerification.GetBlobCertificate()).To(Not(BeNil()))
- blobCert2, err = corev2.BlobCertificateFromProtobuf(blobVerification.GetBlobCertificate())
+ blobVerification2 = reply2.GetBlobVerificationInfo()
+ Expect(blobVerification2).To(Not(BeNil()))
+ Expect(blobVerification2.GetBlobCertificate()).To(Not(BeNil()))
+ blobCert2, err = corev2.BlobCertificateFromProtobuf(blobVerification2.GetBlobCertificate())
Expect(err).To(BeNil())
- inclusionProofBytes = blobVerification.GetInclusionProof()
- blobIndex = blobVerification.GetBlobIndex()
+ inclusionProofBytes = blobVerification2.GetInclusionProof()
+ blobIndex = blobVerification2.GetBlobIndex()
proof, err = core.DeserializeMerkleProof(inclusionProofBytes, uint64(blobIndex))
Expect(err).To(BeNil())
certHash, err = blobCert2.Hash()
@@ -155,11 +154,53 @@ var _ = Describe("Inabox v2 Integration", func() {
verified, err = merkletree.VerifyProofUsing(certHash[:], false, proof, [][]byte{batchHeader2.BatchRoot}, keccak256.New())
Expect(err).To(BeNil())
Expect(verified).To(BeTrue())
- // TODO(ian-shim): verify the blob onchain using a mock rollup contract
loop = false
}
}
+ // necessary to ensure that reference block number < current block number
+ mineAnvilBlocks(1)
+
+ // test onchain verification
+ attestation, err := convertAttestation(signedBatch1.GetAttestation())
+ Expect(err).To(BeNil())
+ proof, err := convertBlobVerificationInfo(blobVerification1)
+ Expect(err).To(BeNil())
+
+ var batchRoot [32]byte
+ copy(batchRoot[:], batchHeader1.BatchRoot)
+
+ err = verifierContract.VerifyBlobV2FromSignedBatch(
+ &bind.CallOpts{},
+ verifierbindings.SignedBatch{
+ BatchHeader: verifierbindings.BatchHeaderV2{
+ BatchRoot: batchRoot,
+ ReferenceBlockNumber: uint32(batchHeader1.ReferenceBlockNumber),
+ },
+ Attestation: *attestation,
+ },
+ *proof,
+ )
+ Expect(err).To(BeNil())
+
+ attestation, err = convertAttestation(signedBatch2.GetAttestation())
+ Expect(err).To(BeNil())
+ proof, err = convertBlobVerificationInfo(blobVerification2)
+ Expect(err).To(BeNil())
+ copy(batchRoot[:], batchHeader2.BatchRoot)
+ err = verifierContract.VerifyBlobV2FromSignedBatch(
+ &bind.CallOpts{},
+ verifierbindings.SignedBatch{
+ BatchHeader: verifierbindings.BatchHeaderV2{
+ BatchRoot: batchRoot,
+ ReferenceBlockNumber: uint32(batchHeader2.ReferenceBlockNumber),
+ },
+ Attestation: *attestation,
+ },
+ *proof,
+ )
+ Expect(err).To(BeNil())
+
// Test retrieval from relay
relayClient, err := clients.NewRelayClient(&clients.RelayClientConfig{
Sockets: relays,
@@ -192,6 +233,7 @@ var _ = Describe("Inabox v2 Integration", func() {
}
}
+ // Test retrieval from DA network
b, err := retrievalClientV2.GetBlob(ctx, blobCert1.BlobHeader, batchHeader1.ReferenceBlockNumber, 0)
Expect(err).To(BeNil())
restored := bytes.TrimRight(b, "\x00")
@@ -206,7 +248,152 @@ var _ = Describe("Inabox v2 Integration", func() {
Expect(restored).To(Equal(paddedData2))
b, err = retrievalClientV2.GetBlob(ctx, blobCert2.BlobHeader, batchHeader2.ReferenceBlockNumber, 1)
restored = bytes.TrimRight(b, "\x00")
- Expect(err).NotTo(BeNil())
- Expect(restored).To(BeNil())
+ Expect(err).To(BeNil())
+ Expect(restored).To(Equal(paddedData2))
})
})
+
+func convertBlobVerificationInfo(verificationInfo *disperserpb.BlobVerificationInfo) (*verifierbindings.BlobVerificationProofV2, error) {
+ blobCertificate, err := corev2.BlobCertificateFromProtobuf(verificationInfo.GetBlobCertificate())
+ if err != nil {
+ return nil, err
+ }
+ paymentHeaderHash, err := blobCertificate.BlobHeader.PaymentMetadata.Hash()
+ if err != nil {
+ return nil, err
+ }
+
+ inclusionProof := verificationInfo.GetInclusionProof()
+ blobIndex := verificationInfo.GetBlobIndex()
+
+ commitX := big.NewInt(0)
+ blobCertificate.BlobHeader.BlobCommitments.Commitment.X.BigInt(commitX)
+ commitY := big.NewInt(0)
+ blobCertificate.BlobHeader.BlobCommitments.Commitment.Y.BigInt(commitY)
+ lengthCommitX0 := big.NewInt(0)
+ blobCertificate.BlobHeader.BlobCommitments.LengthCommitment.X.A0.BigInt(lengthCommitX0)
+ lengthCommitX1 := big.NewInt(0)
+ blobCertificate.BlobHeader.BlobCommitments.LengthCommitment.X.A1.BigInt(lengthCommitX1)
+ lengthCommitY0 := big.NewInt(0)
+ blobCertificate.BlobHeader.BlobCommitments.LengthCommitment.Y.A0.BigInt(lengthCommitY0)
+ lengthCommitY1 := big.NewInt(0)
+ blobCertificate.BlobHeader.BlobCommitments.LengthCommitment.Y.A1.BigInt(lengthCommitY1)
+ lengthProofX0 := big.NewInt(0)
+ blobCertificate.BlobHeader.BlobCommitments.LengthProof.X.A0.BigInt(lengthProofX0)
+ lengthProofX1 := big.NewInt(0)
+ blobCertificate.BlobHeader.BlobCommitments.LengthProof.X.A1.BigInt(lengthProofX1)
+ lengthProofY0 := big.NewInt(0)
+ blobCertificate.BlobHeader.BlobCommitments.LengthProof.Y.A0.BigInt(lengthProofY0)
+ lengthProofY1 := big.NewInt(0)
+ blobCertificate.BlobHeader.BlobCommitments.LengthProof.Y.A1.BigInt(lengthProofY1)
+ return &verifierbindings.BlobVerificationProofV2{
+ BlobCertificate: verifierbindings.BlobCertificate{
+ BlobHeader: verifierbindings.BlobHeaderV2{
+ Version: uint16(blobCertificate.BlobHeader.BlobVersion),
+ QuorumNumbers: blobCertificate.BlobHeader.QuorumNumbers,
+ Commitment: verifierbindings.BlobCommitment{
+ Commitment: verifierbindings.BN254G1Point{
+ X: commitX,
+ Y: commitY,
+ },
+ LengthCommitment: verifierbindings.BN254G2Point{
+ X: [2]*big.Int{lengthCommitX0, lengthCommitX1},
+ Y: [2]*big.Int{lengthCommitY0, lengthCommitY1},
+ },
+ LengthProof: verifierbindings.BN254G2Point{
+ X: [2]*big.Int{lengthProofX0, lengthProofX1},
+ Y: [2]*big.Int{lengthProofY0, lengthProofY1},
+ },
+ DataLength: uint32(blobCertificate.BlobHeader.BlobCommitments.Length),
+ },
+ PaymentHeaderHash: paymentHeaderHash,
+ },
+ RelayKeys: blobCertificate.RelayKeys,
+ },
+ InclusionProof: inclusionProof,
+ BlobIndex: blobIndex,
+ }, nil
+}
+
+func convertAttestation(attestation *disperserpb.Attestation) (*verifierbindings.Attestation, error) {
+ if attestation == nil {
+ return nil, fmt.Errorf("attestation is nil")
+ }
+ nonSignerPubkeys := make([]verifierbindings.BN254G1Point, 0)
+ for _, pubkey := range attestation.GetNonSignerPubkeys() {
+ pk, err := convertG1Point(pubkey)
+ if err != nil {
+ return nil, err
+ }
+ nonSignerPubkeys = append(nonSignerPubkeys, *pk)
+ }
+
+ quorumApks := make([]verifierbindings.BN254G1Point, 0)
+ for _, apk := range attestation.GetQuorumApks() {
+ apk, err := convertG1Point(apk)
+ if err != nil {
+ return nil, err
+ }
+ quorumApks = append(quorumApks, *apk)
+ }
+
+ if attestation.GetSigma() == nil {
+ return nil, fmt.Errorf("attestation sigma is nil")
+ }
+ sigma, err := convertG1Point(attestation.GetSigma())
+ if err != nil {
+ return nil, err
+ }
+
+ if attestation.GetApkG2() == nil {
+ return nil, fmt.Errorf("attestation apkG2 is nil")
+ }
+ apkg2, err := convertG2Point(attestation.GetApkG2())
+ if err != nil {
+ return nil, err
+ }
+
+ return &verifierbindings.Attestation{
+ NonSignerPubkeys: nonSignerPubkeys,
+ QuorumApks: quorumApks,
+ Sigma: *sigma,
+ ApkG2: *apkg2,
+ QuorumNumbers: attestation.GetQuorumNumbers(),
+ }, nil
+}
+
+func convertG1Point(data []byte) (*verifierbindings.BN254G1Point, error) {
+ point, err := new(core.G1Point).Deserialize(data)
+ if err != nil {
+ return nil, err
+ }
+ x := big.NewInt(0)
+ y := big.NewInt(0)
+
+ point.X.BigInt(x)
+ point.Y.BigInt(y)
+ return &verifierbindings.BN254G1Point{
+ X: x,
+ Y: y,
+ }, nil
+}
+
+func convertG2Point(data []byte) (*verifierbindings.BN254G2Point, error) {
+ point, err := new(core.G2Point).Deserialize(data)
+ if err != nil {
+ return nil, err
+ }
+ x0 := big.NewInt(0)
+ x1 := big.NewInt(0)
+ y0 := big.NewInt(0)
+ y1 := big.NewInt(0)
+
+ point.X.A0.BigInt(x0)
+ point.X.A1.BigInt(x1)
+ point.Y.A0.BigInt(y0)
+ point.Y.A1.BigInt(y1)
+ return &verifierbindings.BN254G2Point{
+ X: [2]*big.Int{x1, x0},
+ Y: [2]*big.Int{y1, y0},
+ }, nil
+}
diff --git a/node/auth/authenticator.go b/node/auth/authenticator.go
new file mode 100644
index 0000000000..a5a1a464ad
--- /dev/null
+++ b/node/auth/authenticator.go
@@ -0,0 +1,185 @@
+package auth
+
+import (
+ "context"
+ "fmt"
+ "github.com/Layr-Labs/eigenda/api"
+ grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2"
+ "github.com/Layr-Labs/eigenda/core"
+ gethcommon "github.com/ethereum/go-ethereum/common"
+ lru "github.com/hashicorp/golang-lru/v2"
+ "time"
+)
+
+// RequestAuthenticator authenticates requests to the DA node. This object is thread safe.
+//
+// This class has largely been future-proofed for decentralized dispersers, with the exception of the
+// preloadCache method, which will need to be updated to handle decentralized dispersers.
+type RequestAuthenticator interface {
+ // AuthenticateStoreChunksRequest authenticates a StoreChunksRequest, returning an error if the request is invalid.
+ // The origin is the address of the peer that sent the request. This may be used to cache auth results
+ // in order to save node resources.
+ AuthenticateStoreChunksRequest(
+ ctx context.Context,
+ origin string,
+ request *grpc.StoreChunksRequest,
+ now time.Time) error
+}
+
+// keyWithTimeout is a key with that key's expiration time. After a key "expires", it should be reloaded
+// from the chain state in case the key has been changed.
+type keyWithTimeout struct {
+ key gethcommon.Address
+ expiration time.Time
+}
+
+var _ RequestAuthenticator = &requestAuthenticator{}
+
+type requestAuthenticator struct {
+ // chainReader is used to read the chain state.
+ chainReader core.Reader
+
+ // keyCache is used to cache the public keys of dispersers. The uint32 map keys are disperser IDs. Disperser
+ // IDs are serial numbers, with the original EigenDA disperser assigned ID 0. The map values contain
+ // the public key of the disperser and the time when the local cache of the key will expire.
+ keyCache *lru.Cache[uint32 /* disperser ID */, *keyWithTimeout]
+
+ // keyTimeoutDuration is the duration for which a key is cached. After this duration, the key should be
+ // reloaded from the chain state in case the key has been changed.
+ keyTimeoutDuration time.Duration
+
+ // authenticatedDispersers is a set of disperser addresses that have been recently authenticated, mapped
+ // to the time when that cached authentication will expire.
+ authenticatedDispersers *lru.Cache[string, time.Time]
+
+ // authenticationTimeoutDuration is the duration for which an auth is valid.
+ // If this is zero, then auth saving is disabled, and each request will be authenticated independently.
+ authenticationTimeoutDuration time.Duration
+
+ // disperserIDFilter is a function that returns true if the given disperser ID is valid.
+ disperserIDFilter func(uint32) bool
+}
+
+// NewRequestAuthenticator creates a new RequestAuthenticator.
+func NewRequestAuthenticator(
+ ctx context.Context,
+ chainReader core.Reader,
+ keyCacheSize int,
+ keyTimeoutDuration time.Duration,
+ authenticationTimeoutDuration time.Duration,
+ disperserIDFilter func(uint32) bool,
+ now time.Time) (RequestAuthenticator, error) {
+
+ keyCache, err := lru.New[uint32, *keyWithTimeout](keyCacheSize)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create key cache: %w", err)
+ }
+
+ authenticatedDispersers, err := lru.New[string, time.Time](keyCacheSize)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create authenticated dispersers cache: %w", err)
+ }
+
+ authenticator := &requestAuthenticator{
+ chainReader: chainReader,
+ keyCache: keyCache,
+ keyTimeoutDuration: keyTimeoutDuration,
+ authenticatedDispersers: authenticatedDispersers,
+ authenticationTimeoutDuration: authenticationTimeoutDuration,
+ disperserIDFilter: disperserIDFilter,
+ }
+
+ err = authenticator.preloadCache(ctx, now)
+ if err != nil {
+ return nil, fmt.Errorf("failed to preload cache: %w", err)
+ }
+
+ return authenticator, nil
+}
+
+func (a *requestAuthenticator) preloadCache(ctx context.Context, now time.Time) error {
+ // this will need to be updated for decentralized dispersers
+ _, err := a.getDisperserKey(ctx, now, api.EigenLabsDisperserID)
+ if err != nil {
+ return fmt.Errorf("failed to get operator key: %w", err)
+ }
+
+ return nil
+}
+
+func (a *requestAuthenticator) AuthenticateStoreChunksRequest(
+ ctx context.Context,
+ origin string,
+ request *grpc.StoreChunksRequest,
+ now time.Time) error {
+
+ if a.isAuthenticationStillValid(now, origin) {
+ // We've recently authenticated this client. Do not authenticate again for a while.
+ return nil
+ }
+
+ key, err := a.getDisperserKey(ctx, now, request.DisperserID)
+ if err != nil {
+ return fmt.Errorf("failed to get operator key: %w", err)
+ }
+
+ err = VerifyStoreChunksRequest(*key, request)
+ if err != nil {
+ return fmt.Errorf("failed to verify request: %w", err)
+ }
+
+ a.cacheAuthenticationResult(now, origin)
+ return nil
+}
+
+// getDisperserKey returns the public key of the operator with the given ID, caching the result.
+func (a *requestAuthenticator) getDisperserKey(
+ ctx context.Context,
+ now time.Time,
+ disperserID uint32) (*gethcommon.Address, error) {
+
+ if !a.disperserIDFilter(disperserID) {
+ return nil, fmt.Errorf("invalid disperser ID: %d", disperserID)
+ }
+
+ key, ok := a.keyCache.Get(disperserID)
+ if ok {
+ expirationTime := key.expiration
+ if now.Before(expirationTime) {
+ return &key.key, nil
+ }
+ }
+
+ address, err := a.chainReader.GetDisperserAddress(ctx, disperserID)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get disperser address: %w", err)
+ }
+
+ a.keyCache.Add(disperserID, &keyWithTimeout{
+ key: address,
+ expiration: now.Add(a.keyTimeoutDuration),
+ })
+
+ return &address, nil
+}
+
+// cacheAuthenticationResult saves the result of an auth.
+func (a *requestAuthenticator) cacheAuthenticationResult(now time.Time, origin string) {
+ if a.authenticationTimeoutDuration == 0 {
+ // Authentication saving is disabled.
+ return
+ }
+
+ a.authenticatedDispersers.Add(origin, now.Add(a.authenticationTimeoutDuration))
+}
+
+// isAuthenticationStillValid returns true if the client at the given address has been authenticated recently.
+func (a *requestAuthenticator) isAuthenticationStillValid(now time.Time, address string) bool {
+ if a.authenticationTimeoutDuration == 0 {
+ // Authentication saving is disabled.
+ return false
+ }
+
+ expiration, ok := a.authenticatedDispersers.Get(address)
+ return ok && now.Before(expiration)
+}
diff --git a/node/auth/authenticator_test.go b/node/auth/authenticator_test.go
new file mode 100644
index 0000000000..28ae0122ec
--- /dev/null
+++ b/node/auth/authenticator_test.go
@@ -0,0 +1,512 @@
+package auth
+
+import (
+ "context"
+ "crypto/ecdsa"
+ "errors"
+ "fmt"
+ "github.com/Layr-Labs/eigenda/common/testutils/random"
+ wmock "github.com/Layr-Labs/eigenda/core/mock"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/stretchr/testify/require"
+ "sync/atomic"
+ "testing"
+ "time"
+)
+
+func TestValidRequest(t *testing.T) {
+ rand := random.NewTestRandom(t)
+
+ start := rand.Time()
+
+ publicKey, privateKey := rand.ECDSA()
+ disperserAddress := crypto.PubkeyToAddress(*publicKey)
+
+ chainReader := wmock.MockWriter{}
+ chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil)
+
+ authenticator, err := NewRequestAuthenticator(
+ context.Background(),
+ &chainReader,
+ 10,
+ time.Minute,
+ time.Minute,
+ func(uint32) bool { return true },
+ start)
+ require.NoError(t, err)
+
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err := SignStoreChunksRequest(privateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start)
+ require.NoError(t, err)
+}
+
+func TestInvalidRequestWrongHash(t *testing.T) {
+ rand := random.NewTestRandom(t)
+
+ start := rand.Time()
+
+ publicKey, privateKey := rand.ECDSA()
+ disperserAddress := crypto.PubkeyToAddress(*publicKey)
+
+ chainReader := wmock.MockWriter{}
+ chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil)
+
+ authenticator, err := NewRequestAuthenticator(
+ context.Background(),
+ &chainReader,
+ 10,
+ time.Minute,
+ time.Minute,
+ func(uint32) bool { return true },
+ start)
+ require.NoError(t, err)
+
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err := SignStoreChunksRequest(privateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ // Modify the request so that the hash is different
+ request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32)
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start)
+ require.Error(t, err)
+}
+
+func TestInvalidRequestWrongKey(t *testing.T) {
+ rand := random.NewTestRandom(t)
+
+ start := rand.Time()
+
+ publicKey, _ := rand.ECDSA()
+ disperserAddress := crypto.PubkeyToAddress(*publicKey)
+
+ chainReader := wmock.MockWriter{}
+ chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil)
+
+ authenticator, err := NewRequestAuthenticator(
+ context.Background(),
+ &chainReader,
+ 10,
+ time.Minute,
+ time.Minute,
+ func(uint32) bool { return true },
+ start)
+ require.NoError(t, err)
+
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+
+ _, differentPrivateKey := rand.ECDSA()
+ signature, err := SignStoreChunksRequest(differentPrivateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start)
+ require.Error(t, err)
+}
+
+func TestInvalidRequestInvalidDisperserID(t *testing.T) {
+ rand := random.NewTestRandom(t)
+
+ start := rand.Time()
+
+ publicKey0, privateKey0 := rand.ECDSA()
+ disperserAddress0 := crypto.PubkeyToAddress(*publicKey0)
+
+ // This disperser will be loaded on chain (simulated), but will fail the valid disperser ID filter.
+ publicKey1, privateKey1 := rand.ECDSA()
+ disperserAddress1 := crypto.PubkeyToAddress(*publicKey1)
+
+ chainReader := wmock.MockWriter{}
+ chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress0, nil)
+ chainReader.Mock.On("GetDisperserAddress", uint32(1)).Return(disperserAddress1, nil)
+ chainReader.Mock.On("GetDisperserAddress", uint32(1234)).Return(
+ nil, errors.New("disperser not found"))
+
+ filterCallCount := atomic.Uint32{}
+
+ authenticator, err := NewRequestAuthenticator(
+ context.Background(),
+ &chainReader,
+ 10,
+ time.Minute,
+ 0, /* disable auth caching */
+ func(id uint32) bool {
+ filterCallCount.Add(1)
+ return id != uint32(1)
+ },
+ start)
+ require.NoError(t, err)
+ require.Equal(t, uint32(1), filterCallCount.Load())
+
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err := SignStoreChunksRequest(privateKey0, request)
+ require.NoError(t, err)
+ request.Signature = signature
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start)
+ require.NoError(t, err)
+ require.Equal(t, uint32(2), filterCallCount.Load())
+
+ request.DisperserID = 1
+ signature, err = SignStoreChunksRequest(privateKey1, request)
+ require.NoError(t, err)
+ request.Signature = signature
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start)
+ require.Error(t, err)
+ require.Equal(t, uint32(3), filterCallCount.Load())
+
+ request.DisperserID = 1234
+ signature, err = SignStoreChunksRequest(privateKey1, request)
+ require.NoError(t, err)
+ request.Signature = signature
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start)
+ require.Error(t, err)
+ require.Equal(t, uint32(4), filterCallCount.Load())
+}
+
+func TestAuthCaching(t *testing.T) {
+ rand := random.NewTestRandom(t)
+
+ start := rand.Time()
+
+ publicKey, privateKey := rand.ECDSA()
+ disperserAddress := crypto.PubkeyToAddress(*publicKey)
+
+ chainReader := wmock.MockWriter{}
+ chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil)
+
+ authenticator, err := NewRequestAuthenticator(
+ context.Background(),
+ &chainReader,
+ 10,
+ time.Minute,
+ time.Minute,
+ func(uint32) bool { return true },
+ start)
+ require.NoError(t, err)
+
+ // The first request will actually be validated.
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err := SignStoreChunksRequest(privateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start)
+ require.NoError(t, err)
+
+ // Make some more requests. Intentionally fiddle with the hash to make them invalid if checked.
+ // With auth caching, those checks won't happen until the auth timeout has passed (configured to 1 minute).
+ now := start
+ for i := 0; i < 60; i++ {
+ request = RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err = SignStoreChunksRequest(privateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32)
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, now)
+ now = now.Add(time.Second)
+ require.NoError(t, err)
+
+ // making the same request from a different origin should cause validation to happen and for it to fail
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "otherhost", request, now)
+ require.Error(t, err)
+ }
+
+ // The next request will be made after the auth timeout has passed, so it will be validated.
+ // Since it is actually invalid, the authenticator should reject it.
+ request = RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err = SignStoreChunksRequest(privateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32)
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, now)
+ require.Error(t, err)
+}
+
+func TestAuthCachingDisabled(t *testing.T) {
+ rand := random.NewTestRandom(t)
+
+ start := rand.Time()
+
+ publicKey, privateKey := rand.ECDSA()
+ disperserAddress := crypto.PubkeyToAddress(*publicKey)
+
+ chainReader := wmock.MockWriter{}
+ chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil)
+
+ authenticator, err := NewRequestAuthenticator(
+ context.Background(),
+ &chainReader,
+ 10,
+ time.Minute,
+ 0, // This disables auth caching
+ func(uint32) bool { return true },
+ start)
+ require.NoError(t, err)
+
+ // The first request will always be validated.
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err := SignStoreChunksRequest(privateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start)
+ require.NoError(t, err)
+
+ // Make another request without moving time forward. It should be validated.
+ request = RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err = SignStoreChunksRequest(privateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32)
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start)
+ require.Error(t, err)
+}
+
+func TestKeyExpiry(t *testing.T) {
+ rand := random.NewTestRandom(t)
+
+ start := rand.Time()
+
+ publicKey, privateKey := rand.ECDSA()
+ disperserAddress := crypto.PubkeyToAddress(*publicKey)
+
+ chainReader := wmock.MockWriter{}
+ chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil)
+
+ authenticator, err := NewRequestAuthenticator(
+ context.Background(),
+ &chainReader,
+ 10,
+ time.Minute,
+ time.Minute,
+ func(uint32) bool { return true },
+ start)
+ require.NoError(t, err)
+
+ // Preloading the cache should have grabbed Disperser 0's key
+ chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", 1)
+
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err := SignStoreChunksRequest(privateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, start)
+ require.NoError(t, err)
+
+ // Since time hasn't advanced, the authenticator shouldn't have fetched the key again
+ chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", 1)
+
+ // Move time forward to just before the key expires.
+ now := start.Add(59 * time.Second)
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, now)
+ require.NoError(t, err)
+
+ // The key should not yet have been fetched again.
+ chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", 1)
+
+ // Move time forward to just after the key expires.
+ now = now.Add(2 * time.Second)
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "localhost", request, now)
+ require.NoError(t, err)
+
+ // The key should have been fetched again.
+ chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", 2)
+}
+
+func TestAuthCacheSize(t *testing.T) {
+ rand := random.NewTestRandom(t)
+
+ start := rand.Time()
+
+ publicKey, privateKey := rand.ECDSA()
+ disperserAddress := crypto.PubkeyToAddress(*publicKey)
+
+ chainReader := wmock.MockWriter{}
+ chainReader.Mock.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil)
+
+ cacheSize := rand.Intn(10) + 2
+
+ authenticator, err := NewRequestAuthenticator(
+ context.Background(),
+ &chainReader,
+ cacheSize,
+ time.Minute,
+ time.Minute,
+ func(uint32) bool { return true },
+ start)
+ require.NoError(t, err)
+
+ // Make requests from cacheSize different origins.
+ for i := 0; i < cacheSize; i++ {
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err := SignStoreChunksRequest(privateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ origin := fmt.Sprintf("%d", i)
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), origin, request, start)
+ require.NoError(t, err)
+ }
+
+ // All origins should be authenticated in the auth cache. If we send invalid requests from the same origins,
+ // they should still be authenticated (since the authenticator won't re-check).
+ for i := 0; i < cacheSize; i++ {
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err := SignStoreChunksRequest(privateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32)
+
+ origin := fmt.Sprintf("%d", i)
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), origin, request, start)
+ require.NoError(t, err)
+ }
+
+ // Make a request from a new origin. This should boot origin 0 from the cache.
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err := SignStoreChunksRequest(privateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "neworigin", request, start)
+ require.NoError(t, err)
+
+ for i := 0; i < cacheSize; i++ {
+ request = RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err = SignStoreChunksRequest(privateKey, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32)
+
+ origin := fmt.Sprintf("%d", i)
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), origin, request, start)
+
+ if i == 0 {
+ // Origin 0 should have been booted from the cache, so this request should be re-validated.
+ require.Error(t, err)
+ } else {
+ // All other origins should still be in the cache.
+ require.NoError(t, err)
+ }
+ }
+}
+
+func TestKeyCacheSize(t *testing.T) {
+ rand := random.NewTestRandom(t)
+
+ start := rand.Time()
+
+ cacheSize := rand.Intn(10) + 2
+
+ chainReader := wmock.MockWriter{}
+ keyMap := make(map[uint32]*ecdsa.PrivateKey, cacheSize+1)
+ for i := 0; i < cacheSize+1; i++ {
+ publicKey, privateKey := rand.ECDSA()
+ disperserAddress := crypto.PubkeyToAddress(*publicKey)
+ keyMap[uint32(i)] = privateKey
+
+ chainReader.Mock.On("GetDisperserAddress", uint32(i)).Return(disperserAddress, nil)
+ }
+
+ authenticator, err := NewRequestAuthenticator(
+ context.Background(),
+ &chainReader,
+ cacheSize,
+ time.Minute,
+ 0, // disable auth caching
+ func(uint32) bool { return true },
+ start)
+ require.NoError(t, err)
+
+ // The authenticator will preload key 0 into the cache.
+ chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", 1)
+
+ // Make a request for each key (except for the last one, which won't fit in the cache).
+ for i := 0; i < cacheSize; i++ {
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = uint32(i)
+ signature, err := SignStoreChunksRequest(keyMap[uint32(i)], request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ origin := fmt.Sprintf("%d", i)
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), origin, request, start)
+ require.NoError(t, err)
+ }
+
+ // All keys should have required exactly one read except from 0, which was preloaded.
+ chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", cacheSize)
+
+ // Make another request for each key. None should require a read from the chain.
+ for i := 0; i < cacheSize; i++ {
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = uint32(i)
+ signature, err := SignStoreChunksRequest(keyMap[uint32(i)], request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ origin := fmt.Sprintf("%d", i)
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), origin, request, start)
+ require.NoError(t, err)
+ }
+
+ chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", cacheSize)
+
+ // Make a request for the last key. This should require a read from the chain and will boot key 0 from the cache.
+ request := RandomStoreChunksRequest(rand)
+ request.DisperserID = uint32(cacheSize)
+ signature, err := SignStoreChunksRequest(keyMap[uint32(cacheSize)], request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(),
+ fmt.Sprintf("%d", cacheSize), request, start)
+ require.NoError(t, err)
+
+ chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", cacheSize+1)
+
+ // Make another request for key 0. This should require a read from the chain.
+ request = RandomStoreChunksRequest(rand)
+ request.DisperserID = 0
+ signature, err = SignStoreChunksRequest(keyMap[0], request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ err = authenticator.AuthenticateStoreChunksRequest(context.Background(), "0", request, start)
+ require.NoError(t, err)
+
+ chainReader.Mock.AssertNumberOfCalls(t, "GetDisperserAddress", cacheSize+2)
+}
diff --git a/node/auth/request_signing.go b/node/auth/request_signing.go
new file mode 100644
index 0000000000..1591c48076
--- /dev/null
+++ b/node/auth/request_signing.go
@@ -0,0 +1,41 @@
+package auth
+
+import (
+ "crypto/ecdsa"
+ "fmt"
+ grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2"
+ "github.com/Layr-Labs/eigenda/api/hashing"
+ gethcommon "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+)
+
+// SignStoreChunksRequest signs the given StoreChunksRequest with the given private key. Does not
+// write the signature into the request.
+func SignStoreChunksRequest(key *ecdsa.PrivateKey, request *grpc.StoreChunksRequest) ([]byte, error) {
+ requestHash := hashing.HashStoreChunksRequest(request)
+
+ signature, err := crypto.Sign(requestHash, key)
+ if err != nil {
+ return nil, fmt.Errorf("failed to sign request: %w", err)
+ }
+
+ return signature, nil
+}
+
+// VerifyStoreChunksRequest verifies the given signature of the given StoreChunksRequest with the given
+// public key.
+func VerifyStoreChunksRequest(key gethcommon.Address, request *grpc.StoreChunksRequest) error {
+ requestHash := hashing.HashStoreChunksRequest(request)
+
+ signingPublicKey, err := crypto.SigToPub(requestHash, request.Signature)
+ if err != nil {
+ return fmt.Errorf("failed to recover public key from signature %x: %w", request.Signature, err)
+ }
+
+ signingAddress := crypto.PubkeyToAddress(*signingPublicKey)
+
+ if key.Cmp(signingAddress) != 0 {
+ return fmt.Errorf("signature doesn't match with provided public key")
+ }
+ return nil
+}
diff --git a/node/auth/request_signing_test.go b/node/auth/request_signing_test.go
new file mode 100644
index 0000000000..e4122c18b8
--- /dev/null
+++ b/node/auth/request_signing_test.go
@@ -0,0 +1,181 @@
+package auth
+
+import (
+ "github.com/Layr-Labs/eigenda/api/hashing"
+ "github.com/Layr-Labs/eigenda/common/testutils/random"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/stretchr/testify/require"
+ "testing"
+)
+
+func TestHashing(t *testing.T) {
+ rand := random.NewTestRandom(t)
+
+ request := RandomStoreChunksRequest(rand)
+ originalRequestHash := hashing.HashStoreChunksRequest(request)
+
+ // modifying the signature should not change the hash
+ request.Signature = rand.Bytes(32)
+ hash := hashing.HashStoreChunksRequest(request)
+ require.Equal(t, originalRequestHash, hash)
+
+ // modify the disperser id
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.DisperserID = request.DisperserID + 1
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // remove a blob cert
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates = request.Batch.BlobCertificates[:len(request.Batch.BlobCertificates)-1]
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, modify a relay
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].Relays[0] = request.Batch.BlobCertificates[0].Relays[0] + 1
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, remove a relay
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].Relays =
+ request.Batch.BlobCertificates[0].Relays[:len(request.Batch.BlobCertificates[0].Relays)-1]
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, add a relay
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].Relays = append(request.Batch.BlobCertificates[0].Relays, rand.Uint32())
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, modify a quorum number
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers[0] =
+ request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers[0] + 1
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, remove a quorum number
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers =
+ request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers[:len(
+ request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers)-1]
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, add a quorum number
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers = append(
+ request.Batch.BlobCertificates[0].BlobHeader.QuorumNumbers, rand.Uint32())
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, modify the Commitment.Commitment
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].BlobHeader.Commitment.Commitment = rand.Bytes(32)
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, modify the Commitment.LengthCommitment
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthCommitment = rand.Bytes(32)
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, modify the Commitment.LengthProof
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].BlobHeader.Commitment.LengthProof = rand.Bytes(32)
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, modify the Commitment.Length
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].BlobHeader.Commitment.Length = rand.Uint32()
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, modify the PaymentHeader.AccountId
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.AccountId = rand.String(32)
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, modify the PaymentHeader.ReservationPeriod
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.ReservationPeriod = rand.Uint32()
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, modify the PaymentHeader.CumulativePayment
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.CumulativePayment = rand.Bytes(32)
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, modify the PaymentHeader.Salt
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].BlobHeader.PaymentHeader.Salt = rand.Uint32()
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+
+ // within a blob cert, modify the Signature
+ rand.Reset()
+ request = RandomStoreChunksRequest(rand)
+ request.Batch.BlobCertificates[0].BlobHeader.Signature = rand.Bytes(32)
+ hash = hashing.HashStoreChunksRequest(request)
+ require.NotEqual(t, originalRequestHash, hash)
+}
+
+func TestRequestSigning(t *testing.T) {
+ rand := random.NewTestRandom(t)
+
+ public, private := rand.ECDSA()
+ publicAddress := crypto.PubkeyToAddress(*public)
+
+ request := RandomStoreChunksRequest(rand)
+
+ signature, err := SignStoreChunksRequest(private, request)
+ require.NoError(t, err)
+ request.Signature = signature
+
+ err = VerifyStoreChunksRequest(publicAddress, request)
+ require.NoError(t, err)
+
+ // Using a different public key should make the signature invalid
+ otherPublic, _ := rand.ECDSA()
+ otherPublicAddress := crypto.PubkeyToAddress(*otherPublic)
+ err = VerifyStoreChunksRequest(otherPublicAddress, request)
+ require.Error(t, err)
+
+ // Changing a byte in the signature should make it invalid
+ alteredSignature := make([]byte, len(signature))
+ copy(alteredSignature, signature)
+ alteredSignature[0] = alteredSignature[0] + 1
+ request.Signature = alteredSignature
+ err = VerifyStoreChunksRequest(publicAddress, request)
+ require.Error(t, err)
+
+ // Changing a field in the request should make it invalid
+ request.DisperserID = request.DisperserID + 1
+ request.Signature = signature
+ err = VerifyStoreChunksRequest(publicAddress, request)
+ require.Error(t, err)
+}
diff --git a/node/auth/request_signing_test_utils.go b/node/auth/request_signing_test_utils.go
new file mode 100644
index 0000000000..cdc82956a4
--- /dev/null
+++ b/node/auth/request_signing_test_utils.go
@@ -0,0 +1,60 @@
+package auth
+
+import (
+ "github.com/Layr-Labs/eigenda/api/grpc/common"
+ v2 "github.com/Layr-Labs/eigenda/api/grpc/common/v2"
+ grpc "github.com/Layr-Labs/eigenda/api/grpc/node/v2"
+ "github.com/Layr-Labs/eigenda/common/testutils/random"
+)
+
+func RandomStoreChunksRequest(rand *random.TestRandom) *grpc.StoreChunksRequest {
+ certificateCount := rand.Intn(10) + 1
+ blobCertificates := make([]*v2.BlobCertificate, certificateCount)
+ for i := 0; i < certificateCount; i++ {
+
+ relayCount := rand.Intn(10) + 1
+ relays := make([]uint32, relayCount)
+ for j := 0; j < relayCount; j++ {
+ relays[j] = rand.Uint32()
+ }
+
+ quorumCount := rand.Intn(10) + 1
+ quorumNumbers := make([]uint32, quorumCount)
+ for j := 0; j < quorumCount; j++ {
+ quorumNumbers[j] = rand.Uint32()
+ }
+
+ blobCertificates[i] = &v2.BlobCertificate{
+ BlobHeader: &v2.BlobHeader{
+ Version: rand.Uint32(),
+ QuorumNumbers: quorumNumbers,
+ Commitment: &common.BlobCommitment{
+ Commitment: rand.Bytes(32),
+ LengthCommitment: rand.Bytes(32),
+ LengthProof: rand.Bytes(32),
+ Length: rand.Uint32(),
+ },
+ PaymentHeader: &common.PaymentHeader{
+ AccountId: rand.String(32),
+ ReservationPeriod: rand.Uint32(),
+ CumulativePayment: rand.Bytes(32),
+ Salt: rand.Uint32(),
+ },
+ Signature: rand.Bytes(32),
+ },
+ Relays: relays,
+ }
+ }
+
+ return &grpc.StoreChunksRequest{
+ Batch: &v2.Batch{
+ Header: &v2.BatchHeader{
+ BatchRoot: rand.Bytes(32),
+ ReferenceBlockNumber: rand.Uint64(),
+ },
+ BlobCertificates: blobCertificates,
+ },
+ DisperserID: rand.Uint32(),
+ Signature: rand.Bytes(32),
+ }
+}
diff --git a/node/cmd/main.go b/node/cmd/main.go
index a54e7bdfdb..f4b696fd73 100644
--- a/node/cmd/main.go
+++ b/node/cmd/main.go
@@ -3,6 +3,9 @@ package main
import (
"context"
"fmt"
+ "github.com/Layr-Labs/eigenda/common/geth"
+ coreeth "github.com/Layr-Labs/eigenda/core/eth"
+ rpccalls "github.com/Layr-Labs/eigensdk-go/metrics/collectors/rpc_calls"
"log"
"os"
"time"
@@ -72,8 +75,23 @@ func NodeMain(ctx *cli.Context) error {
ratelimiter := ratelimit.NewRateLimiter(reg, globalParams, bucketStore, logger)
+ rpcCallsCollector := rpccalls.NewCollector(node.AppName, reg)
+ client, err := geth.NewInstrumentedEthClient(config.EthClientConfig, rpcCallsCollector, logger)
+ if err != nil {
+ return fmt.Errorf("cannot create chain.Client: %w", err)
+ }
+
+ reader, err := coreeth.NewReader(
+ logger,
+ client,
+ config.BLSOperatorStateRetrieverAddr,
+ config.EigenDAServiceManagerAddr)
+ if err != nil {
+ return fmt.Errorf("cannot create eth.Reader: %w", err)
+ }
+
// Create the node.
- node, err := node.NewNode(reg, config, pubIPProvider, logger)
+ node, err := node.NewNode(reg, config, pubIPProvider, client, logger)
if err != nil {
return err
}
@@ -89,7 +107,7 @@ func NodeMain(ctx *cli.Context) error {
// TODO(cody-littley): the metrics server is currently started by eigenmetrics, which is in another repo.
// When we fully remove v1 support, we need to start the metrics server inside the v2 metrics code.
server := nodegrpc.NewServer(config, node, logger, ratelimiter)
- serverV2, err := nodegrpc.NewServerV2(config, node, logger, ratelimiter, reg)
+ serverV2, err := nodegrpc.NewServerV2(context.Background(), config, node, logger, ratelimiter, reg, reader)
if err != nil {
return fmt.Errorf("failed to create server v2: %v", err)
}
diff --git a/node/config.go b/node/config.go
index 7bf051efdd..6b4bec16cb 100644
--- a/node/config.go
+++ b/node/config.go
@@ -3,7 +3,6 @@ package node
import (
"errors"
"fmt"
-
"os"
"strconv"
"strings"
@@ -95,6 +94,18 @@ type Config struct {
PprofHttpPort string
EnablePprof bool
+
+ // if true then the node will not authenticate StoreChunks requests from dispersers (v2 only)
+ DisableDispersalAuthentication bool
+ // the size of the cache for storing public keys of dispersers
+ DispersalAuthenticationKeyCacheSize int
+ // the timeout for disperser keys (after which the disperser key is reloaded from the chain)
+ DisperserKeyTimeout time.Duration
+ // the timeout for disperser authentication (set to 0 to disable), if enabled then a successful authentication
+ // of a StoreChunks request causes the node to skip validation for requests coming from the same IP address
+ // for this duration. Adds risk of disruptive behavior if an attacker is able to send requests from the same IP
+ // address as a legitimate disperser, but reduces performance overhead of StoreChunks validation.
+ DispersalAuthenticationTimeout time.Duration
}
// NewConfig parses the Config from the provided flags or environment variables and
@@ -199,50 +210,54 @@ func NewConfig(ctx *cli.Context) (*Config, error) {
}
return &Config{
- Hostname: ctx.GlobalString(flags.HostnameFlag.Name),
- DispersalPort: ctx.GlobalString(flags.DispersalPortFlag.Name),
- RetrievalPort: ctx.GlobalString(flags.RetrievalPortFlag.Name),
- InternalDispersalPort: internalDispersalFlag,
- InternalRetrievalPort: internalRetrievalFlag,
- EnableNodeApi: ctx.GlobalBool(flags.EnableNodeApiFlag.Name),
- NodeApiPort: ctx.GlobalString(flags.NodeApiPortFlag.Name),
- EnableMetrics: ctx.GlobalBool(flags.EnableMetricsFlag.Name),
- MetricsPort: ctx.GlobalInt(flags.MetricsPortFlag.Name),
- OnchainMetricsInterval: ctx.GlobalInt64(flags.OnchainMetricsIntervalFlag.Name),
- Timeout: timeout,
- RegisterNodeAtStart: registerNodeAtStart,
- ExpirationPollIntervalSec: expirationPollIntervalSec,
- ReachabilityPollIntervalSec: reachabilityPollIntervalSec,
- EnableTestMode: testMode,
- OverrideBlockStaleMeasure: ctx.GlobalInt64(flags.OverrideBlockStaleMeasureFlag.Name),
- OverrideStoreDurationBlocks: ctx.GlobalInt64(flags.OverrideStoreDurationBlocksFlag.Name),
- QuorumIDList: ids,
- DbPath: ctx.GlobalString(flags.DbPathFlag.Name),
- PrivateBls: privateBls,
- EthClientConfig: ethClientConfig,
- EncoderConfig: kzg.ReadCLIConfig(ctx),
- LoggerConfig: *loggerConfig,
- BLSOperatorStateRetrieverAddr: ctx.GlobalString(flags.BlsOperatorStateRetrieverFlag.Name),
- EigenDAServiceManagerAddr: ctx.GlobalString(flags.EigenDAServiceManagerFlag.Name),
- PubIPProvider: ctx.GlobalString(flags.PubIPProviderFlag.Name),
- PubIPCheckInterval: pubIPCheckInterval,
- ChurnerUrl: ctx.GlobalString(flags.ChurnerUrlFlag.Name),
- DataApiUrl: ctx.GlobalString(flags.DataApiUrlFlag.Name),
- NumBatchValidators: ctx.GlobalInt(flags.NumBatchValidatorsFlag.Name),
- NumBatchDeserializationWorkers: ctx.GlobalInt(flags.NumBatchDeserializationWorkersFlag.Name),
- EnableGnarkBundleEncoding: ctx.Bool(flags.EnableGnarkBundleEncodingFlag.Name),
- ClientIPHeader: ctx.GlobalString(flags.ClientIPHeaderFlag.Name),
- UseSecureGrpc: ctx.GlobalBoolT(flags.ChurnerUseSecureGRPC.Name),
- DisableNodeInfoResources: ctx.GlobalBool(flags.DisableNodeInfoResourcesFlag.Name),
- BLSRemoteSignerUrl: ctx.GlobalString(flags.BLSRemoteSignerUrlFlag.Name),
- BLSPublicKeyHex: ctx.GlobalString(flags.BLSPublicKeyHexFlag.Name),
- BLSKeyPassword: ctx.GlobalString(flags.BlsKeyPasswordFlag.Name),
- BLSSignerTLSCertFilePath: ctx.GlobalString(flags.BLSSignerCertFileFlag.Name),
- BLSRemoteSignerEnabled: blsRemoteSignerEnabled,
- EnableV2: ctx.GlobalBool(flags.EnableV2Flag.Name),
- OnchainStateRefreshInterval: ctx.GlobalDuration(flags.OnchainStateRefreshIntervalFlag.Name),
- ChunkDownloadTimeout: ctx.GlobalDuration(flags.ChunkDownloadTimeoutFlag.Name),
- PprofHttpPort: ctx.GlobalString(flags.PprofHttpPort.Name),
- EnablePprof: ctx.GlobalBool(flags.EnablePprof.Name),
+ Hostname: ctx.GlobalString(flags.HostnameFlag.Name),
+ DispersalPort: ctx.GlobalString(flags.DispersalPortFlag.Name),
+ RetrievalPort: ctx.GlobalString(flags.RetrievalPortFlag.Name),
+ InternalDispersalPort: internalDispersalFlag,
+ InternalRetrievalPort: internalRetrievalFlag,
+ EnableNodeApi: ctx.GlobalBool(flags.EnableNodeApiFlag.Name),
+ NodeApiPort: ctx.GlobalString(flags.NodeApiPortFlag.Name),
+ EnableMetrics: ctx.GlobalBool(flags.EnableMetricsFlag.Name),
+ MetricsPort: ctx.GlobalInt(flags.MetricsPortFlag.Name),
+ OnchainMetricsInterval: ctx.GlobalInt64(flags.OnchainMetricsIntervalFlag.Name),
+ Timeout: timeout,
+ RegisterNodeAtStart: registerNodeAtStart,
+ ExpirationPollIntervalSec: expirationPollIntervalSec,
+ ReachabilityPollIntervalSec: reachabilityPollIntervalSec,
+ EnableTestMode: testMode,
+ OverrideBlockStaleMeasure: ctx.GlobalInt64(flags.OverrideBlockStaleMeasureFlag.Name),
+ OverrideStoreDurationBlocks: ctx.GlobalInt64(flags.OverrideStoreDurationBlocksFlag.Name),
+ QuorumIDList: ids,
+ DbPath: ctx.GlobalString(flags.DbPathFlag.Name),
+ PrivateBls: privateBls,
+ EthClientConfig: ethClientConfig,
+ EncoderConfig: kzg.ReadCLIConfig(ctx),
+ LoggerConfig: *loggerConfig,
+ BLSOperatorStateRetrieverAddr: ctx.GlobalString(flags.BlsOperatorStateRetrieverFlag.Name),
+ EigenDAServiceManagerAddr: ctx.GlobalString(flags.EigenDAServiceManagerFlag.Name),
+ PubIPProvider: ctx.GlobalString(flags.PubIPProviderFlag.Name),
+ PubIPCheckInterval: pubIPCheckInterval,
+ ChurnerUrl: ctx.GlobalString(flags.ChurnerUrlFlag.Name),
+ DataApiUrl: ctx.GlobalString(flags.DataApiUrlFlag.Name),
+ NumBatchValidators: ctx.GlobalInt(flags.NumBatchValidatorsFlag.Name),
+ NumBatchDeserializationWorkers: ctx.GlobalInt(flags.NumBatchDeserializationWorkersFlag.Name),
+ EnableGnarkBundleEncoding: ctx.Bool(flags.EnableGnarkBundleEncodingFlag.Name),
+ ClientIPHeader: ctx.GlobalString(flags.ClientIPHeaderFlag.Name),
+ UseSecureGrpc: ctx.GlobalBoolT(flags.ChurnerUseSecureGRPC.Name),
+ DisableNodeInfoResources: ctx.GlobalBool(flags.DisableNodeInfoResourcesFlag.Name),
+ BLSRemoteSignerUrl: ctx.GlobalString(flags.BLSRemoteSignerUrlFlag.Name),
+ BLSPublicKeyHex: ctx.GlobalString(flags.BLSPublicKeyHexFlag.Name),
+ BLSKeyPassword: ctx.GlobalString(flags.BlsKeyPasswordFlag.Name),
+ BLSSignerTLSCertFilePath: ctx.GlobalString(flags.BLSSignerCertFileFlag.Name),
+ BLSRemoteSignerEnabled: blsRemoteSignerEnabled,
+ EnableV2: ctx.GlobalBool(flags.EnableV2Flag.Name),
+ OnchainStateRefreshInterval: ctx.GlobalDuration(flags.OnchainStateRefreshIntervalFlag.Name),
+ ChunkDownloadTimeout: ctx.GlobalDuration(flags.ChunkDownloadTimeoutFlag.Name),
+ PprofHttpPort: ctx.GlobalString(flags.PprofHttpPort.Name),
+ EnablePprof: ctx.GlobalBool(flags.EnablePprof.Name),
+ DisableDispersalAuthentication: ctx.GlobalBool(flags.DisableDispersalAuthenticationFlag.Name),
+ DispersalAuthenticationKeyCacheSize: ctx.GlobalInt(flags.DispersalAuthenticationKeyCacheSizeFlag.Name),
+ DisperserKeyTimeout: ctx.GlobalDuration(flags.DisperserKeyTimeoutFlag.Name),
+ DispersalAuthenticationTimeout: ctx.GlobalDuration(flags.DispersalAuthenticationTimeoutFlag.Name),
}, nil
}
diff --git a/node/flags/flags.go b/node/flags/flags.go
index 79e1e7feb6..0494b3a4fd 100644
--- a/node/flags/flags.go
+++ b/node/flags/flags.go
@@ -238,6 +238,33 @@ var (
EnvVar: common.PrefixEnvVar(EnvVarPrefix, "CHUNK_DOWNLOAD_TIMEOUT"),
Value: 20 * time.Second,
}
+ DisableDispersalAuthenticationFlag = cli.BoolFlag{
+ Name: common.PrefixFlag(FlagPrefix, "disable-dispersal-authentication"),
+ Usage: "Disable authentication for StoreChunks() calls from the disperser",
+ Required: false,
+ EnvVar: common.PrefixEnvVar(EnvVarPrefix, "DISABLE_DISPERSAL_AUTHENTICATION"),
+ }
+ DispersalAuthenticationKeyCacheSizeFlag = cli.IntFlag{
+ Name: common.PrefixFlag(FlagPrefix, "dispersal-authentication-key-cache-size"),
+ Usage: "The size of the dispersal authentication key cache",
+ Required: false,
+ EnvVar: common.PrefixEnvVar(EnvVarPrefix, "DISPERSAL_AUTHENTICATION_KEY_CACHE_SIZE"),
+ Value: 1024,
+ }
+ DisperserKeyTimeoutFlag = cli.DurationFlag{
+ Name: common.PrefixFlag(FlagPrefix, "disperser-key-timeout"),
+ Usage: "The duration for which a disperser key is cached",
+ Required: false,
+ EnvVar: common.PrefixEnvVar(EnvVarPrefix, "DISPERSER_KEY_TIMEOUT"),
+ Value: 1 * time.Hour,
+ }
+ DispersalAuthenticationTimeoutFlag = cli.DurationFlag{
+ Name: common.PrefixFlag(FlagPrefix, "dispersal-authentication-timeout"),
+ Usage: "The duration for which a disperser authentication is valid",
+ Required: false,
+ EnvVar: common.PrefixEnvVar(EnvVarPrefix, "DISPERSAL_AUTHENTICATION_TIMEOUT"),
+ Value: time.Minute,
+ }
// Test only, DO NOT USE the following flags in production
@@ -384,6 +411,10 @@ var optionalFlags = []cli.Flag{
ChunkDownloadTimeoutFlag,
PprofHttpPort,
EnablePprof,
+ DisableDispersalAuthenticationFlag,
+ DispersalAuthenticationKeyCacheSizeFlag,
+ DisperserKeyTimeoutFlag,
+ DispersalAuthenticationTimeoutFlag,
}
func init() {
diff --git a/node/grpc/server_test.go b/node/grpc/server_test.go
index 825876192e..b608e91c2b 100644
--- a/node/grpc/server_test.go
+++ b/node/grpc/server_test.go
@@ -78,13 +78,14 @@ func makeTestComponents() (encoding.Prover, encoding.Verifier, error) {
func makeConfig(t *testing.T) *node.Config {
return &node.Config{
- Timeout: 10 * time.Second,
- ExpirationPollIntervalSec: 1,
- QuorumIDList: []core.QuorumID{0},
- DbPath: t.TempDir(),
- ID: opID,
- NumBatchValidators: runtime.GOMAXPROCS(0),
- EnableV2: false,
+ Timeout: 10 * time.Second,
+ ExpirationPollIntervalSec: 1,
+ QuorumIDList: []core.QuorumID{0},
+ DbPath: t.TempDir(),
+ ID: opID,
+ NumBatchValidators: runtime.GOMAXPROCS(0),
+ EnableV2: false,
+ DisableDispersalAuthentication: true,
}
}
diff --git a/node/grpc/server_v2.go b/node/grpc/server_v2.go
index 9e7f3508f4..acbc057f9e 100644
--- a/node/grpc/server_v2.go
+++ b/node/grpc/server_v2.go
@@ -3,10 +3,8 @@ package grpc
import (
"context"
"encoding/hex"
+ "errors"
"fmt"
- "runtime"
- "time"
-
"github.com/Layr-Labs/eigenda/api"
pb "github.com/Layr-Labs/eigenda/api/grpc/node/v2"
"github.com/Layr-Labs/eigenda/common"
@@ -14,9 +12,13 @@ import (
"github.com/Layr-Labs/eigenda/core"
corev2 "github.com/Layr-Labs/eigenda/core/v2"
"github.com/Layr-Labs/eigenda/node"
+ "github.com/Layr-Labs/eigenda/node/auth"
"github.com/Layr-Labs/eigensdk-go/logging"
"github.com/prometheus/client_golang/prometheus"
"github.com/shirou/gopsutil/mem"
+ "google.golang.org/grpc/peer"
+ "runtime"
+ "time"
)
// ServerV2 implements the Node v2 proto APIs.
@@ -24,32 +26,53 @@ type ServerV2 struct {
pb.UnimplementedDispersalServer
pb.UnimplementedRetrievalServer
- config *node.Config
- node *node.Node
- ratelimiter common.RateLimiter
- logger logging.Logger
- metrics *MetricsV2
+ config *node.Config
+ node *node.Node
+ ratelimiter common.RateLimiter
+ logger logging.Logger
+ metrics *MetricsV2
+ authenticator auth.RequestAuthenticator
}
// NewServerV2 creates a new Server instance with the provided parameters.
func NewServerV2(
+ ctx context.Context,
config *node.Config,
node *node.Node,
logger logging.Logger,
ratelimiter common.RateLimiter,
- registry *prometheus.Registry) (*ServerV2, error) {
+ registry *prometheus.Registry,
+ reader core.Reader) (*ServerV2, error) {
metrics, err := NewV2Metrics(logger, registry)
if err != nil {
return nil, err
}
+ var authenticator auth.RequestAuthenticator
+ if !config.DisableDispersalAuthentication {
+ authenticator, err = auth.NewRequestAuthenticator(
+ ctx,
+ reader,
+ config.DispersalAuthenticationKeyCacheSize,
+ config.DisperserKeyTimeout,
+ config.DispersalAuthenticationTimeout,
+ func(id uint32) bool {
+ return id == api.EigenLabsDisperserID
+ },
+ time.Now())
+ if err != nil {
+ return nil, fmt.Errorf("failed to create authenticator: %w", err)
+ }
+ }
+
return &ServerV2{
- config: config,
- node: node,
- ratelimiter: ratelimiter,
- logger: logger,
- metrics: metrics,
+ config: config,
+ node: node,
+ ratelimiter: ratelimiter,
+ logger: logger,
+ metrics: metrics,
+ authenticator: authenticator,
}, nil
}
@@ -74,6 +97,19 @@ func (s *ServerV2) StoreChunks(ctx context.Context, in *pb.StoreChunksRequest) (
return nil, api.NewErrorInvalidArg("v2 API is disabled")
}
+ if s.authenticator != nil {
+ disperserPeer, ok := peer.FromContext(ctx)
+ if !ok {
+ return nil, errors.New("could not get peer information")
+ }
+ disperserAddress := disperserPeer.Addr.String()
+
+ err := s.authenticator.AuthenticateStoreChunksRequest(ctx, disperserAddress, in, time.Now())
+ if err != nil {
+ return nil, fmt.Errorf("failed to authenticate request: %w", err)
+ }
+ }
+
if s.node.StoreV2 == nil {
return nil, api.NewErrorInternal("v2 store not initialized")
}
diff --git a/node/grpc/server_v2_test.go b/node/grpc/server_v2_test.go
index d3058319de..b1784a7a67 100644
--- a/node/grpc/server_v2_test.go
+++ b/node/grpc/server_v2_test.go
@@ -3,6 +3,7 @@ package grpc_test
import (
"context"
"errors"
+ coreeth "github.com/Layr-Labs/eigenda/core/eth"
"os"
"sync/atomic"
"testing"
@@ -82,7 +83,19 @@ func newTestComponents(t *testing.T, config *node.Config) *testComponents {
RelayClient: atomicRelayClient,
}
node.BlobVersionParams.Store(v2.NewBlobVersionParameterMap(blobParamsMap))
- server, err := grpc.NewServerV2(config, node, logger, ratelimiter, prometheus.NewRegistry())
+
+ // The eth client is only utilized for StoreChunks validation, which is disabled in these tests
+ var reader *coreeth.Reader
+
+ server, err := grpc.NewServerV2(
+ context.Background(),
+ config,
+ node,
+ logger,
+ ratelimiter,
+ prometheus.NewRegistry(),
+ reader)
+
require.NoError(t, err)
return &testComponents{
server: server,
diff --git a/node/node.go b/node/node.go
index 6ae337e4b0..b7bfaed516 100644
--- a/node/node.go
+++ b/node/node.go
@@ -42,7 +42,6 @@ import (
v2 "github.com/Layr-Labs/eigenda/core/v2"
"github.com/Layr-Labs/eigensdk-go/logging"
"github.com/Layr-Labs/eigensdk-go/metrics"
- rpccalls "github.com/Layr-Labs/eigensdk-go/metrics/collectors/rpc_calls"
"github.com/Layr-Labs/eigensdk-go/nodeapi"
"github.com/gammazero/workerpool"
@@ -94,6 +93,7 @@ func NewNode(
reg *prometheus.Registry,
config *Config,
pubIPProvider pubip.Provider,
+ client *geth.InstrumentedEthClient,
logger logging.Logger,
) (*Node, error) {
// Setup metrics
@@ -105,7 +105,6 @@ func NewNode(
nodeLogger := logger.With("component", "Node")
eigenMetrics := metrics.NewEigenMetrics(AppName, fmt.Sprintf(":%d", config.MetricsPort), reg, logger.With("component", "EigenMetrics"))
- rpcCallsCollector := rpccalls.NewCollector(AppName, reg)
// Make sure config folder exists.
err := os.MkdirAll(config.DbPath, os.ModePerm)
@@ -113,11 +112,6 @@ func NewNode(
return nil, fmt.Errorf("could not create db directory at %s: %w", config.DbPath, err)
}
- client, err := geth.NewInstrumentedEthClient(config.EthClientConfig, rpcCallsCollector, logger)
- if err != nil {
- return nil, fmt.Errorf("cannot create chain.Client: %w", err)
- }
-
chainID, err := client.ChainID(context.Background())
if err != nil {
return nil, fmt.Errorf("failed to get chainID: %w", err)
diff --git a/relay/auth/authenticator.go b/relay/auth/authenticator.go
index 664979d366..caf611b165 100644
--- a/relay/auth/authenticator.go
+++ b/relay/auth/authenticator.go
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
+ "github.com/Layr-Labs/eigenda/api/hashing"
"sync"
"time"
@@ -129,7 +130,7 @@ func (a *requestAuthenticator) AuthenticateGetChunksRequest(
G1Point: g1Point,
}
- hash := HashGetChunksRequest(request)
+ hash := hashing.HashGetChunksRequest(request)
isValid := signature.Verify(key, ([32]byte)(hash))
if !isValid {
diff --git a/relay/auth/request_signing.go b/relay/auth/request_signing.go
index 149992bce2..f08666122e 100644
--- a/relay/auth/request_signing.go
+++ b/relay/auth/request_signing.go
@@ -1,58 +1,15 @@
package auth
import (
- "encoding/binary"
pb "github.com/Layr-Labs/eigenda/api/grpc/relay"
+ "github.com/Layr-Labs/eigenda/api/hashing"
"github.com/Layr-Labs/eigenda/core"
- "golang.org/x/crypto/sha3"
)
-var (
- iByte = []byte{0x69}
- rByte = []byte{0x72}
-)
-
-// HashGetChunksRequest hashes the given GetChunksRequest.
-func HashGetChunksRequest(request *pb.GetChunksRequest) []byte {
-
- // Protobuf serialization is non-deterministic, so we can't just hash the
- // serialized bytes. Instead, we have to define our own hashing function.
-
- hasher := sha3.NewLegacyKeccak256()
-
- hasher.Write(request.GetOperatorId())
- for _, chunkRequest := range request.GetChunkRequests() {
- if chunkRequest.GetByIndex() != nil {
- getByIndex := chunkRequest.GetByIndex()
- hasher.Write(iByte)
- hasher.Write(getByIndex.BlobKey)
- for _, index := range getByIndex.ChunkIndices {
- indexBytes := make([]byte, 4)
- binary.BigEndian.PutUint32(indexBytes, index)
- hasher.Write(indexBytes)
- }
- } else {
- getByRange := chunkRequest.GetByRange()
- hasher.Write(rByte)
- hasher.Write(getByRange.BlobKey)
-
- startBytes := make([]byte, 4)
- binary.BigEndian.PutUint32(startBytes, getByRange.StartIndex)
- hasher.Write(startBytes)
-
- endBytes := make([]byte, 4)
- binary.BigEndian.PutUint32(endBytes, getByRange.EndIndex)
- hasher.Write(endBytes)
- }
- }
-
- return hasher.Sum(nil)
-}
-
// SignGetChunksRequest signs the given GetChunksRequest with the given private key. Does not
// write the signature into the request.
func SignGetChunksRequest(keys *core.KeyPair, request *pb.GetChunksRequest) []byte {
- hash := HashGetChunksRequest(request)
+ hash := hashing.HashGetChunksRequest(request)
signature := keys.SignMessage(([32]byte)(hash))
return signature.G1Point.Serialize()
}
diff --git a/relay/auth/request_signing_test.go b/relay/auth/request_signing_test.go
index 3c05188514..968052cf12 100644
--- a/relay/auth/request_signing_test.go
+++ b/relay/auth/request_signing_test.go
@@ -2,6 +2,7 @@ package auth
import (
pb "github.com/Layr-Labs/eigenda/api/grpc/relay"
+ "github.com/Layr-Labs/eigenda/api/hashing"
tu "github.com/Layr-Labs/eigenda/common/testutils"
"github.com/stretchr/testify/require"
"golang.org/x/exp/rand"
@@ -51,21 +52,21 @@ func TestHashGetChunksRequest(t *testing.T) {
requestB := randomGetChunksRequest()
// Hashing the same request twice should yield the same hash
- hashA := HashGetChunksRequest(requestA)
- hashAA := HashGetChunksRequest(requestA)
+ hashA := hashing.HashGetChunksRequest(requestA)
+ hashAA := hashing.HashGetChunksRequest(requestA)
require.Equal(t, hashA, hashAA)
// Hashing different requests should yield different hashes
- hashB := HashGetChunksRequest(requestB)
+ hashB := hashing.HashGetChunksRequest(requestB)
require.NotEqual(t, hashA, hashB)
// Adding a signature should not affect the hash
requestA.OperatorSignature = tu.RandomBytes(32)
- hashAA = HashGetChunksRequest(requestA)
+ hashAA = hashing.HashGetChunksRequest(requestA)
require.Equal(t, hashA, hashAA)
// Changing the requester ID should change the hash
requestA.OperatorId = tu.RandomBytes(32)
- hashAA = HashGetChunksRequest(requestA)
+ hashAA = hashing.HashGetChunksRequest(requestA)
require.NotEqual(t, hashA, hashAA)
}
diff --git a/relay/cache/cache_accessor.go b/relay/cache/cache_accessor.go
index c9ad42611a..971eb9eb99 100644
--- a/relay/cache/cache_accessor.go
+++ b/relay/cache/cache_accessor.go
@@ -123,7 +123,13 @@ func (c *cacheAccessor[K, V]) Get(ctx context.Context, key K) (V, error) {
c.cacheLock.Unlock()
if c.metrics != nil {
- c.metrics.ReportCacheMiss()
+ if alreadyLoading {
+ // A lookup is currently in progress. Not a cache hit, but this call won't duplicate the work.
+ c.metrics.ReportCacheNearMiss()
+ } else {
+ // The data is not in the cache and no lookup is in progress. We must fetch the data from the source.
+ c.metrics.ReportCacheMiss()
+ }
}
if alreadyLoading {
diff --git a/relay/cache/cache_accessor_metrics.go b/relay/cache/cache_accessor_metrics.go
index c873f25670..61260fe12d 100644
--- a/relay/cache/cache_accessor_metrics.go
+++ b/relay/cache/cache_accessor_metrics.go
@@ -13,6 +13,7 @@ const namespace = "eigenda_relay"
// CacheAccessorMetrics provides metrics for a CacheAccessor.
type CacheAccessorMetrics struct {
cacheHits *prometheus.CounterVec
+ cacheNearMisses *prometheus.CounterVec
cacheMisses *prometheus.CounterVec
size *prometheus.GaugeVec
weight *prometheus.GaugeVec
@@ -34,6 +35,15 @@ func NewCacheAccessorMetrics(
[]string{},
)
+ cacheNearMisses := promauto.With(registry).NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: namespace,
+ Name: fmt.Sprintf("%s_cache_near_miss_count", cacheName),
+ Help: "Number of near cache misses (i.e. a lookup is already in progress)",
+ },
+ []string{},
+ )
+
cacheMisses := promauto.With(registry).NewCounterVec(
prometheus.CounterOpts{
Namespace: namespace,
@@ -82,6 +92,7 @@ func NewCacheAccessorMetrics(
return &CacheAccessorMetrics{
cacheHits: cacheHits,
+ cacheNearMisses: cacheNearMisses,
cacheMisses: cacheMisses,
size: size,
weight: weight,
@@ -94,6 +105,10 @@ func (m *CacheAccessorMetrics) ReportCacheHit() {
m.cacheHits.WithLabelValues().Inc()
}
+func (m *CacheAccessorMetrics) ReportCacheNearMiss() {
+ m.cacheNearMisses.WithLabelValues().Inc()
+}
+
func (m *CacheAccessorMetrics) ReportCacheMiss() {
m.cacheMisses.WithLabelValues().Inc()
}
diff --git a/relay/chunk_provider.go b/relay/chunk_provider.go
index 2f9a33ead7..3009310b71 100644
--- a/relay/chunk_provider.go
+++ b/relay/chunk_provider.go
@@ -4,14 +4,15 @@ import (
"bytes"
"context"
"fmt"
+ "sync"
+ "time"
+
v2 "github.com/Layr-Labs/eigenda/core/v2"
"github.com/Layr-Labs/eigenda/encoding"
"github.com/Layr-Labs/eigenda/encoding/rs"
"github.com/Layr-Labs/eigenda/relay/cache"
"github.com/Layr-Labs/eigenda/relay/chunkstore"
"github.com/Layr-Labs/eigensdk-go/logging"
- "sync"
- "time"
)
type chunkProvider struct {
@@ -110,7 +111,7 @@ func (s *chunkProvider) GetFrames(ctx context.Context, mMap metadataMap) (frameM
go func() {
frames, err := s.frameCache.Get(ctx, *boundKey)
if err != nil {
- s.logger.Errorf("Failed to get frames for blob %v: %v", boundKey.blobKey, err)
+ s.logger.Errorf("Failed to get frames for blob %v: %v", boundKey.blobKey.Hex(), err)
completionChannel <- &framesResult{
key: boundKey.blobKey,
err: err,
@@ -129,7 +130,7 @@ func (s *chunkProvider) GetFrames(ctx context.Context, mMap metadataMap) (frameM
for len(fMap) < len(keys) {
result := <-completionChannel
if result.err != nil {
- return nil, fmt.Errorf("error fetching frames for blob %v: %w", result.key, result.err)
+ return nil, fmt.Errorf("error fetching frames for blob %v: %w", result.key.Hex(), result.err)
}
fMap[result.key] = result.data
}
diff --git a/relay/cmd/main.go b/relay/cmd/main.go
index bffdd9fa9e..9f12813a47 100644
--- a/relay/cmd/main.go
+++ b/relay/cmd/main.go
@@ -54,7 +54,6 @@ func RunRelay(ctx *cli.Context) error {
if err != nil {
return fmt.Errorf("failed to create logger: %w", err)
}
- logger.Info(fmt.Sprintf("Relay configuration: %#v", config))
dynamoClient, err := dynamodb.NewClient(config.AWS, logger)
if err != nil {
diff --git a/relay/server.go b/relay/server.go
index 117127de15..47f1c488ff 100644
--- a/relay/server.go
+++ b/relay/server.go
@@ -523,7 +523,7 @@ func (s *Server) RefreshOnchainState(ctx context.Context) error {
// Stop stops the server.
func (s *Server) Stop() error {
if s.grpcServer != nil {
- s.grpcServer.Stop()
+ s.grpcServer.GracefulStop()
}
err := s.metrics.Stop()
diff --git a/test/integration_test.go b/test/integration_test.go
index e3c390e56d..6c93cd3821 100644
--- a/test/integration_test.go
+++ b/test/integration_test.go
@@ -31,6 +31,7 @@ import (
clientsmock "github.com/Layr-Labs/eigenda/api/clients/mock"
commonaws "github.com/Layr-Labs/eigenda/common/aws"
"github.com/Layr-Labs/eigenda/core/meterer"
+ coremock "github.com/Layr-Labs/eigenda/core/mock"
"github.com/Layr-Labs/eigenda/disperser/apiserver"
dispatcher "github.com/Layr-Labs/eigenda/disperser/batcher/grpc"
"github.com/Layr-Labs/eigenda/disperser/encoder"
@@ -45,7 +46,6 @@ import (
"github.com/Layr-Labs/eigenda/common"
commonmock "github.com/Layr-Labs/eigenda/common/mock"
"github.com/Layr-Labs/eigenda/core"
- coremock "github.com/Layr-Labs/eigenda/core/mock"
"github.com/Layr-Labs/eigenda/disperser"
"github.com/Layr-Labs/eigenda/disperser/batcher"
batchermock "github.com/Layr-Labs/eigenda/disperser/batcher/mock"
@@ -351,19 +351,21 @@ func mustMakeOperators(t *testing.T, cst *coremock.ChainDataMock, logger logging
}
config := &node.Config{
- Hostname: op.Host,
- DispersalPort: op.DispersalPort,
- RetrievalPort: op.RetrievalPort,
- InternalRetrievalPort: op.RetrievalPort,
- InternalDispersalPort: op.DispersalPort,
- EnableMetrics: false,
- Timeout: 10,
- ExpirationPollIntervalSec: 10,
- DbPath: dbPath,
- LogPath: logPath,
- PrivateBls: string(op.KeyPair.GetPubKeyG1().Serialize()),
- ID: id,
- QuorumIDList: registeredQuorums,
+ Hostname: op.Host,
+ DispersalPort: op.DispersalPort,
+ RetrievalPort: op.RetrievalPort,
+ InternalRetrievalPort: op.RetrievalPort,
+ InternalDispersalPort: op.DispersalPort,
+ EnableMetrics: false,
+ Timeout: 10,
+ ExpirationPollIntervalSec: 10,
+ DbPath: dbPath,
+ LogPath: logPath,
+ PrivateBls: string(op.KeyPair.GetPubKeyG1().Serialize()),
+ ID: id,
+ QuorumIDList: registeredQuorums,
+ DispersalAuthenticationKeyCacheSize: 1024,
+ DisableDispersalAuthentication: false,
}
// creating a new instance of encoder instead of sharing enc because enc is not thread safe
@@ -417,10 +419,23 @@ func mustMakeOperators(t *testing.T, cst *coremock.ChainDataMock, logger logging
OperatorSocketsFilterer: mockOperatorSocketsFilterer,
}
- ratelimiter := &commonmock.NoopRatelimiter{}
-
- serverV1 := nodegrpc.NewServer(config, n, logger, ratelimiter)
- serverV2, err := nodegrpc.NewServerV2(config, n, logger, ratelimiter, prometheus.NewRegistry())
+ rateLimiter := &commonmock.NoopRatelimiter{}
+
+ // TODO(cody-littley): Once we switch this test to use the v2 disperser, we will need to properly set up
+ // the disperser's public/private keys for signing StoreChunks() requests
+ disperserAddress := gethcommon.Address{}
+ reader := &coremock.MockWriter{}
+ reader.On("GetDisperserAddress", uint32(0)).Return(disperserAddress, nil)
+
+ serverV1 := nodegrpc.NewServer(config, n, logger, rateLimiter)
+ serverV2, err := nodegrpc.NewServerV2(
+ context.Background(),
+ config,
+ n,
+ logger,
+ rateLimiter,
+ prometheus.NewRegistry(),
+ reader)
require.NoError(t, err)
ops[id] = TestOperator{