From 4a23f4331e450ab80cbfae3be04fd805d48a5b8a Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 4 Feb 2025 09:09:49 -0600 Subject: [PATCH 1/3] tweaks Signed-off-by: Cody Littley --- common/testutils/random/test_random.go | 13 +++++++++++++ test/v2/v2_test.go | 21 +++++++-------------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/common/testutils/random/test_random.go b/common/testutils/random/test_random.go index 39a25c0b9a..8b91f02b7d 100644 --- a/common/testutils/random/test_random.go +++ b/common/testutils/random/test_random.go @@ -66,6 +66,12 @@ func (r *TestRandom) Bytes(length int) []byte { return bytes } +// VariableBytes generates a random byte slice of a length between min (inclusive) and max (exclusive). +func (r *TestRandom) VariableBytes(min int, max int) []byte { + length := r.Intn(max-min) + min + return r.Bytes(length) +} + // Time generates a random time. func (r *TestRandom) Time() time.Time { return time.Unix(r.Int63(), r.Int63()) @@ -80,6 +86,13 @@ func (r *TestRandom) String(length int) string { return string(b) } +// VariableString generates a random string out of printable ASCII characters of a length between +// min (inclusive) and max (exclusive). +func (r *TestRandom) VariableString(min int, max int) string { + length := r.Intn(max-min) + min + return r.String(length) +} + // Uint32n generates a random uint32 less than n. func (r *TestRandom) Uint32n(n uint32) uint32 { return r.Uint32() % n diff --git a/test/v2/v2_test.go b/test/v2/v2_test.go index 473a5671d3..a82e367ea8 100644 --- a/test/v2/v2_test.go +++ b/test/v2/v2_test.go @@ -26,7 +26,7 @@ var ( EthRPCURLs: []string{"https://ethereum-holesky-rpc.publicnode.com"}, BLSOperatorStateRetrieverAddr: "0x93545e3b9013CcaBc31E80898fef7569a4024C0C", EigenDAServiceManagerAddr: "0x54A03db2784E3D0aCC08344D05385d0b62d4F432", - EigenDACertVerifierAddress: "0x5c33Ce64EE04400fD593F960d63336F1B65bF77B", + EigenDACertVerifierAddress: "0xe2C7AfB3c47B800b439b0a3d8EA40ca79759B245", SubgraphURL: "https://subgraph.satsuma-prod.com/51caed8fa9cb/eigenlabs/eigenda-operator-state-preprod-holesky/version/v0.7.0/api", SRSOrder: 268435456, SRSNumberToLoad: 2097152, @@ -203,8 +203,7 @@ func TestPaddingError(t *testing.T) { // Disperse a small payload (between 1KB and 2KB). func TestSmallBlobDispersal(t *testing.T) { rand := random.NewTestRandom(t) - dataLength := 1024 + rand.Intn(1024) - payload := rand.Bytes(dataLength) + payload := rand.VariableBytes(units.KiB, 2*units.KiB) paddedPayload := codec.ConvertByPaddingEmptyByte(payload) err := testBasicDispersal(t, rand, paddedPayload, []core.QuorumID{0, 1}) require.NoError(t, err) @@ -213,8 +212,7 @@ func TestSmallBlobDispersal(t *testing.T) { // Disperse a medium payload (between 100KB and 200KB). func TestMediumBlobDispersal(t *testing.T) { rand := random.NewTestRandom(t) - dataLength := 1024 * (100 + rand.Intn(100)) - payload := rand.Bytes(dataLength) + payload := rand.VariableBytes(100*units.KiB, 200*units.KiB) paddedPayload := codec.ConvertByPaddingEmptyByte(payload) err := testBasicDispersal(t, rand, paddedPayload, []core.QuorumID{0, 1}) require.NoError(t, err) @@ -233,8 +231,7 @@ func TestLargeBlobDispersal(t *testing.T) { // Disperse a small payload (between 1KB and 2KB) with a single quorum func TestSmallBlobDispersalSingleQuorum(t *testing.T) { rand := random.NewTestRandom(t) - desiredDataLength := 1024 + rand.Intn(1024) - payload := rand.Bytes(desiredDataLength) + payload := rand.VariableBytes(units.KiB, 2*units.KiB) paddedPayload := codec.ConvertByPaddingEmptyByte(payload) err := testBasicDispersal(t, rand, paddedPayload, []core.QuorumID{0}) require.NoError(t, err) @@ -267,8 +264,7 @@ func TestDoubleDispersal(t *testing.T) { rand := random.NewTestRandom(t) c := getClient(t) - dataLength := 1024 + rand.Intn(1024) - payload := rand.Bytes(dataLength) + payload := rand.VariableBytes(units.KiB, 2*units.KiB) paddedPayload := codec.ConvertByPaddingEmptyByte(payload) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) @@ -285,13 +281,10 @@ func TestDoubleDispersal(t *testing.T) { } func TestUnauthorizedGetChunks(t *testing.T) { - t.Skip("this test is not working due to a bug") - rand := random.NewTestRandom(t) c := getClient(t) - dataLength := 1024 + rand.Intn(1024) - payload := rand.Bytes(dataLength) + payload := rand.VariableBytes(units.KiB, 2*units.KiB) paddedPayload := codec.ConvertByPaddingEmptyByte(payload) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) @@ -313,5 +306,5 @@ func TestUnauthorizedGetChunks(t *testing.T) { } _, err = c.RelayClient.GetChunksByRange(ctx, targetRelay, chunkRequests) require.Error(t, err) - // TODO (cody-littley) once this is properly returning an error, validate the error message + require.Contains(t, err.Error(), "failed to get operator key: operator not found") } From cae275a4e39bcfe997b50355addb6d27792f8224 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 4 Feb 2025 10:21:03 -0600 Subject: [PATCH 2/3] Cleanup, additional test case Signed-off-by: Cody Littley --- test/v2/test_client.go | 58 +++++++-------- test/v2/test_setup.go | 131 ++++++++++++++++++++++++++++++++++ test/v2/v2_test.go | 158 +++++++++-------------------------------- 3 files changed, 196 insertions(+), 151 deletions(-) create mode 100644 test/v2/test_setup.go diff --git a/test/v2/test_client.go b/test/v2/test_client.go index 848927bcb4..16ef3932e7 100644 --- a/test/v2/test_client.go +++ b/test/v2/test_client.go @@ -40,14 +40,15 @@ const ( // TestClient encapsulates the various clients necessary for interacting with EigenDA. type TestClient struct { - t *testing.T - config *TestClientConfig - logger logging.Logger + T *testing.T + Config *TestClientConfig + Logger logging.Logger DisperserClient clients.DisperserClient RelayClient clients.RelayClient indexedChainState core.IndexedChainState RetrievalClient clients.RetrievalClient CertVerifier *verification.CertVerifier + PrivateKey string } type TestClientConfig struct { @@ -199,14 +200,15 @@ func NewTestClient(t *testing.T, config *TestClientConfig) *TestClient { require.NoError(t, err) return &TestClient{ - t: t, - config: config, - logger: logger, + T: t, + Config: config, + Logger: logger, DisperserClient: disperserClient, RelayClient: relayClient, indexedChainState: indexedChainState, RetrievalClient: retrievalClient, CertVerifier: certVerifier, + PrivateKey: privateKeyString, } } @@ -265,7 +267,7 @@ func (c *TestClient) WaitForCertification( select { case <-ticker.C: reply, err := c.DisperserClient.GetBlobStatus(ctx, key) - require.NoError(c.t, err) + require.NoError(c.T, err) if reply.Status == v2.BlobStatus_COMPLETE { elapsed := time.Since(statusStart) @@ -297,13 +299,13 @@ func (c *TestClient) WaitForCertification( if reply.Status == v2.BlobStatus_FAILED || reply.Status == v2.BlobStatus_UNKNOWN { require.Fail( - c.t, + c.T, "Blob status is in a terminal non-successful state.", reply.Status.String()) } } case <-ctx.Done(): - require.Fail(c.t, "Timed out waiting for blob to be confirmed") + require.Fail(c.T, "Timed out waiting for blob to be confirmed") } } } @@ -316,15 +318,15 @@ func (c *TestClient) VerifyBlobCertification( inclusionInfo *v2.BlobInclusionInfo) { blobCert := inclusionInfo.BlobCertificate - require.NotNil(c.t, blobCert) - require.True(c.t, len(blobCert.RelayKeys) >= 1) + require.NotNil(c.T, blobCert) + require.True(c.T, len(blobCert.RelayKeys) >= 1) // make sure the returned header hash matches the expected blob key bh, err := corev2.BlobHeaderFromProtobuf(blobCert.BlobHeader) - require.NoError(c.t, err) + require.NoError(c.T, err) computedBlobKey, err := bh.BlobKey() - require.NoError(c.t, err) - require.Equal(c.t, key, computedBlobKey) + require.NoError(c.T, err) + require.Equal(c.T, key, computedBlobKey) // verify that expected quorums are present quorumSet := make(map[core.QuorumID]struct{}, len(expectedQuorums)) @@ -332,9 +334,9 @@ func (c *TestClient) VerifyBlobCertification( quorumSet[core.QuorumID(quorumNumber)] = struct{}{} } // There may be other quorums in the batch. No biggie as long as the expected ones are there. - require.True(c.t, len(expectedQuorums) <= len(quorumSet)) + require.True(c.T, len(expectedQuorums) <= len(quorumSet)) for expectedQuorum := range quorumSet { - require.Contains(c.t, quorumSet, expectedQuorum) + require.Contains(c.T, quorumSet, expectedQuorum) } // Check the signing percentages @@ -345,15 +347,15 @@ func (c *TestClient) VerifyBlobCertification( } for _, quorum := range expectedQuorums { percent, ok := signingPercents[quorum] - require.True(c.t, ok) - require.True(c.t, percent >= 0 && percent <= 100) - require.True(c.t, percent >= c.config.MinimumSigningPercent) + require.True(c.T, ok) + require.True(c.T, percent >= 0 && percent <= 100) + require.True(c.T, percent >= c.Config.MinimumSigningPercent) } // TODO This currently does not pass! // On-chain verification //err = c.CertVerifier.VerifyCertV2FromSignedBatch(context.Background(), signedBatch, inclusionInfo) - //require.NoError(c.t, err) + //require.NoError(c.T, err) } // ReadBlobFromRelay reads a blob from the relays and compares it to the given payload. @@ -366,10 +368,10 @@ func (c *TestClient) ReadBlobFromRelay( for _, relayID := range blobCert.RelayKeys { fmt.Printf("Reading blob from relay %d\n", relayID) blobFromRelay, err := c.RelayClient.GetBlob(ctx, relayID, key) - require.NoError(c.t, err) + require.NoError(c.T, err) relayPayload := codec.RemoveEmptyByteFromPaddedBytes(blobFromRelay) - require.Equal(c.t, payload, relayPayload) + require.Equal(c.T, payload, relayPayload) } } @@ -381,27 +383,27 @@ func (c *TestClient) ReadBlobFromValidators( payload []byte) { currentBlockNumber, err := c.indexedChainState.GetCurrentBlockNumber() - require.NoError(c.t, err) + require.NoError(c.T, err) for _, quorumID := range quorums { fmt.Printf("Reading blob from validators for quorum %d\n", quorumID) header, err := corev2.BlobHeaderFromProtobuf(blobCert.BlobHeader) - require.NoError(c.t, err) + require.NoError(c.T, err) retrievedBlob, err := c.RetrievalClient.GetBlob(ctx, header, uint64(currentBlockNumber), quorumID) - require.NoError(c.t, err) + require.NoError(c.T, err) retrievedPayload := codec.RemoveEmptyByteFromPaddedBytes(retrievedBlob) // The payload may have a bunch of 0s appended at the end. Remove them. - require.True(c.t, len(retrievedPayload) >= len(payload)) + require.True(c.T, len(retrievedPayload) >= len(payload)) truncatedPayload := retrievedPayload[:len(payload)] // Only 0s should be appended at the end. for i := len(payload); i < len(retrievedPayload); i++ { - require.Equal(c.t, byte(0), retrievedPayload[i]) + require.Equal(c.T, byte(0), retrievedPayload[i]) } - require.Equal(c.t, payload, truncatedPayload) + require.Equal(c.T, payload, truncatedPayload) } } diff --git a/test/v2/test_setup.go b/test/v2/test_setup.go new file mode 100644 index 0000000000..1aad34deb2 --- /dev/null +++ b/test/v2/test_setup.go @@ -0,0 +1,131 @@ +package v2 + +import ( + "fmt" + "github.com/docker/go-units" + "github.com/stretchr/testify/require" + "os" + "os/exec" + "sync" + "testing" +) + +var ( + preprodConfig = &TestClientConfig{ + TestDataPath: "~/.test-v2", + DisperserHostname: "disperser-preprod-holesky.eigenda.xyz", + DisperserPort: 443, + EthRPCURLs: []string{"https://ethereum-holesky-rpc.publicnode.com"}, + BLSOperatorStateRetrieverAddr: "0x93545e3b9013CcaBc31E80898fef7569a4024C0C", + EigenDAServiceManagerAddr: "0x54A03db2784E3D0aCC08344D05385d0b62d4F432", + EigenDACertVerifierAddress: "0xe2C7AfB3c47B800b439b0a3d8EA40ca79759B245", + SubgraphURL: "https://subgraph.satsuma-prod.com/51caed8fa9cb/eigenlabs/eigenda-operator-state-preprod-holesky/version/v0.7.0/api", + SRSOrder: 268435456, + SRSNumberToLoad: 2097152, + MaxBlobSize: 16 * units.MiB, + MinimumSigningPercent: 55, + } + + lock sync.Mutex + client *TestClient + + targetConfig = preprodConfig +) + +// getClient returns a TestClient instance, creating one if it does not exist. +// This uses a global static client... this is icky, but it takes ~1 minute +// to read the SRS points, so it's the lesser of two evils to keep it around. +func getClient(t *testing.T) *TestClient { + lock.Lock() + defer lock.Unlock() + + skipInCI(t) + setupFilesystem(t, targetConfig) + + if client == nil { + client = NewTestClient(t, targetConfig) + } + + return client +} + +func skipInCI(t *testing.T) { + if os.Getenv("CI") != "" { + t.Skip("Skipping test in CI environment") + } +} + +func setupFilesystem(t *testing.T, config *TestClientConfig) { + // Create the test data directory if it does not exist + err := os.MkdirAll(config.TestDataPath, 0755) + require.NoError(t, err) + + // Create the SRS directories if they do not exist + err = os.MkdirAll(config.path(t, SRSPath), 0755) + require.NoError(t, err) + err = os.MkdirAll(config.path(t, SRSPathSRSTables), 0755) + require.NoError(t, err) + + // If any of the srs files do not exist, download them. + filePath := config.path(t, SRSPathG1) + _, err = os.Stat(filePath) + if os.IsNotExist(err) { + command := make([]string, 3) + command[0] = "wget" + command[1] = "https://srs-mainnet.s3.amazonaws.com/kzg/g1.point" + command[2] = "--output-document=" + filePath + fmt.Printf("executing %s\n", command) + + cmd := exec.Command(command[0], command[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + require.NoError(t, err) + } else { + require.NoError(t, err) + } + + filePath = config.path(t, SRSPathG2) + _, err = os.Stat(filePath) + if os.IsNotExist(err) { + command := make([]string, 3) + command[0] = "wget" + command[1] = "https://srs-mainnet.s3.amazonaws.com/kzg/g2.point" + command[2] = "--output-document=" + filePath + fmt.Printf("executing %s\n", command) + + cmd := exec.Command(command[0], command[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + require.NoError(t, err) + } else { + require.NoError(t, err) + } + + filePath = config.path(t, SRSPathG2PowerOf2) + _, err = os.Stat(filePath) + if os.IsNotExist(err) { + command := make([]string, 3) + command[0] = "wget" + command[1] = "https://srs-mainnet.s3.amazonaws.com/kzg/g2.point.powerOf2" + command[2] = "--output-document=" + filePath + fmt.Printf("executing %s\n", command) + + cmd := exec.Command(command[0], command[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + require.NoError(t, err) + } else { + require.NoError(t, err) + } + + // Check to see if the private key file exists. If not, stop the test. + filePath = config.path(t, KeyPath) + _, err = os.Stat(filePath) + require.NoError(t, err, + "private key file %s does not exist. This file should "+ + "contain the private key for the account used in the test, in hex.", + filePath) +} diff --git a/test/v2/v2_test.go b/test/v2/v2_test.go index a82e367ea8..37829db71e 100644 --- a/test/v2/v2_test.go +++ b/test/v2/v2_test.go @@ -4,11 +4,10 @@ import ( "context" "fmt" "github.com/Layr-Labs/eigenda/api/clients/v2" + auth "github.com/Layr-Labs/eigenda/core/auth/v2" "github.com/docker/go-units" - "os" - "os/exec" + gethcommon "github.com/ethereum/go-ethereum/common" "strings" - "sync" "testing" "time" @@ -18,126 +17,6 @@ import ( "github.com/stretchr/testify/require" ) -var ( - preprodConfig = &TestClientConfig{ - TestDataPath: "~/.test-v2", - DisperserHostname: "disperser-preprod-holesky.eigenda.xyz", - DisperserPort: 443, - EthRPCURLs: []string{"https://ethereum-holesky-rpc.publicnode.com"}, - BLSOperatorStateRetrieverAddr: "0x93545e3b9013CcaBc31E80898fef7569a4024C0C", - EigenDAServiceManagerAddr: "0x54A03db2784E3D0aCC08344D05385d0b62d4F432", - EigenDACertVerifierAddress: "0xe2C7AfB3c47B800b439b0a3d8EA40ca79759B245", - SubgraphURL: "https://subgraph.satsuma-prod.com/51caed8fa9cb/eigenlabs/eigenda-operator-state-preprod-holesky/version/v0.7.0/api", - SRSOrder: 268435456, - SRSNumberToLoad: 2097152, - MaxBlobSize: 16 * units.MiB, - MinimumSigningPercent: 55, - } - - lock sync.Mutex - client *TestClient - - targetConfig = preprodConfig -) - -func setupFilesystem(t *testing.T, config *TestClientConfig) { - // Create the test data directory if it does not exist - err := os.MkdirAll(config.TestDataPath, 0755) - require.NoError(t, err) - - // Create the SRS directories if they do not exist - err = os.MkdirAll(config.path(t, SRSPath), 0755) - require.NoError(t, err) - err = os.MkdirAll(config.path(t, SRSPathSRSTables), 0755) - require.NoError(t, err) - - // If any of the srs files do not exist, download them. - filePath := config.path(t, SRSPathG1) - _, err = os.Stat(filePath) - if os.IsNotExist(err) { - command := make([]string, 3) - command[0] = "wget" - command[1] = "https://srs-mainnet.s3.amazonaws.com/kzg/g1.point" - command[2] = "--output-document=" + filePath - fmt.Printf("executing %s\n", command) - - cmd := exec.Command(command[0], command[1:]...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Run() - require.NoError(t, err) - } else { - require.NoError(t, err) - } - - filePath = config.path(t, SRSPathG2) - _, err = os.Stat(filePath) - if os.IsNotExist(err) { - command := make([]string, 3) - command[0] = "wget" - command[1] = "https://srs-mainnet.s3.amazonaws.com/kzg/g2.point" - command[2] = "--output-document=" + filePath - fmt.Printf("executing %s\n", command) - - cmd := exec.Command(command[0], command[1:]...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Run() - require.NoError(t, err) - } else { - require.NoError(t, err) - } - - filePath = config.path(t, SRSPathG2PowerOf2) - _, err = os.Stat(filePath) - if os.IsNotExist(err) { - command := make([]string, 3) - command[0] = "wget" - command[1] = "https://srs-mainnet.s3.amazonaws.com/kzg/g2.point.powerOf2" - command[2] = "--output-document=" + filePath - fmt.Printf("executing %s\n", command) - - cmd := exec.Command(command[0], command[1:]...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Run() - require.NoError(t, err) - } else { - require.NoError(t, err) - } - - // Check to see if the private key file exists. If not, stop the test. - filePath = config.path(t, KeyPath) - _, err = os.Stat(filePath) - require.NoError(t, err, - "private key file %s does not exist. This file should "+ - "contain the private key for the account used in the test, in hex.", - filePath) -} - -// getClient returns a TestClient instance, creating one if it does not exist. -// This uses a global static client... this is icky, but it takes ~1 minute -// to read the SRS points, so it's the lesser of two evils to keep it around. -func getClient(t *testing.T) *TestClient { - lock.Lock() - defer lock.Unlock() - - skipInCI(t) - setupFilesystem(t, targetConfig) - - if client == nil { - client = NewTestClient(t, targetConfig) - } - - return client -} - -func skipInCI(t *testing.T) { - if os.Getenv("CI") != "" { - t.Skip("Skipping test in CI environment") - } -} - // Tests the basic dispersal workflow: // - disperse a blob // - wait for it to be confirmed @@ -308,3 +187,36 @@ func TestUnauthorizedGetChunks(t *testing.T) { require.Error(t, err) require.Contains(t, err.Error(), "failed to get operator key: operator not found") } + +func TestDispersalWithInvalidSignature(t *testing.T) { + rand := random.NewTestRandom(t) + + c := getClient(t) + + // Create a dispersal client with a random key + signer, err := auth.NewLocalBlobRequestSigner(fmt.Sprintf("%x", rand.Bytes(32))) + require.NoError(t, err) + + signerAccountId, err := signer.GetAccountID() + require.NoError(t, err) + accountId := gethcommon.HexToAddress(signerAccountId) + fmt.Printf("Account ID: %s\n", accountId.String()) + + disperserConfig := &clients.DisperserClientConfig{ + Hostname: c.Config.DisperserHostname, + Port: fmt.Sprintf("%d", c.Config.DisperserPort), + UseSecureGrpcFlag: true, + } + disperserClient, err := clients.NewDisperserClient(disperserConfig, signer, nil, nil) + require.NoError(t, err) + + payload := rand.VariableBytes(units.KiB, 2*units.KiB) + paddedPayload := codec.ConvertByPaddingEmptyByte(payload) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + _, _, err = disperserClient.DisperseBlob(ctx, paddedPayload, 0, []core.QuorumID{0, 1}, rand.Uint32()) + require.Error(t, err) + require.Contains(t, err.Error(), "error accounting blob") +} From f0d9341c80f70875221da336ca7bdbaccd0dbe71 Mon Sep 17 00:00:00 2001 From: Cody Littley Date: Tue, 4 Feb 2025 11:34:57 -0600 Subject: [PATCH 3/3] Load fewer SRS points Signed-off-by: Cody Littley --- test/v2/test_client.go | 5 +++++ test/v2/test_setup.go | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/test/v2/test_client.go b/test/v2/test_client.go index 16ef3932e7..aad71ddea8 100644 --- a/test/v2/test_client.go +++ b/test/v2/test_client.go @@ -83,6 +83,11 @@ func (c *TestClientConfig) path(t *testing.T, elements ...string) string { // NewTestClient creates a new TestClient instance. func NewTestClient(t *testing.T, config *TestClientConfig) *TestClient { + if config.SRSNumberToLoad == 0 { + // See https://github.com/Layr-Labs/eigenda/pull/1208#discussion_r1941571297 + config.SRSNumberToLoad = config.MaxBlobSize / 32 / 4096 * 8 + } + var loggerConfig common.LoggerConfig if os.Getenv("CI") != "" { loggerConfig = common.DefaultLoggerConfig() diff --git a/test/v2/test_setup.go b/test/v2/test_setup.go index 1aad34deb2..67e3ac249d 100644 --- a/test/v2/test_setup.go +++ b/test/v2/test_setup.go @@ -21,7 +21,6 @@ var ( EigenDACertVerifierAddress: "0xe2C7AfB3c47B800b439b0a3d8EA40ca79759B245", SubgraphURL: "https://subgraph.satsuma-prod.com/51caed8fa9cb/eigenlabs/eigenda-operator-state-preprod-holesky/version/v0.7.0/api", SRSOrder: 268435456, - SRSNumberToLoad: 2097152, MaxBlobSize: 16 * units.MiB, MinimumSigningPercent: 55, }