diff --git a/README.md b/README.md index 907e616c..ac039a7c 100644 --- a/README.md +++ b/README.md @@ -192,8 +192,8 @@ Both `keccak256` (i.e, S3 storage using hash of pre-image for commitment value) OP Stack itself only has a conception of the first byte (`commit type`) and does no semantical interpretation of any subsequent bytes within the encoding. The `da layer type` byte for EigenDA is always `0x00`. However it is currently unused by OP Stack with name space values still being actively [discussed](https://github.com/ethereum-optimism/specs/discussions/135#discussioncomment-9271282). -### Simple Commitment Mode -For simple clients communicating with proxy (e.g, arbitrum nitro), the following commitment schema is supported: +### Standard Commitment Mode +For standard clients (i.e, `client/client.go`) communicating with proxy (e.g, arbitrum nitro), the following commitment schema is supported: ``` 0 1 N diff --git a/client/simple.go b/client/client.go similarity index 76% rename from client/simple.go rename to client/client.go index 18360def..b7f31649 100644 --- a/client/simple.go +++ b/client/client.go @@ -21,28 +21,28 @@ type HTTPClient interface { Do(req *http.Request) (*http.Response, error) } -// SimpleCommitmentClient implements a simple client for the eigenda-proxy -// that can put/get simple commitment data and query the health endpoint. +type ClientOption func(c *Client) + +// WithHTTPClient ... Embeds custom http client type +func WithHTTPClient(client HTTPClient) ClientOption { + return func(c *Client) { + c.httpClient = client + } +} + +// Client implements a standard client for the eigenda-proxy +// that can put/get standard commitment data and query the health endpoint. // Currently it is meant to be used by Arbitrum nitro integrations but can be extended to others in the future. // Optimism has its own client: https://github.com/ethereum-optimism/optimism/blob/develop/op-alt-da/daclient.go // so clients wanting to send op commitment mode data should use that client. -type SimpleCommitmentClient struct { +type Client struct { cfg *Config httpClient HTTPClient } -type SimpleClientOption func(scc *SimpleCommitmentClient) - -// WithHTTPClient ... Embeds custom http client type -func WithHTTPClient(client HTTPClient) SimpleClientOption { - return func(scc *SimpleCommitmentClient) { - scc.httpClient = client - } -} - -// New ... Constructor -func New(cfg *Config, opts ...SimpleClientOption) *SimpleCommitmentClient { - scc := &SimpleCommitmentClient{ +// New ... constructor +func New(cfg *Config, opts ...ClientOption) *Client { + scc := &Client{ cfg, http.DefaultClient, } @@ -56,7 +56,7 @@ func New(cfg *Config, opts ...SimpleClientOption) *SimpleCommitmentClient { // Health indicates if the server is operational; useful for event based awaits // when integration testing -func (c *SimpleCommitmentClient) Health() error { +func (c *Client) Health() error { url := c.cfg.URL + "/health" req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil) if err != nil { @@ -77,8 +77,8 @@ func (c *SimpleCommitmentClient) Health() error { } // GetData fetches blob data associated with a DA certificate -func (c *SimpleCommitmentClient) GetData(ctx context.Context, comm []byte) ([]byte, error) { - url := fmt.Sprintf("%s/get/0x%x?commitment_mode=simple", c.cfg.URL, comm) +func (c *Client) GetData(ctx context.Context, comm []byte) ([]byte, error) { + url := fmt.Sprintf("%s/get/0x%x?commitment_mode=standard", c.cfg.URL, comm) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { @@ -107,8 +107,8 @@ func (c *SimpleCommitmentClient) GetData(ctx context.Context, comm []byte) ([]by // SetData writes raw byte data to DA and returns the associated certificate // which should be verified within the proxy -func (c *SimpleCommitmentClient) SetData(ctx context.Context, b []byte) ([]byte, error) { - url := fmt.Sprintf("%s/put?commitment_mode=simple", c.cfg.URL) +func (c *Client) SetData(ctx context.Context, b []byte) ([]byte, error) { + url := fmt.Sprintf("%s/put?commitment_mode=standard", c.cfg.URL) req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(b)) if err != nil { return nil, fmt.Errorf("failed to create HTTP request: %w", err) diff --git a/client/go.mod b/client/go.mod new file mode 100644 index 00000000..dce7dcfd --- /dev/null +++ b/client/go.mod @@ -0,0 +1,10 @@ +// We use a separate module for the client to allow dependencies to import it without importing all of proxy's main module's dependencies. +// This follows the recommendation in: https://go.dev/wiki/Modules#should-i-have-multiple-modules-in-a-single-repository +// +// Two example scenarios where it can make sense to have more than one go.mod in a repository: +// 1. [omitted] +// 2. if you have a repository with a complex set of dependencies, but you have a client API with a smaller set of dependencies. +// In some cases, it might make sense to have an api or clientapi or similar directory with its own go.mod, or to separate out that clientapi into its own repository. +module github.com/Layr-Labs/eigenda-proxy/client + +go 1.21.0 diff --git a/commitments/mode.go b/commitments/mode.go index 4931bb9c..7eaee4a9 100644 --- a/commitments/mode.go +++ b/commitments/mode.go @@ -13,9 +13,9 @@ type CommitmentMeta struct { type CommitmentMode string const ( - OptimismKeccak CommitmentMode = "optimism_keccak256" - OptimismGeneric CommitmentMode = "optimism_generic" - SimpleCommitmentMode CommitmentMode = "simple" + OptimismKeccak CommitmentMode = "optimism_keccak256" + OptimismGeneric CommitmentMode = "optimism_generic" + Standard CommitmentMode = "standard" ) func StringToCommitmentMode(s string) (CommitmentMode, error) { @@ -24,8 +24,8 @@ func StringToCommitmentMode(s string) (CommitmentMode, error) { return OptimismKeccak, nil case string(OptimismGeneric): return OptimismGeneric, nil - case string(SimpleCommitmentMode): - return SimpleCommitmentMode, nil + case string(Standard): + return Standard, nil default: return "", fmt.Errorf("unknown commitment mode: %s", s) } @@ -42,7 +42,7 @@ func EncodeCommitment(b []byte, c CommitmentMode) ([]byte, error) { altDACommit := NewGenericCommitment(svcCommit).Encode() return altDACommit, nil - case SimpleCommitmentMode: + case Standard: return NewV0CertCommitment(b).Encode(), nil } diff --git a/e2e/main_test.go b/e2e/main_test.go index f8a60cb3..0af174fb 100644 --- a/e2e/main_test.go +++ b/e2e/main_test.go @@ -65,8 +65,8 @@ func requireWriteReadSecondary(t *testing.T, cm *metrics.CountMap, bt common.Bac require.True(t, readCount > 0) } -// requireSimpleClientSetGet ... ensures that simple proxy client can disperse and read a blob -func requireSimpleClientSetGet(t *testing.T, ts e2e.TestSuite, blob []byte) { +// requireStandardClientSetGet ... ensures that std proxy client can disperse and read a blob +func requireStandardClientSetGet(t *testing.T, ts e2e.TestSuite, blob []byte) { cfg := &client.Config{ URL: ts.Address(), } diff --git a/e2e/safety_checks_test.go b/e2e/safety_checks_test.go index 9f248278..571bf02b 100644 --- a/e2e/safety_checks_test.go +++ b/e2e/safety_checks_test.go @@ -121,7 +121,7 @@ func TestProxyClientMalformedInputCases(t *testing.T) { t.Run("get data edge cases - huge cert", func(t *testing.T) { // TODO: we need to add the 0 version byte at the beginning. - // should this not be done automatically by the simple_commitment client? + // should this not be done automatically by the std_commitment client? testCert := append([]byte{0}, e2e.RandBytes(10000)...) _, err := daClient.GetData(ts.Ctx, testCert) require.Error(t, err) diff --git a/e2e/server_test.go b/e2e/server_test.go index 19accff1..380da2bf 100644 --- a/e2e/server_test.go +++ b/e2e/server_test.go @@ -63,8 +63,8 @@ func TestProxyClientWriteRead(t *testing.T) { ts, kill := e2e.CreateTestSuite(tsConfig) defer kill() - requireSimpleClientSetGet(t, ts, e2e.RandBytes(100)) - requireDispersalRetrievalEigenDA(t, ts.Metrics.HTTPServerRequestsTotal, commitments.SimpleCommitmentMode) + requireStandardClientSetGet(t, ts, e2e.RandBytes(100)) + requireDispersalRetrievalEigenDA(t, ts.Metrics.HTTPServerRequestsTotal, commitments.Standard) } func TestProxyWithMaximumSizedBlob(t *testing.T) { @@ -78,8 +78,8 @@ func TestProxyWithMaximumSizedBlob(t *testing.T) { ts, kill := e2e.CreateTestSuite(tsConfig) defer kill() - requireSimpleClientSetGet(t, ts, e2e.RandBytes(16_000_000)) - requireDispersalRetrievalEigenDA(t, ts.Metrics.HTTPServerRequestsTotal, commitments.SimpleCommitmentMode) + requireStandardClientSetGet(t, ts, e2e.RandBytes(16_000_000)) + requireDispersalRetrievalEigenDA(t, ts.Metrics.HTTPServerRequestsTotal, commitments.Standard) } /* @@ -99,9 +99,9 @@ func TestProxyCaching(t *testing.T) { ts, kill := e2e.CreateTestSuite(tsConfig) defer kill() - requireSimpleClientSetGet(t, ts, e2e.RandBytes(1_000_000)) + requireStandardClientSetGet(t, ts, e2e.RandBytes(1_000_000)) requireWriteReadSecondary(t, ts.Metrics.SecondaryRequestsTotal, common.S3BackendType) - requireDispersalRetrievalEigenDA(t, ts.Metrics.HTTPServerRequestsTotal, commitments.SimpleCommitmentMode) + requireDispersalRetrievalEigenDA(t, ts.Metrics.HTTPServerRequestsTotal, commitments.Standard) } func TestProxyCachingWithRedis(t *testing.T) { @@ -118,9 +118,9 @@ func TestProxyCachingWithRedis(t *testing.T) { ts, kill := e2e.CreateTestSuite(tsConfig) defer kill() - requireSimpleClientSetGet(t, ts, e2e.RandBytes(1_000_000)) + requireStandardClientSetGet(t, ts, e2e.RandBytes(1_000_000)) requireWriteReadSecondary(t, ts.Metrics.SecondaryRequestsTotal, common.RedisBackendType) - requireDispersalRetrievalEigenDA(t, ts.Metrics.HTTPServerRequestsTotal, commitments.SimpleCommitmentMode) + requireDispersalRetrievalEigenDA(t, ts.Metrics.HTTPServerRequestsTotal, commitments.Standard) } /* @@ -162,7 +162,7 @@ func TestProxyReadFallback(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedBlob, actualBlob) - requireSimpleClientSetGet(t, ts, e2e.RandBytes(1_000_000)) + requireStandardClientSetGet(t, ts, e2e.RandBytes(1_000_000)) requireWriteReadSecondary(t, ts.Metrics.SecondaryRequestsTotal, common.S3BackendType) - requireDispersalRetrievalEigenDA(t, ts.Metrics.HTTPServerRequestsTotal, commitments.SimpleCommitmentMode) + requireDispersalRetrievalEigenDA(t, ts.Metrics.HTTPServerRequestsTotal, commitments.Standard) } diff --git a/go.mod b/go.mod index 2e17db67..c77ad910 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ toolchain go1.22.7 require ( github.com/Layr-Labs/eigenda v0.8.5-rc.0.0.20241101212705-fa8776ae648c + github.com/Layr-Labs/eigenda-proxy/client v0.0.0-00010101000000-000000000000 github.com/avast/retry-go/v4 v4.6.0 github.com/consensys/gnark-crypto v0.12.1 github.com/ethereum-optimism/optimism v1.9.5 @@ -24,6 +25,9 @@ require ( google.golang.org/grpc v1.64.1 ) +// TODO: Remove this after we have published v0.1.0 of the new client module. +replace github.com/Layr-Labs/eigenda-proxy/client => ./client/ + require ( dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect diff --git a/server/handlers.go b/server/handlers.go index 8cb4e27c..d1acb29c 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -21,14 +21,14 @@ func (svr *Server) handleHealth(w http.ResponseWriter, _ *http.Request) error { // GET ROUTES // ================================================================================================= -// handleGetSimpleCommitment handles the GET request for simple commitments. -func (svr *Server) handleGetSimpleCommitment(w http.ResponseWriter, r *http.Request) error { +// handleGetStdCommitment handles the GET request for std commitments. +func (svr *Server) handleGetStdCommitment(w http.ResponseWriter, r *http.Request) error { versionByte, err := parseVersionByte(w, r) if err != nil { return fmt.Errorf("error parsing version byte: %w", err) } commitmentMeta := commitments.CommitmentMeta{ - Mode: commitments.SimpleCommitmentMode, + Mode: commitments.Standard, CertVersion: versionByte, } @@ -118,10 +118,10 @@ func (svr *Server) handleGetShared(ctx context.Context, w http.ResponseWriter, c // POST ROUTES // ================================================================================================= -// handlePostSimpleCommitment handles the POST request for simple commitments. -func (svr *Server) handlePostSimpleCommitment(w http.ResponseWriter, r *http.Request) error { +// handlePostStdCommitment handles the POST request for std commitments. +func (svr *Server) handlePostStdCommitment(w http.ResponseWriter, r *http.Request) error { commitmentMeta := commitments.CommitmentMeta{ - Mode: commitments.SimpleCommitmentMode, + Mode: commitments.Standard, CertVersion: byte(commitments.CertV0), // TODO: hardcoded for now } return svr.handlePostShared(w, r, nil, commitmentMeta) diff --git a/server/handlers_test.go b/server/handlers_test.go index 97958826..4385da28 100644 --- a/server/handlers_test.go +++ b/server/handlers_test.go @@ -25,7 +25,7 @@ import ( ) const ( - simpleCommitmentPrefix = "\x00" + stdCommitmentPrefix = "\x00" // [alt-da, da layer, cert version] opGenericPrefixStr = "\x01\x00\x00" @@ -145,14 +145,14 @@ func TestHandlerPutSuccess(t *testing.T) { expectedBody: "", }, { - name: "Success Simple Commitment Mode", - url: "/put?commitment_mode=simple", + name: "Success Standard Commitment Mode", + url: "/put?commitment_mode=standard", body: []byte("some data that will successfully be written to EigenDA"), mockBehavior: func() { mockStorageMgr.EXPECT().Put(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte(testCommitStr), nil) }, expectedCode: http.StatusOK, - expectedBody: simpleCommitmentPrefix + testCommitStr, + expectedBody: stdCommitmentPrefix + testCommitStr, }, } @@ -198,8 +198,8 @@ func TestHandlerPutErrors(t *testing.T) { url: fmt.Sprintf("/put/0x00%s", testCommitStr), }, { - name: "Simple Commitment Mode", - url: "/put?commitment_mode=simple", + name: "Standard Commitment Mode", + url: "/put?commitment_mode=standard", }, } diff --git a/server/routing.go b/server/routing.go index ca235935..fe4fd458 100644 --- a/server/routing.go +++ b/server/routing.go @@ -16,13 +16,13 @@ const ( func (svr *Server) registerRoutes(r *mux.Router) { subrouterGET := r.Methods("GET").PathPrefix("/get").Subrouter() - // simple commitments (for nitro) + // std commitments (for nitro) subrouterGET.HandleFunc("/"+ "{optional_prefix:(?:0x)?}"+ // commitments can be prefixed with 0x "{"+routingVarNameVersionByteHex+":[0-9a-fA-F]{2}}"+ // should always be 0x00 for now but we let others through to return a 404 "{"+routingVarNameRawCommitmentHex+":[0-9a-fA-F]*}", - withLogging(withMetrics(svr.handleGetSimpleCommitment, svr.m, commitments.SimpleCommitmentMode), svr.log), - ).Queries("commitment_mode", "simple") + withLogging(withMetrics(svr.handleGetStdCommitment, svr.m, commitments.Standard), svr.log), + ).Queries("commitment_mode", "standard") // op keccak256 commitments (write to S3) subrouterGET.HandleFunc("/"+ "{optional_prefix:(?:0x)?}"+ // commitments can be prefixed with 0x @@ -51,13 +51,13 @@ func (svr *Server) registerRoutes(r *mux.Router) { commitType := mux.Vars(r)[routingVarNameCommitTypeByteHex] http.Error(w, fmt.Sprintf("unsupported commitment type %s", commitType), http.StatusBadRequest) }, - ).MatcherFunc(notCommitmentModeSimple) + ).MatcherFunc(notCommitmentModeStandard) subrouterPOST := r.Methods("POST").PathPrefix("/put").Subrouter() - // simple commitments (for nitro) + // std commitments (for nitro) subrouterPOST.HandleFunc("", // commitment is calculated by the server using the body data - withLogging(withMetrics(svr.handlePostSimpleCommitment, svr.m, commitments.SimpleCommitmentMode), svr.log), - ).Queries("commitment_mode", "simple") + withLogging(withMetrics(svr.handlePostStdCommitment, svr.m, commitments.Standard), svr.log), + ).Queries("commitment_mode", "standard") // op keccak256 commitments (write to S3) subrouterPOST.HandleFunc("/"+ "{optional_prefix:(?:0x)?}"+ // commitments can be prefixed with 0x @@ -80,7 +80,7 @@ func (svr *Server) registerRoutes(r *mux.Router) { r.HandleFunc("/health", withLogging(svr.handleHealth, svr.log)).Methods("GET") } -func notCommitmentModeSimple(r *http.Request, _ *mux.RouteMatch) bool { +func notCommitmentModeStandard(r *http.Request, _ *mux.RouteMatch) bool { commitmentMode := r.URL.Query().Get("commitment_mode") - return commitmentMode == "" || commitmentMode != "simple" + return commitmentMode == "" || commitmentMode != "standard" } diff --git a/store/manager.go b/store/manager.go index d6bd4b3b..93721d89 100644 --- a/store/manager.go +++ b/store/manager.go @@ -22,7 +22,7 @@ type IManager interface { type Manager struct { log log.Logger // primary storage backends - eigenda common.GeneratedKeyStore // ALT DA commitment type for OP mode && simple commitment mode for standard /client + eigenda common.GeneratedKeyStore // ALT DA commitment type for OP mode && std commitment mode for standard /client s3 common.PrecomputedKeyStore // OP commitment mode && keccak256 commitment type // secondary storage backends (caching and fallbacks) @@ -63,7 +63,7 @@ func (m *Manager) Get(ctx context.Context, key []byte, cm commitments.Commitment } return value, nil - case commitments.SimpleCommitmentMode, commitments.OptimismGeneric: + case commitments.Standard, commitments.OptimismGeneric: if m.eigenda == nil { return nil, errors.New("expected EigenDA backend for DA commitment type, but none configured") } @@ -117,7 +117,7 @@ func (m *Manager) Put(ctx context.Context, cm commitments.CommitmentMode, key, v switch cm { case commitments.OptimismKeccak: // caching and fallbacks are unsupported for this commitment mode return m.putKeccak256Mode(ctx, key, value) - case commitments.OptimismGeneric, commitments.SimpleCommitmentMode: + case commitments.OptimismGeneric, commitments.Standard: commit, err = m.putEigenDAMode(ctx, value) default: return nil, fmt.Errorf("unknown commitment mode")