From e5f8c9ef77090846a73f434c177af170712e5089 Mon Sep 17 00:00:00 2001 From: Teddy Knox Date: Thu, 13 Jun 2024 20:47:44 -0400 Subject: [PATCH] Add commitment mode parameter and commitment type system --- .github/workflows/actions.yml | 3 +- .gitignore | 6 +- README.md | 6 +- client/client.go | 42 +- cmd/server/entrypoint.go | 2 +- commitments/da_service_op_commitment.go | 67 + commitments/eigenda_commitment.go | 58 + commitments/interface.go | 6 + commitments/op_commitment.go | 90 + common/common.go | 174 - e2e/resources/kzg/SRSTables/dimE512.coset1 | Bin 0 -> 32769 bytes e2e/server_test.go | 9 +- e2e/setup.go | 13 +- eigenda/commitment.go | 73 - go.mod | 2 +- graph.svg | 3791 +++++++++++++++++ server/commitment_mode.go | 97 + {eigenda => server}/config.go | 45 +- server/domain_type.go | 56 + store/eigenda.go => server/eigenda_store.go | 13 +- server/flags.go | 11 +- server/load_store.go | 10 +- store/memory.go => server/memory_store.go | 75 +- .../memory_store_test.go | 31 +- server/server.go | 73 +- store/types.go | 16 - utils/parse_bytes.go | 70 + .../parse_bytes_test.go | 7 +- verify/cert.go | 8 +- verify/certificate.go | 70 + verify/hasher.go | 5 +- verify/hasher_test.go | 5 +- verify/verifier.go | 9 +- verify/verify_test.go | 10 +- 34 files changed, 4474 insertions(+), 479 deletions(-) create mode 100644 commitments/da_service_op_commitment.go create mode 100644 commitments/eigenda_commitment.go create mode 100644 commitments/interface.go create mode 100644 commitments/op_commitment.go delete mode 100644 common/common.go create mode 100644 e2e/resources/kzg/SRSTables/dimE512.coset1 delete mode 100644 eigenda/commitment.go create mode 100644 graph.svg create mode 100644 server/commitment_mode.go rename {eigenda => server}/config.go (84%) create mode 100644 server/domain_type.go rename store/eigenda.go => server/eigenda_store.go (92%) rename store/memory.go => server/memory_store.go (71%) rename store/memory_test.go => server/memory_store_test.go (70%) delete mode 100644 store/types.go create mode 100644 utils/parse_bytes.go rename common/common_test.go => utils/parse_bytes_test.go (92%) create mode 100644 verify/certificate.go diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 2c454023..9c1ad4ac 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -70,12 +70,11 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.21 + go-version: 1.21 - name: Install project dependencies run: | go mod download - - name: Run holesky tests env: SIGNER_PRIVATE_KEY: ${{ secrets.SIGNER_PRIVATE_KEY }} diff --git a/.gitignore b/.gitignore index 08205e05..1680322c 100644 --- a/.gitignore +++ b/.gitignore @@ -24,10 +24,10 @@ go.work .env ## kzg cache -e2e/resources/kzg/SRSTables/ - +test/resources/SRSTables/ + ## Vscode /.vscode ## Idea -.idea \ No newline at end of file +.idea diff --git a/README.md b/README.md index 0ac4be86..ca2c11e8 100644 --- a/README.md +++ b/README.md @@ -84,16 +84,18 @@ The `raw commitment` for EigenDA is encoding certificate and kzg fields. ## Testing ### Unit + Unit tests can be ran via invoking `make test`. ### Holesky -A holesky integration test can be ran using `make holesky-test` to assert proper dispersal/retrieval against a public network. Please **note** that EigenDA Holesky network which is subject to rate-limiting and slow confirmation times *(i.e, >10 minutes per blob confirmation)*. Please advise EigenDA's [inabox](https://github.com/Layr-Labs/eigenda/tree/master/inabox#readme) if you'd like to spin-up a local DA network for faster iteration testing. +A holesky integration test can be ran using `make holesky-test` to assert proper dispersal/retrieval against a public network. Please **note** that EigenDA Holesky network which is subject to rate-limiting and slow confirmation times *(i.e, >10 minutes per blob confirmation)*. Please advise EigenDA's [inabox](https://github.com/Layr-Labs/eigenda/tree/master/inabox#readme) if you'd like to spin-up a local DA network for faster iteration testing. ### Optimism + An E2E test exists which spins up a local OP sequencer instance using the [op-e2e](https://github.com/ethereum-optimism/optimism/tree/develop/op-e2e) framework for asserting correct interaction behaviors with batch submission and state derivation. These tests can be ran via `make optimism-test`. -**NOTE:** +**NOTE:** ## Downloading Mainnet SRS diff --git a/client/client.go b/client/client.go index d31e273c..1a12374a 100644 --- a/client/client.go +++ b/client/client.go @@ -7,14 +7,7 @@ import ( "io" "net/http" - "github.com/Layr-Labs/eigenda-proxy/common" - "github.com/Layr-Labs/eigenda-proxy/eigenda" - "github.com/ethereum/go-ethereum/rlp" -) - -const ( - // NOTE: this will need to be updated as plasma's implementation changes - decodingOffset = 3 + "github.com/Layr-Labs/eigenda-proxy/server" ) // TODO: Add support for custom http client option @@ -25,8 +18,8 @@ type Config struct { // ProxyClient is an interface for communicating with the EigenDA proxy server type ProxyClient interface { Health() error - GetData(ctx context.Context, cert *common.Certificate, domain common.DomainType) ([]byte, error) - SetData(ctx context.Context, b []byte) (*common.Certificate, error) + GetData(ctx context.Context, cert []byte, domain server.DomainType) ([]byte, error) + SetData(ctx context.Context, b []byte) ([]byte, error) } // client is the implementation of ProxyClient @@ -35,6 +28,8 @@ type client struct { httpClient *http.Client } +var _ ProxyClient = (*client)(nil) + func New(cfg *Config) ProxyClient { return &client{ cfg, @@ -64,16 +59,8 @@ func (c *client) Health() error { } // GetData fetches blob data associated with a DA certificate -func (c *client) GetData(ctx context.Context, cert *common.Certificate, domain common.DomainType) ([]byte, error) { - b, err := rlp.EncodeToBytes(cert) - if err != nil { - return nil, err - } - - // encode prefix bytes - b = eigenda.Commitment(b).Encode() - - url := fmt.Sprintf("%s/get/0x%x?domain=%s", c.cfg.URL, b, domain.String()) +func (c *client) GetData(ctx context.Context, comm []byte, domain server.DomainType) ([]byte, error) { + url := fmt.Sprintf("%s/get/0x%x?domain=%s&commitment_mode=simple", c.cfg.URL, comm, domain.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { @@ -96,8 +83,8 @@ func (c *client) GetData(ctx context.Context, cert *common.Certificate, domain c } // SetData writes raw byte data to DA and returns the respective certificate -func (c *client) SetData(ctx context.Context, b []byte) (*common.Certificate, error) { - url := fmt.Sprintf("%s/put/", c.cfg.URL) +func (c *client) SetData(ctx context.Context, b []byte) ([]byte, error) { + url := fmt.Sprintf("%s/put/?commitment_mode=simple", 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) @@ -117,14 +104,9 @@ func (c *client) SetData(ctx context.Context, b []byte) (*common.Certificate, er return nil, err } - if len(b) < decodingOffset { - return nil, fmt.Errorf("read certificate is of invalid length: %d", len(b)) - } - - var cert *common.Certificate - if err = rlp.DecodeBytes(b[decodingOffset:], &cert); err != nil { - return nil, err + if len(b) == 0 { + return nil, fmt.Errorf("read certificate is empty") } - return cert, err + return b, err } diff --git a/cmd/server/entrypoint.go b/cmd/server/entrypoint.go index ec45143d..b0f2f537 100644 --- a/cmd/server/entrypoint.go +++ b/cmd/server/entrypoint.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/Layr-Labs/eigenda-proxy/metrics" + "github.com/Layr-Labs/eigenda-proxy/server" "github.com/urfave/cli/v2" - "github.com/Layr-Labs/eigenda-proxy/server" oplog "github.com/ethereum-optimism/optimism/op-service/log" "github.com/ethereum-optimism/optimism/op-service/opio" ) diff --git a/commitments/da_service_op_commitment.go b/commitments/da_service_op_commitment.go new file mode 100644 index 00000000..d0b89a9e --- /dev/null +++ b/commitments/da_service_op_commitment.go @@ -0,0 +1,67 @@ +package commitments + +import ( + "fmt" + "log" +) + +type DAServiceOPCommitmentType byte + +const ( + EigenDAByte DAServiceOPCommitmentType = 0 +) + +// DAServiceOPCommitment represents a value of one of two possible types (Keccak256Commitment or DAServiceCommitment). +type DAServiceOPCommitment struct { + eigendaCommitment *EigenDACommitment +} + +var _ Commitment = (*DAServiceOPCommitment)(nil) + +func OptimismEigenDACommitment(value EigenDACommitment) DAServiceOPCommitment { + return DAServiceOPCommitment{eigendaCommitment: &value} +} + +func (e DAServiceOPCommitment) IsEigenDA() bool { + return e.eigendaCommitment != nil +} + +func (e DAServiceOPCommitment) MustEigenDAValue() EigenDACommitment { + if e.eigendaCommitment != nil { + return *e.eigendaCommitment + } + log.Panic("CommitmentEither does not contain a Keccak256Commitment value") + return EigenDACommitment{} // This will never be reached, but is required for compilation. +} + +func (e DAServiceOPCommitment) Marshal() ([]byte, error) { + if e.IsEigenDA() { + eigenDABytes, err := e.MustEigenDAValue().Marshal() + if err != nil { + return nil, err + } + return append([]byte{byte(EigenDAByte)}, eigenDABytes...), nil + } else { + return nil, fmt.Errorf("DAServiceOPCommitment is neither a keccak256 commitment or a DA service commitment") + } +} + +func (e *DAServiceOPCommitment) Unmarshal(bz []byte) error { + if len(bz) < 1 { + return fmt.Errorf("OP commitment does not contain generic commitment type prefix byte") + } + head := DAServiceOPCommitmentType(bz[0]) + tail := bz[1:] + switch head { + case EigenDAByte: + eigendaCommitment := EigenDACommitment{} + err := eigendaCommitment.Unmarshal(tail) + if err != nil { + return err + } + e.eigendaCommitment = &eigendaCommitment + default: + return fmt.Errorf("unrecognized generic commitment type byte: %x", bz[0]) + } + return nil +} diff --git a/commitments/eigenda_commitment.go b/commitments/eigenda_commitment.go new file mode 100644 index 00000000..6d92d65f --- /dev/null +++ b/commitments/eigenda_commitment.go @@ -0,0 +1,58 @@ +package commitments + +import ( + "fmt" + "log" +) + +// Define the parent and child types +type CertEncodingVersion byte + +const ( + CertEncodingV0 CertEncodingVersion = 0 +) + +type EigenDACommitment struct { + certV0 []byte +} + +var _ Commitment = (*EigenDACommitment)(nil) + +func EigenDACertV0(value []byte) EigenDACommitment { + return EigenDACommitment{certV0: value} +} + +func (e EigenDACommitment) IsCertV0() bool { + return e.certV0 != nil +} + +func (e EigenDACommitment) MustCertV0Value() []byte { + if e.certV0 != nil { + return e.certV0 + } + log.Panic("CommitmentEither does not contain a Keccak256Commitment value") + return nil // This will never be reached, but is required for compilation. +} + +func (e EigenDACommitment) Marshal() ([]byte, error) { + if e.IsCertV0() { + return append([]byte{byte(CertEncodingV0)}, e.certV0...), nil + } else { + return nil, fmt.Errorf("EigenDADAServiceOPCommitment is of unknown type") + } +} + +func (e *EigenDACommitment) Unmarshal(bz []byte) error { + if len(bz) < 1 { + return fmt.Errorf("OP commitment does not contain eigenda commitment encoding version prefix byte") + } + head := CertEncodingVersion(bz[0]) + tail := bz[1:] + switch head { + case CertEncodingV0: + e.certV0 = tail + default: + return fmt.Errorf("unrecognized EigenDA commitment encoding type byte: %x", bz[0]) + } + return nil +} diff --git a/commitments/interface.go b/commitments/interface.go new file mode 100644 index 00000000..da47a3b8 --- /dev/null +++ b/commitments/interface.go @@ -0,0 +1,6 @@ +package commitments + +type Commitment interface { + Marshal() ([]byte, error) + Unmarshal([]byte) error +} diff --git a/commitments/op_commitment.go b/commitments/op_commitment.go new file mode 100644 index 00000000..ef035ddc --- /dev/null +++ b/commitments/op_commitment.go @@ -0,0 +1,90 @@ +package commitments + +import ( + "fmt" + "log" +) + +type OPCommitmentType byte + +const ( + // Keccak256CommitmentTypeByte represents a commitment using Keccak256 hashing. + Keccak256CommitmentTypeByte OPCommitmentType = 0 + // GenericCommitmentTypeByte represents a commitment using a DA service. + GenericCommitmentTypeByte OPCommitmentType = 1 +) + +type OPCommitment struct { + keccak256Commitment []byte + genericCommitment *DAServiceOPCommitment +} + +var _ Commitment = (*OPCommitment)(nil) + +func Keccak256Commitment(value []byte) OPCommitment { + return OPCommitment{keccak256Commitment: value} +} + +func GenericCommitment(value DAServiceOPCommitment) OPCommitment { + return OPCommitment{genericCommitment: &value} +} + +func (e OPCommitment) IsKeccak256Commitment() bool { + return e.keccak256Commitment != nil +} + +func (e OPCommitment) IsGenericCommitment() bool { + return e.genericCommitment != nil +} + +func (e OPCommitment) MustKeccak256CommitmentValue() []byte { + if e.keccak256Commitment != nil { + return e.keccak256Commitment + } + log.Panic("OPCommitment does not contain a Keccak256Commitment value") + return nil // This will never be reached, but is required for compilation. +} + +func (e OPCommitment) MustGenericCommitmentValue() DAServiceOPCommitment { + if e.genericCommitment != nil { + return *e.genericCommitment + } + log.Panic("OPCommitment does not contain a DAServiceCommitment value") + return DAServiceOPCommitment{} // This will never be reached, but is required for compilation. +} + +func (e OPCommitment) Marshal() ([]byte, error) { + if e.IsGenericCommitment() { + bytes, err := e.MustGenericCommitmentValue().Marshal() + if err != nil { + return nil, err + } + return append([]byte{byte(GenericCommitmentTypeByte)}, bytes...), nil + } else if e.IsKeccak256Commitment() { + return append([]byte{byte(Keccak256CommitmentTypeByte)}, e.MustKeccak256CommitmentValue()...), nil + } else { + return nil, fmt.Errorf("OPCommitment is neither a Keccak256 commitment nor a DA service commitment") + } +} + +func (e *OPCommitment) Unmarshal(bz []byte) error { + if len(bz) < 1 { + return fmt.Errorf("OPCommitment does not contain a commitment type prefix byte") + } + head := OPCommitmentType(bz[0]) + tail := bz[1:] + switch head { + case Keccak256CommitmentTypeByte: + e.keccak256Commitment = tail + case GenericCommitmentTypeByte: + daServiceCommitment := DAServiceOPCommitment{} + err := daServiceCommitment.Unmarshal(tail) + if err != nil { + return err + } + e.genericCommitment = &daServiceCommitment + default: + return fmt.Errorf("unrecognized commitment type byte: %x", bz[0]) + } + return nil +} diff --git a/common/common.go b/common/common.go deleted file mode 100644 index c1ccdd8e..00000000 --- a/common/common.go +++ /dev/null @@ -1,174 +0,0 @@ -package common - -import ( - "fmt" - "math/big" - "strconv" - "strings" - - "github.com/Layr-Labs/eigenda/api/grpc/disperser" -) - -var ( - ErrInvalidDomainType = fmt.Errorf("invalid domain type") -) - -// G1Point struct to represent G1Point in Solidity -type G1Point struct { - X *big.Int - Y *big.Int -} - -// QuorumBlobParam struct to represent QuorumBlobParam in Solidity -type QuorumBlobParam struct { - QuorumNumber uint8 - AdversaryThresholdPercentage uint8 - ConfirmationThresholdPercentage uint8 - ChunkLength uint32 -} - -// BlobHeader struct to represent BlobHeader in Solidity -type BlobHeader struct { - Commitment G1Point - DataLength uint32 - QuorumBlobParams []QuorumBlobParam -} - -type Certificate disperser.BlobInfo - -func (c *Certificate) BlobIndex() uint32 { - return c.BlobVerificationProof.BlobIndex -} - -func (c *Certificate) BatchHeaderRoot() []byte { - return c.BlobVerificationProof.BatchMetadata.BatchHeader.BatchRoot -} - -func (c *Certificate) ReadBlobHeader() BlobHeader { - // parse quorum params - - qps := make([]QuorumBlobParam, len(c.BlobHeader.BlobQuorumParams)) - for i, qp := range c.BlobHeader.BlobQuorumParams { - qps[i] = QuorumBlobParam{ - QuorumNumber: uint8(qp.QuorumNumber), - AdversaryThresholdPercentage: uint8(qp.AdversaryThresholdPercentage), - ConfirmationThresholdPercentage: uint8(qp.ConfirmationThresholdPercentage), - ChunkLength: qp.ChunkLength, - } - } - - return BlobHeader{ - Commitment: G1Point{ - X: new(big.Int).SetBytes(c.BlobHeader.Commitment.X), - Y: new(big.Int).SetBytes(c.BlobHeader.Commitment.Y), - }, - DataLength: c.BlobHeader.DataLength, - QuorumBlobParams: qps, - } -} - -func (c *Certificate) Proof() *disperser.BlobVerificationProof { - return c.BlobVerificationProof -} - -// DomainType is an enumeration type for the different data domains for which a -// blob can exist between -type DomainType uint8 - -const ( - BinaryDomain DomainType = iota - PolyDomain - UnknownDomain -) - -func (dt DomainType) String() string { - switch dt { - case BinaryDomain: - return "binary" - - case PolyDomain: - return "polynomial" - - default: - return "unknown" - } -} - -func StrToDomainType(s string) DomainType { - switch s { - case "binary": - return BinaryDomain - case "polynomial": - return PolyDomain - default: - return UnknownDomain - } -} - -// Helper utility functions // - -func EqualSlices[P comparable](s1, s2 []P) bool { - if len(s1) != len(s2) { - return false - } - - for i := 0; i < len(s1); i++ { - if s1[i] != s2[i] { - return false - } - } - - return true -} - -func ParseBytesAmount(s string) (uint64, error) { - s = strings.TrimSpace(s) - - // Extract numeric part and unit - numStr := s - unit := "" - for i, r := range s { - if !('0' <= r && r <= '9' || r == '.') { - numStr = s[:i] - unit = s[i:] - break - } - } - - // Convert numeric part to float64 - num, err := strconv.ParseFloat(numStr, 64) - if err != nil { - return 0, fmt.Errorf("invalid numeric value: %v", err) - } - - unit = strings.ToLower(strings.TrimSpace(unit)) - - // Convert to uint64 based on the unit (case-insensitive) - switch unit { - case "b", "": - return uint64(num), nil - case "kib": - return uint64(num * 1024), nil - case "kb": - return uint64(num * 1000), nil // Decimal kilobyte - case "mib": - return uint64(num * 1024 * 1024), nil - case "mb": - return uint64(num * 1000 * 1000), nil // Decimal megabyte - case "gib": - return uint64(num * 1024 * 1024 * 1024), nil - case "gb": - return uint64(num * 1000 * 1000 * 1000), nil // Decimal gigabyte - case "tib": - return uint64(num * 1024 * 1024 * 1024 * 1024), nil - case "tb": - return uint64(num * 1000 * 1000 * 1000 * 1000), nil // Decimal terabyte - default: - return 0, fmt.Errorf("unsupported unit: %s", unit) - } -} - -type Stats struct { - Entries int - Reads int -} diff --git a/e2e/resources/kzg/SRSTables/dimE512.coset1 b/e2e/resources/kzg/SRSTables/dimE512.coset1 new file mode 100644 index 0000000000000000000000000000000000000000..3444d721303fcf7dce4facb17bbf76eceb3df1a7 GIT binary patch literal 32769 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 z z{|7l^ar$ky){g8-Z0xh6+s)9IL7J@uR!g=WV=F4UcRZUnd{q`-!$7Gf?%nT`@ z!v@v0hnU78r2sN+3)i-c87Dqit8Od#k_?ty0l0ZE)XJ=0p!&B(F4@foj-I$-hV@~6 z-veMo>59Jp23(M%wY9*BYG;)x%k4x2^;XwhzlZ%W3HkVM-{TCkt`Qu5d<+_IJ|VVu zISm~Cfq45VhQONCImV7kbS1DNUz{;bB@2hor%?_&Ryky8n6kb7HXY4HArC6GTG-->5v z#lE>)i66BwcM}8mINL_h!DJ2Djo8^bo_=BX5Zr)rgh(x$y9=ZLTPoABe{-#y>DSH? zRg^SS^dIqnF1cNzg$%&5zTzABx%bZspu213@?B3xZ;906)12sR<3{D8!P=}c=VWcI zw{%dtof*TT7EMN1v?U zNB0|#p#I6e`--?T6JFB*$-*RYTYkV#8qmAh;W{M4Lv7SFkl@MfvxS&C9vtG@nU|jy z{&=xPu*4;6=UKY2FHVaVC_^d4cr2u=yPUUdY2KNd2Hoy;z_Rb + + + + + +G + + + +flag + + +flag +1051 / 39.7KB + + + + + +github.com/ipfs/go-cid + + +github.com/ipfs/go-cid +883 / 25.3KB + + + + + +github.com/multiformats/go-multihash + + +github.com/multiformats/go-multihash +502 / 15.5KB + + + + + +github.com/ipfs/go-cid:e->github.com/multiformats/go-multihash + + + + + +github.com/ipfs/go-log/v2 + + +github.com/ipfs/go-log/v2 +622 / 17.8KB + + + + + +go.uber.org/zap + + +go.uber.org/zap +3169 / 116.3KB + + + + + +github.com/ipfs/go-log/v2:e->go.uber.org/zap + + + + + +github.com/klauspost/cpuid/v2 + + +github.com/klauspost/cpuid/v2 +1963 / 71.3KB + + + + + +github.com/klauspost/cpuid/v2:e->flag + + + + + +github.com/libp2p/go-libp2p + + +github.com/libp2p/go-libp2p +934 / 33.3KB + + + + + +github.com/libp2p/go-libp2p/config + + +github.com/libp2p/go-libp2p/config +500 / 16.4KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/config + + + + + +github.com/libp2p/go-libp2p/core/connmgr + + +github.com/libp2p/go-libp2p/core/connmgr +337 / 14.6KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/core/host + + +github.com/libp2p/go-libp2p/core/host +68 / 2.9KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/core/metrics + + +github.com/libp2p/go-libp2p/core/metrics +171 / 6.2KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/core/metrics + + + + + +github.com/libp2p/go-libp2p/core/network + + +github.com/libp2p/go-libp2p/core/network +847 / 35.3KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/core/peer + + +github.com/libp2p/go-libp2p/core/peer +592 / 18.5KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/peerstore + + +github.com/libp2p/go-libp2p/core/peerstore +221 / 10.0KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/core/transport + + +github.com/libp2p/go-libp2p/core/transport +150 / 6.1KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/host/autonat + + +github.com/libp2p/go-libp2p/p2p/host/autonat +1179 / 36.5KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/host/autonat + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay + + +github.com/libp2p/go-libp2p/p2p/host/autorelay +1494 / 45.9KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/host/autorelay + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic + + +github.com/libp2p/go-libp2p/p2p/host/basic +1177 / 38.1KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/host/basic + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem +878 / 22.8KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem + + + + + +github.com/libp2p/go-libp2p/p2p/host/resource-manager + + +github.com/libp2p/go-libp2p/p2p/host/resource-manager +3865 / 112.7KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/host/resource-manager + + + + + +github.com/libp2p/go-libp2p/p2p/muxer/yamux + + +github.com/libp2p/go-libp2p/p2p/muxer/yamux +136 / 3.8KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/muxer/yamux + + + + + +github.com/libp2p/go-libp2p/p2p/net/connmgr + + +github.com/libp2p/go-libp2p/p2p/net/connmgr +991 / 29.1KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/net/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm + + +github.com/libp2p/go-libp2p/p2p/net/swarm +3530 / 113.0KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/net/swarm + + + + + +github.com/libp2p/go-libp2p/p2p/net/upgrader + + +github.com/libp2p/go-libp2p/p2p/net/upgrader +558 / 16.7KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/net/upgrader + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto +59 / 1.7KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay +1017 / 30.4KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/holepunch + + +github.com/libp2p/go-libp2p/p2p/protocol/holepunch +1006 / 30.9KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/protocol/holepunch + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/identify + + +github.com/libp2p/go-libp2p/p2p/protocol/identify +1653 / 54.9KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/protocol/identify + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/ping + + +github.com/libp2p/go-libp2p/p2p/protocol/ping +165 / 3.9KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/protocol/ping + + + + + +github.com/libp2p/go-libp2p/p2p/security/noise + + +github.com/libp2p/go-libp2p/p2p/security/noise +740 / 26.3KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/security/noise + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls + + +github.com/libp2p/go-libp2p/p2p/security/tls +453 / 14.7KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/security/tls + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic + + +github.com/libp2p/go-libp2p/p2p/transport/quic +734 / 22.2KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/transport/quic + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quicreuse + + +github.com/libp2p/go-libp2p/p2p/transport/quicreuse +869 / 24.6KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/transport/quicreuse + + + + + +github.com/libp2p/go-libp2p/p2p/transport/tcp + + +github.com/libp2p/go-libp2p/p2p/transport/tcp +503 / 15.2KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/transport/tcp + + + + + +github.com/libp2p/go-libp2p/p2p/transport/websocket + + +github.com/libp2p/go-libp2p/p2p/transport/websocket +634 / 16.8KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/transport/websocket + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webtransport + + +github.com/libp2p/go-libp2p/p2p/transport/webtransport +1137 / 35.8KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/libp2p/go-libp2p/p2p/transport/webtransport + + + + + +github.com/multiformats/go-multiaddr + + +github.com/multiformats/go-multiaddr +1709 / 44.6KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/multiformats/go-multiaddr + + + + + +github.com/multiformats/go-multiaddr-dns + + +github.com/multiformats/go-multiaddr-dns +340 / 9.8KB + + + + + +github.com/libp2p/go-libp2p:e->github.com/multiformats/go-multiaddr-dns + + + + + +go.uber.org/fx + + +go.uber.org/fx +4578 / 147.9KB + + + + + +github.com/libp2p/go-libp2p:e->go.uber.org/fx + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/core/event + + +github.com/libp2p/go-libp2p/core/event +301 / 12.5KB + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/core/metrics + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/core/routing + + +github.com/libp2p/go-libp2p/core/routing +285 / 9.0KB + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/core/routing + + + + + +github.com/libp2p/go-libp2p/core/sec + + +github.com/libp2p/go-libp2p/core/sec +34 / 1.2KB + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/core/sec + + + + + +github.com/libp2p/go-libp2p/core/sec/insecure + + +github.com/libp2p/go-libp2p/core/sec/insecure +193 / 6.4KB + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/core/sec/insecure + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/host/autonat + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/host/autorelay + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/host/basic + + + + + +github.com/libp2p/go-libp2p/p2p/host/blank + + +github.com/libp2p/go-libp2p/p2p/host/blank +191 / 5.8KB + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/host/blank + + + + + +github.com/libp2p/go-libp2p/p2p/host/eventbus + + +github.com/libp2p/go-libp2p/p2p/host/eventbus +549 / 14.7KB + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/host/eventbus + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/host/resource-manager + + + + + +github.com/libp2p/go-libp2p/p2p/host/routed + + +github.com/libp2p/go-libp2p/p2p/host/routed +185 / 6.1KB + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/host/routed + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/net/swarm + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/net/upgrader + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client +666 / 20.9KB + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/protocol/holepunch + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/libp2p/go-libp2p/p2p/transport/quicreuse + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/config:e->github.com/multiformats/go-multiaddr-dns + + + + + +github.com/libp2p/go-libp2p/config:e->go.uber.org/fx + + + + + +go.uber.org/fx/fxevent + + +go.uber.org/fx/fxevent +628 / 21.7KB + + + + + +github.com/libp2p/go-libp2p/config:e->go.uber.org/fx/fxevent + + + + + +github.com/libp2p/go-libp2p/core + + +github.com/libp2p/go-libp2p/core +41 / 1.3KB + + + + + +github.com/libp2p/go-libp2p/core:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/core:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/core:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/core/canonicallog + + +github.com/libp2p/go-libp2p/core/canonicallog +49 / 2.3KB + + + + + +github.com/libp2p/go-libp2p/core/canonicallog:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/core/canonicallog:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/canonicallog:e->github.com/multiformats/go-multiaddr + + + + + +github.com/multiformats/go-multiaddr/net + + +github.com/multiformats/go-multiaddr/net +1149 / 32.8KB + + + + + +github.com/libp2p/go-libp2p/core/canonicallog:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/core/connmgr:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/core/connmgr:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/connmgr:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/core/discovery + + +github.com/libp2p/go-libp2p/core/discovery +56 / 1.6KB + + + + + +github.com/libp2p/go-libp2p/core/discovery:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/event:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/core/event:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/event:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/core/host:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/core/host:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/core/host:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/core/host:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/host:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/core/host:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/core/metrics:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/network:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/network:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/core/network:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/core/network/mocks + + +github.com/libp2p/go-libp2p/core/network/mocks +683 / 28.2KB + + + + + +github.com/libp2p/go-libp2p/core/network/mocks:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/core/network/mocks:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/network/mocks:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/core/peer:e->github.com/ipfs/go-cid + + + + + +github.com/libp2p/go-libp2p/core/peer:e->github.com/multiformats/go-multiaddr + + + + + +github.com/multiformats/go-multicodec + + +github.com/multiformats/go-multicodec +3309 / 111.7KB + + + + + +github.com/libp2p/go-libp2p/core/peer:e->github.com/multiformats/go-multicodec + + + + + +github.com/libp2p/go-libp2p/core/peer:e->github.com/multiformats/go-multihash + + + + + +github.com/libp2p/go-libp2p/core/peerstore:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/peerstore:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/core/routing:e->github.com/ipfs/go-cid + + + + + +github.com/libp2p/go-libp2p/core/routing:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/sec:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/core/sec:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/sec/insecure:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/core/sec/insecure:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/sec/insecure:e->github.com/libp2p/go-libp2p/core/sec + + + + + +github.com/libp2p/go-libp2p/core/test + + +github.com/libp2p/go-libp2p/core/test +206 / 4.9KB + + + + + +github.com/libp2p/go-libp2p/core/test:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/test:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/core/test:e->github.com/multiformats/go-multihash + + + + + +testing + + +testing +4232 / 137.4KB + + + + + +github.com/libp2p/go-libp2p/core/test:e->testing + + + + + +github.com/libp2p/go-libp2p/core/transport:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/core/transport:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/core/transport:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/core/transport:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/backoff + + +github.com/libp2p/go-libp2p/p2p/discovery/backoff +561 / 16.1KB + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/backoff:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/backoff:e->github.com/libp2p/go-libp2p/core/discovery + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/backoff:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/backoff:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/backoff:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/mdns + + +github.com/libp2p/go-libp2p/p2p/discovery/mdns +181 / 4.3KB + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/mdns:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/mdns:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/mdns:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/mdns:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/mdns:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/mocks + + +github.com/libp2p/go-libp2p/p2p/discovery/mocks +94 / 2.3KB + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/mocks:e->github.com/libp2p/go-libp2p/core/discovery + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/mocks:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/mocks:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/routing + + +github.com/libp2p/go-libp2p/p2p/discovery/routing +89 / 2.7KB + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/routing:e->github.com/ipfs/go-cid + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/routing:e->github.com/libp2p/go-libp2p/core/discovery + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/routing:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/routing:e->github.com/libp2p/go-libp2p/core/routing + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/routing:e->github.com/multiformats/go-multihash + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/util + + +github.com/libp2p/go-libp2p/p2p/discovery/util +47 / 1.2KB + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/util:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/util:e->github.com/libp2p/go-libp2p/core/discovery + + + + + +github.com/libp2p/go-libp2p/p2p/discovery/util:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/host/autonat:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/host/autonat:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/host/autonat:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/host/autonat:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/host/autonat:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/host/autonat:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/host/autonat:e->github.com/libp2p/go-libp2p/p2p/host/eventbus + + + + + +github.com/libp2p/go-libp2p/p2p/metricshelper + + +github.com/libp2p/go-libp2p/p2p/metricshelper +75 / 1.9KB + + + + + +github.com/libp2p/go-libp2p/p2p/host/autonat:e->github.com/libp2p/go-libp2p/p2p/metricshelper + + + + + +github.com/libp2p/go-libp2p/p2p/host/autonat:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/host/autonat:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay:e->github.com/libp2p/go-libp2p/p2p/host/basic + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay:e->github.com/libp2p/go-libp2p/p2p/host/eventbus + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay:e->github.com/libp2p/go-libp2p/p2p/metricshelper + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay:e->github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay:e->github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/host/autorelay:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/p2p/host/autonat + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/p2p/host/eventbus + + + + + +github.com/libp2p/go-libp2p/p2p/host/pstoremanager + + +github.com/libp2p/go-libp2p/p2p/host/pstoremanager +115 / 3.0KB + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/p2p/host/pstoremanager + + + + + +github.com/libp2p/go-libp2p/p2p/host/relaysvc + + +github.com/libp2p/go-libp2p/p2p/host/relaysvc +85 / 2.0KB + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/p2p/host/relaysvc + + + + + +github.com/libp2p/go-libp2p/p2p/net/nat + + +github.com/libp2p/go-libp2p/p2p/net/nat +223 / 6.8KB + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/p2p/net/nat + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/p2p/protocol/holepunch + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/p2p/protocol/identify + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/p2p/protocol/ping + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/libp2p/go-libp2p/p2p/transport/webtransport + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/multiformats/go-multiaddr-dns + + + + + +github.com/libp2p/go-libp2p/p2p/host/basic:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/host/blank:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/host/blank:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/host/blank:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/host/blank:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/host/blank:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/host/blank:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/host/blank:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/host/blank:e->github.com/libp2p/go-libp2p/p2p/host/eventbus + + + + + +github.com/libp2p/go-libp2p/p2p/host/blank:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/host/eventbus:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/host/eventbus:e->github.com/libp2p/go-libp2p/p2p/metricshelper + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore + + +github.com/libp2p/go-libp2p/p2p/host/peerstore +68 / 1.7KB + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoreds + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoreds +1502 / 47.1KB + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoreds:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoreds:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoreds:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoreds:e->github.com/libp2p/go-libp2p/p2p/host/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoreds:e->github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoreds:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem:e->github.com/libp2p/go-libp2p/p2p/host/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem:e->github.com/multiformats/go-multiaddr + + + + + +github.com/multiformats/go-multiaddr-fmt + + +github.com/multiformats/go-multiaddr-fmt +144 / 3.4KB + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem:e->github.com/multiformats/go-multiaddr-fmt + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/test + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/test +1240 / 38.2KB + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/test:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/test:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/test:e->github.com/libp2p/go-libp2p/core/test + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/test:e->github.com/multiformats/go-multiaddr + + + + + +github.com/stretchr/testify/require + + +github.com/stretchr/testify/require +3450 / 120.1KB + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/test:e->github.com/stretchr/testify/require + + + + + +github.com/libp2p/go-libp2p/p2p/host/peerstore/test:e->testing + + + + + +github.com/libp2p/go-libp2p/p2p/host/pstoremanager:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/host/pstoremanager:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/host/pstoremanager:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/host/pstoremanager:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/host/pstoremanager:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/host/pstoremanager:e->github.com/libp2p/go-libp2p/p2p/host/eventbus + + + + + +github.com/libp2p/go-libp2p/p2p/host/relaysvc:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/host/relaysvc:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/host/relaysvc:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/host/relaysvc:e->github.com/libp2p/go-libp2p/p2p/host/eventbus + + + + + +github.com/libp2p/go-libp2p/p2p/host/relaysvc:e->github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay + + + + + +github.com/libp2p/go-libp2p/p2p/host/resource-manager:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/host/resource-manager:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/host/resource-manager:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/host/resource-manager:e->github.com/libp2p/go-libp2p/p2p/metricshelper + + + + + +github.com/libp2p/go-libp2p/p2p/host/resource-manager:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/host/resource-manager:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/host/resource-manager/obs + + +github.com/libp2p/go-libp2p/p2p/host/resource-manager/obs +14 / 616B + + + + + +github.com/libp2p/go-libp2p/p2p/host/resource-manager/obs:e->github.com/libp2p/go-libp2p/p2p/host/resource-manager + + + + + +github.com/libp2p/go-libp2p/p2p/host/routed:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/host/routed:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/host/routed:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/host/routed:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/host/routed:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/host/routed:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/host/routed:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/host/routed:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/http + + +github.com/libp2p/go-libp2p/p2p/http +729 / 25.5KB + + + + + +github.com/libp2p/go-libp2p/p2p/http:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/http:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/http:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/http:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/http:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/net/gostream + + +github.com/libp2p/go-libp2p/p2p/net/gostream +125 / 3.9KB + + + + + +github.com/libp2p/go-libp2p/p2p/http:e->github.com/libp2p/go-libp2p/p2p/net/gostream + + + + + +github.com/libp2p/go-libp2p/p2p/http:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/metricshelper:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/metricshelper:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/muxer/testsuite + + +github.com/libp2p/go-libp2p/p2p/muxer/testsuite +566 / 12.9KB + + + + + +github.com/libp2p/go-libp2p/p2p/muxer/testsuite:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/muxer/testsuite:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/muxer/testsuite:e->github.com/stretchr/testify/require + + + + + +github.com/libp2p/go-libp2p/p2p/muxer/testsuite:e->testing + + + + + +github.com/libp2p/go-libp2p/p2p/muxer/yamux:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/net/conngater + + +github.com/libp2p/go-libp2p/p2p/net/conngater +280 / 8.2KB + + + + + +github.com/libp2p/go-libp2p/p2p/net/conngater:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/net/conngater:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/net/conngater:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/net/conngater:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/net/conngater:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/net/conngater:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/net/connmgr:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/net/connmgr:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/net/connmgr:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/net/connmgr:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/net/connmgr:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/net/gostream:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/net/gostream:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/net/gostream:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock + + +github.com/libp2p/go-libp2p/p2p/net/mock +1484 / 39.3KB + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock:e->github.com/libp2p/go-libp2p/p2p/host/basic + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock:e->github.com/libp2p/go-libp2p/p2p/host/eventbus + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock:e->github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/net/mock:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/net/nat:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/net/reuseport + + +github.com/libp2p/go-libp2p/p2p/net/reuseport +313 / 9.1KB + + + + + +github.com/libp2p/go-libp2p/p2p/net/reuseport:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/net/reuseport:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/net/reuseport:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/libp2p/go-libp2p/core/canonicallog + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/libp2p/go-libp2p/core/metrics + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/libp2p/go-libp2p/p2p/metricshelper + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/multiformats/go-multiaddr-dns + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/multiformats/go-multiaddr-fmt + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing +222 / 7.0KB + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/core/metrics + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/core/sec + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/core/sec/insecure + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/p2p/host/eventbus + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/p2p/muxer/yamux + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/p2p/net/swarm + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/p2p/net/upgrader + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/p2p/transport/quic + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/p2p/transport/quicreuse + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/libp2p/go-libp2p/p2p/transport/tcp + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->github.com/stretchr/testify/require + + + + + +github.com/libp2p/go-libp2p/p2p/net/swarm/testing:e->testing + + + + + +github.com/libp2p/go-libp2p/p2p/net/upgrader:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/net/upgrader:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/net/upgrader:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/net/upgrader:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/net/upgrader:e->github.com/libp2p/go-libp2p/core/sec + + + + + +github.com/libp2p/go-libp2p/p2p/net/upgrader:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/net/upgrader:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client:e->github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util +89 / 2.5KB + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client:e->github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay:e->github.com/libp2p/go-libp2p/p2p/metricshelper + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay:e->github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay:e->github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/holepunch:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/holepunch:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/holepunch:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/holepunch:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/holepunch:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/holepunch:e->github.com/libp2p/go-libp2p/p2p/host/eventbus + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/holepunch:e->github.com/libp2p/go-libp2p/p2p/metricshelper + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/holepunch:e->github.com/libp2p/go-libp2p/p2p/protocol/identify + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/holepunch:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/holepunch:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/identify:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/identify:e->github.com/libp2p/go-libp2p/core/event + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/identify:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/identify:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/identify:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/identify:e->github.com/libp2p/go-libp2p/core/peerstore + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/identify:e->github.com/libp2p/go-libp2p/p2p/host/eventbus + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/identify:e->github.com/libp2p/go-libp2p/p2p/metricshelper + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/identify:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/identify:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/ping:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/ping:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/ping:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/protocol/ping:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/security/noise:e->github.com/libp2p/go-libp2p/core/canonicallog + + + + + +github.com/libp2p/go-libp2p/p2p/security/noise:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/security/noise:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/security/noise:e->github.com/libp2p/go-libp2p/core/sec + + + + + +github.com/libp2p/go-libp2p/p2p/security/noise:e->github.com/libp2p/go-libp2p/p2p/net/upgrader + + + + + +github.com/libp2p/go-libp2p/p2p/security/noise:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls:e->github.com/libp2p/go-libp2p/core/canonicallog + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls:e->github.com/libp2p/go-libp2p/core/sec + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls:e->github.com/libp2p/go-libp2p/p2p/net/upgrader + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls/cmd + + +github.com/libp2p/go-libp2p/p2p/security/tls/cmd +28 / 560B + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls/cmd/tlsdiag + + +github.com/libp2p/go-libp2p/p2p/security/tls/cmd/tlsdiag +138 / 3.6KB + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls/cmd:e->github.com/libp2p/go-libp2p/p2p/security/tls/cmd/tlsdiag + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls/cmd/tlsdiag:e->flag + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls/cmd/tlsdiag:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/security/tls/cmd/tlsdiag:e->github.com/libp2p/go-libp2p/p2p/security/tls + + + + + +github.com/libp2p/go-libp2p/p2p/test/resource-manager + + +github.com/libp2p/go-libp2p/p2p/test/resource-manager +220 / 4.9KB + + + + + +github.com/libp2p/go-libp2p/p2p/test/resource-manager:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/test/resource-manager:e->github.com/libp2p/go-libp2p/core/host + + + + + +github.com/libp2p/go-libp2p/p2p/test/resource-manager:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/test/resource-manager:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic:e->github.com/libp2p/go-libp2p/p2p/security/tls + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic:e->github.com/libp2p/go-libp2p/p2p/transport/quicreuse + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic:e->github.com/multiformats/go-multiaddr-fmt + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/client + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/client +71 / 1.6KB + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/client:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/client:e->github.com/libp2p/go-libp2p/p2p/transport/quic + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/client:e->github.com/libp2p/go-libp2p/p2p/transport/quicreuse + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/client:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/server + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/server +78 / 1.8KB + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/server:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/server:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/server:e->github.com/libp2p/go-libp2p/p2p/transport/quic + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/server:e->github.com/libp2p/go-libp2p/p2p/transport/quicreuse + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quic/cmd/server:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quicreuse:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quicreuse:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quicreuse:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/transport/quicreuse:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/transport/tcp:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/transport/tcp:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/transport/tcp:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/transport/tcp:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/transport/tcp:e->github.com/libp2p/go-libp2p/p2p/net/reuseport + + + + + +github.com/libp2p/go-libp2p/p2p/transport/tcp:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/transport/tcp:e->github.com/multiformats/go-multiaddr-fmt + + + + + +github.com/libp2p/go-libp2p/p2p/transport/tcp:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/transport/testsuite + + +github.com/libp2p/go-libp2p/p2p/transport/testsuite +693 / 14.8KB + + + + + +github.com/libp2p/go-libp2p/p2p/transport/testsuite:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/transport/testsuite:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/transport/testsuite:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/transport/testsuite:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/transport/testsuite:e->testing + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc +1792 / 56.3KB + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc:e->github.com/libp2p/go-libp2p/core/sec + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc:e->github.com/libp2p/go-libp2p/p2p/security/noise + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc/udpmux + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc/udpmux +350 / 9.9KB + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc:e->github.com/libp2p/go-libp2p/p2p/transport/webrtc/udpmux + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc:e->github.com/multiformats/go-multiaddr-fmt + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc:e->github.com/multiformats/go-multihash + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webrtc/udpmux:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/transport/websocket:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/transport/websocket:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/transport/websocket:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/transport/websocket:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/transport/websocket:e->github.com/multiformats/go-multiaddr-fmt + + + + + +github.com/libp2p/go-libp2p/p2p/transport/websocket:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webtransport:e->github.com/ipfs/go-log/v2 + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webtransport:e->github.com/libp2p/go-libp2p/core/connmgr + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webtransport:e->github.com/libp2p/go-libp2p/core/network + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webtransport:e->github.com/libp2p/go-libp2p/core/peer + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webtransport:e->github.com/libp2p/go-libp2p/core/transport + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webtransport:e->github.com/libp2p/go-libp2p/p2p/security/noise + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webtransport:e->github.com/libp2p/go-libp2p/p2p/transport/quicreuse + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webtransport:e->github.com/multiformats/go-multiaddr + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webtransport:e->github.com/multiformats/go-multiaddr/net + + + + + +github.com/libp2p/go-libp2p/p2p/transport/webtransport:e->github.com/multiformats/go-multihash + + + + + +github.com/multiformats/go-multiaddr:e->github.com/ipfs/go-cid + + + + + +github.com/multiformats/go-multiaddr:e->github.com/multiformats/go-multihash + + + + + +github.com/multiformats/go-multiaddr-dns:e->github.com/multiformats/go-multiaddr + + + + + +github.com/multiformats/go-multiaddr-fmt:e->github.com/multiformats/go-multiaddr + + + + + +github.com/multiformats/go-multiaddr/net:e->github.com/multiformats/go-multiaddr + + + + + +github.com/multiformats/go-multicodec:e->flag + + + + + +github.com/multiformats/go-multihash/register/all + + +github.com/multiformats/go-multihash/register/all +20 / 0.9KB + + + + + +github.com/multiformats/go-multihash:e->github.com/multiformats/go-multihash/register/all + + + + + +github.com/multiformats/go-multihash/register/blake3 + + +github.com/multiformats/go-multihash/register/blake3 +26 / 631B + + + + + +github.com/multiformats/go-multihash/register/all:e->github.com/multiformats/go-multihash/register/blake3 + + + + + +lukechampine.com/blake3 + + +lukechampine.com/blake3 +633 / 19.9KB + + + + + +github.com/multiformats/go-multihash/register/blake3:e->lukechampine.com/blake3 + + + + + +github.com/stretchr/testify/assert + + +github.com/stretchr/testify/assert +4728 / 171.7KB + + + + + +net/http/httptest + + +net/http/httptest +661 / 19.9KB + + + + + +github.com/stretchr/testify/assert:e->net/http/httptest + + + + + +github.com/stretchr/testify/require:e->github.com/stretchr/testify/assert + + + + + +go.uber.org/fx:e->go.uber.org/fx/fxevent + + + + + +go.uber.org/fx/internal/fxlog + + +go.uber.org/fx/internal/fxlog +105 / 3.8KB + + + + + +go.uber.org/fx:e->go.uber.org/fx/internal/fxlog + + + + + +go.uber.org/fx/internal/lifecycle + + +go.uber.org/fx/internal/lifecycle +351 / 10.3KB + + + + + +go.uber.org/fx:e->go.uber.org/fx/internal/lifecycle + + + + + +go.uber.org/fx/fxevent:e->go.uber.org/zap + + + + + +go.uber.org/fx/internal/fxlog:e->go.uber.org/fx/fxevent + + + + + +go.uber.org/fx/internal/lifecycle:e->go.uber.org/fx/fxevent + + + + + +go.uber.org/zap:e->flag + + + + + +lukechampine.com/blake3:e->github.com/klauspost/cpuid/v2 + + + + + +net/http/httptest:e->flag + + + + + +testing:e->flag + + + + + diff --git a/server/commitment_mode.go b/server/commitment_mode.go new file mode 100644 index 00000000..9edda592 --- /dev/null +++ b/server/commitment_mode.go @@ -0,0 +1,97 @@ +package server + +import ( + "encoding/hex" + "fmt" + "net/http" + + "github.com/Layr-Labs/eigenda-proxy/commitments" +) + +type CommitmentMode string + +const ( + OptimismCommitmentMode CommitmentMode = "optimism" + SimpleCommitmentMode CommitmentMode = "simple" +) + +func StringToCommitmentMode(s string) (CommitmentMode, error) { + switch s { + case string(OptimismCommitmentMode): + return OptimismCommitmentMode, nil + case string(SimpleCommitmentMode): + return SimpleCommitmentMode, nil + default: + return "", fmt.Errorf("unknown commitment mode: %s", s) + } +} + +func StringToCommitment(key string, c CommitmentMode) ([]byte, error) { + if len(key) <= 2 { + return nil, fmt.Errorf("commitment is empty") + } + + if key[:2] != "0x" { + return nil, fmt.Errorf("commitment parameter does not have 0x prefix") + } + + b, err := hex.DecodeString(key[2:]) + if err != nil { + return nil, err + } + + switch c { + case OptimismCommitmentMode: + var comm commitments.OPCommitment + err = comm.Unmarshal(b) + if err != nil { + return nil, err + } + if !comm.IsGenericCommitment() { + return nil, fmt.Errorf("commitment is not a OP DA service commitment") + } + daComm := comm.MustGenericCommitmentValue() + if !daComm.IsEigenDA() { + return nil, fmt.Errorf("commitment is not an EigenDA OP DA service commitment") + } + eigendaComm := daComm.MustEigenDAValue() + if !eigendaComm.IsCertV0() { + return nil, fmt.Errorf("commitment is not a supported EigenDA cert encoding") + } + return eigendaComm.MustCertV0Value(), nil + case SimpleCommitmentMode: + var eigendaComm commitments.EigenDACommitment + err = eigendaComm.Unmarshal(b) + if err != nil { + return nil, err + } + if !eigendaComm.IsCertV0() { + return nil, fmt.Errorf("commitment is not a supported EigenDA cert encoding") + } + return eigendaComm.MustCertV0Value(), nil + default: + return nil, fmt.Errorf("unknown commitment type") + } +} + +func EncodeCommitment(s []byte, c CommitmentMode) ([]byte, error) { + switch c { + case OptimismCommitmentMode: + comm := commitments.GenericCommitment(commitments.OptimismEigenDACommitment(commitments.EigenDACertV0(s))) + return comm.Marshal() + case SimpleCommitmentMode: + comm := commitments.EigenDACertV0(s) + return comm.Marshal() + default: + return nil, fmt.Errorf("unknown commitment type") + } +} + +func ReadCommitmentMode(r *http.Request) (CommitmentMode, error) { + query := r.URL.Query() + key := query.Get(CommitmentModeKey) + if key == "" { // default + return OptimismCommitmentMode, nil + } + return StringToCommitmentMode(key) +} diff --git a/eigenda/config.go b/server/config.go similarity index 84% rename from eigenda/config.go rename to server/config.go index db61e493..d32620b8 100644 --- a/eigenda/config.go +++ b/server/config.go @@ -1,4 +1,4 @@ -package eigenda +package server import ( "fmt" @@ -6,7 +6,7 @@ import ( "runtime" "time" - "github.com/Layr-Labs/eigenda-proxy/common" + "github.com/Layr-Labs/eigenda-proxy/utils" "github.com/Layr-Labs/eigenda-proxy/verify" "github.com/Layr-Labs/eigenda/api/clients" "github.com/Layr-Labs/eigenda/api/clients/codecs" @@ -28,10 +28,12 @@ const ( PutBlobEncodingVersionFlagName = "eigenda-put-blob-encoding-version" DisablePointVerificationModeFlagName = "eigenda-disable-point-verification-mode" // Kzg flags - G1PathFlagName = "eigenda-g1-path" - G2TauFlagName = "eigenda-g2-tau-path" - CachePathFlagName = "eigenda-cache-path" - MaxBlobLengthFlagName = "eigenda-max-blob-length" + G1PathFlagName = "eigenda-g1-path" + G2TauFlagName = "eigenda-g2-tau-path" + CachePathFlagName = "eigenda-cache-path" + MaxBlobLengthFlagName = "eigenda-max-blob-length" + MemstoreFlagName = "memstore.enabled" + MemstoreExpirationFlagName = "memstore.expiration" ) const BytesPerSymbol = 31 @@ -61,11 +63,15 @@ type Config struct { maxBlobLengthBytes uint64 G2PowerOfTauPath string + + // Memstore Config params + MemstoreEnabled bool + MemstoreBlobExpiration time.Duration } func (c *Config) GetMaxBlobLength() (uint64, error) { if c.maxBlobLengthBytes == 0 { - numBytes, err := common.ParseBytesAmount(c.MaxBlobLength) + numBytes, err := utils.ParseBytesAmount(c.MaxBlobLength) if err != nil { return 0, err } @@ -128,12 +134,14 @@ func ReadConfig(ctx *cli.Context) Config { PutBlobEncodingVersion: codecs.BlobEncodingVersion(ctx.Uint(PutBlobEncodingVersionFlagName)), DisablePointVerificationMode: ctx.Bool(DisablePointVerificationModeFlagName), }, - G1Path: ctx.String(G1PathFlagName), - G2PowerOfTauPath: ctx.String(G2TauFlagName), - CacheDir: ctx.String(CachePathFlagName), - MaxBlobLength: ctx.String(MaxBlobLengthFlagName), - SvcManagerAddr: ctx.String(SvcManagerAddrFlagName), - EthRPC: ctx.String(EthRPCFlagName), + G1Path: ctx.String(G1PathFlagName), + G2PowerOfTauPath: ctx.String(G2TauFlagName), + CacheDir: ctx.String(CachePathFlagName), + MaxBlobLength: ctx.String(MaxBlobLengthFlagName), + SvcManagerAddr: ctx.String(SvcManagerAddrFlagName), + EthRPC: ctx.String(EthRPCFlagName), + MemstoreEnabled: ctx.Bool(MemstoreFlagName), + MemstoreBlobExpiration: ctx.Duration(MemstoreExpirationFlagName), } return cfg } @@ -234,5 +242,16 @@ func CLIFlags(envPrefix string) []cli.Flag { Usage: "Deployed EigenDA service manager address.", EnvVars: prefixEnvVars("SERVICE_MANAGER_ADDR"), }, + &cli.BoolFlag{ + Name: MemstoreFlagName, + Usage: "Whether to use mem-store for DA logic.", + EnvVars: []string{"MEMSTORE_ENABLED"}, + }, + &cli.DurationFlag{ + Name: MemstoreExpirationFlagName, + Usage: "Duration that a mem-store blob/commitment pair are allowed to live.", + Value: 25 * time.Minute, + EnvVars: []string{"MEMSTORE_EXPIRATION"}, + }, } } diff --git a/server/domain_type.go b/server/domain_type.go new file mode 100644 index 00000000..d161e0c8 --- /dev/null +++ b/server/domain_type.go @@ -0,0 +1,56 @@ +package server + +import ( + "fmt" + "net/http" +) + +var ( + ErrInvalidDomainType = fmt.Errorf("invalid domain type") +) + +// DomainType is a enumeration type for the different data domains for which a +// blob can exist between +type DomainType uint8 + +const ( + BinaryDomain DomainType = iota + PolyDomain + UnknownDomain +) + +func (d DomainType) String() string { + switch d { + case BinaryDomain: + return "binary" + case PolyDomain: + return "polynomial" + default: + return "unknown" + } +} + +func StrToDomainType(s string) DomainType { + switch s { + case "binary": + return BinaryDomain + case "polynomial": + return PolyDomain + default: + return UnknownDomain + } +} + +func ReadDomainFilter(r *http.Request) (DomainType, error) { + query := r.URL.Query() + key := query.Get(DomainFilterKey) + if key == "" { // default + return BinaryDomain, nil + } + dt := StrToDomainType(key) + if dt == UnknownDomain { + return UnknownDomain, ErrInvalidDomainType + } + + return dt, nil +} diff --git a/store/eigenda.go b/server/eigenda_store.go similarity index 92% rename from store/eigenda.go rename to server/eigenda_store.go index b45eed5e..6b5066e4 100644 --- a/store/eigenda.go +++ b/server/eigenda_store.go @@ -1,10 +1,9 @@ -package store +package server import ( "context" "fmt" - "github.com/Layr-Labs/eigenda-proxy/common" "github.com/Layr-Labs/eigenda-proxy/verify" "github.com/Layr-Labs/eigenda/api/clients" "github.com/ethereum/go-ethereum/rlp" @@ -29,8 +28,8 @@ func NewEigenDAStore(ctx context.Context, client *clients.EigenDAClient, v *veri // Get fetches a blob from DA using certificate fields and verifies blob // against commitment to ensure data is valid and non-tampered. -func (e EigenDAStore) Get(ctx context.Context, key []byte, domain common.DomainType) ([]byte, error) { - var cert common.Certificate +func (e EigenDAStore) Get(ctx context.Context, key []byte, domain DomainType) ([]byte, error) { + var cert verify.Certificate err := rlp.DecodeBytes(key, &cert) if err != nil { return nil, fmt.Errorf("failed to decode DA cert to RLP format: %w", err) @@ -57,9 +56,9 @@ func (e EigenDAStore) Get(ctx context.Context, key []byte, domain common.DomainT } switch domain { - case common.BinaryDomain: + case BinaryDomain: return decodedBlob, nil - case common.PolyDomain: + case PolyDomain: return encodedBlob, nil default: return nil, fmt.Errorf("unexpected domain type: %d", domain) @@ -94,6 +93,6 @@ func (e EigenDAStore) Put(ctx context.Context, value []byte) (comm []byte, err e } // Entries are a no-op for EigenDA Store -func (e EigenDAStore) Stats() *common.Stats { +func (e EigenDAStore) Stats() *Stats { return nil } diff --git a/server/flags.go b/server/flags.go index 7d2579a2..9f6547b6 100644 --- a/server/flags.go +++ b/server/flags.go @@ -5,8 +5,6 @@ import ( "github.com/urfave/cli/v2" - "github.com/Layr-Labs/eigenda-proxy/eigenda" - "github.com/Layr-Labs/eigenda-proxy/store" opservice "github.com/ethereum-optimism/optimism/op-service" oplog "github.com/ethereum-optimism/optimism/op-service/log" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" @@ -47,9 +45,8 @@ var optionalFlags = []cli.Flag{} func init() { optionalFlags = append(optionalFlags, oplog.CLIFlags(EnvVarPrefix)...) - optionalFlags = append(optionalFlags, eigenda.CLIFlags(EnvVarPrefix)...) + optionalFlags = append(optionalFlags, CLIFlags(EnvVarPrefix)...) optionalFlags = append(optionalFlags, opmetrics.CLIFlags(EnvVarPrefix)...) - optionalFlags = append(optionalFlags, store.CLIFlags(EnvVarPrefix)...) Flags = append(requiredFlags, optionalFlags...) } @@ -57,16 +54,14 @@ func init() { var Flags []cli.Flag type CLIConfig struct { - MemStoreCfg store.MemStoreConfig - EigenDAConfig eigenda.Config + EigenDAConfig Config MetricsCfg opmetrics.CLIConfig } func ReadCLIConfig(ctx *cli.Context) CLIConfig { return CLIConfig{ - EigenDAConfig: eigenda.ReadConfig(ctx), + EigenDAConfig: ReadConfig(ctx), MetricsCfg: opmetrics.ReadCLIConfig(ctx), - MemStoreCfg: store.ReadConfig(ctx), } } diff --git a/server/load_store.go b/server/load_store.go index 0947822e..06456054 100644 --- a/server/load_store.go +++ b/server/load_store.go @@ -3,13 +3,13 @@ package server import ( "context" - "github.com/Layr-Labs/eigenda-proxy/store" "github.com/Layr-Labs/eigenda-proxy/verify" "github.com/Layr-Labs/eigenda/api/clients" "github.com/ethereum/go-ethereum/log" ) -func LoadStore(cfg CLIConfig, ctx context.Context, log log.Logger) (store.Store, error) { +func LoadStore(cfg CLIConfig, ctx context.Context, log log.Logger) (Store, error) { + log.Info("Using eigenda backend") daCfg := cfg.EigenDAConfig vCfg := daCfg.VerificationCfg() @@ -29,9 +29,9 @@ func LoadStore(cfg CLIConfig, ctx context.Context, log log.Logger) (store.Store, return nil, err } - if cfg.MemStoreCfg.Enabled { + if cfg.EigenDAConfig.MemstoreEnabled { log.Info("Using memstore backend") - return store.NewMemStore(ctx, &cfg.MemStoreCfg, verifier, log, maxBlobLength) + return NewMemStore(ctx, verifier, log, maxBlobLength, cfg.EigenDAConfig.MemstoreBlobExpiration) } log.Info("Using eigenda backend") @@ -39,7 +39,7 @@ func LoadStore(cfg CLIConfig, ctx context.Context, log log.Logger) (store.Store, if err != nil { return nil, err } - return store.NewEigenDAStore( + return NewEigenDAStore( ctx, client, verifier, diff --git a/store/memory.go b/server/memory_store.go similarity index 71% rename from store/memory.go rename to server/memory_store.go index 9535c83b..77e7d357 100644 --- a/store/memory.go +++ b/server/memory_store.go @@ -1,4 +1,4 @@ -package store +package server import ( "context" @@ -10,61 +10,50 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" - "github.com/Layr-Labs/eigenda-proxy/common" - eigendacommon "github.com/Layr-Labs/eigenda-proxy/common" "github.com/Layr-Labs/eigenda-proxy/verify" "github.com/Layr-Labs/eigenda/api/clients/codecs" - grpccommon "github.com/Layr-Labs/eigenda/api/grpc/common" + "github.com/Layr-Labs/eigenda/api/grpc/common" "github.com/Layr-Labs/eigenda/api/grpc/disperser" "github.com/ethereum/go-ethereum/crypto" - "github.com/urfave/cli/v2" ) const ( - MemStoreFlagName = "memstore.enabled" - ExpirationFlagName = "memstore.expiration" - DefaultPruneInterval = 500 * time.Millisecond ) -type MemStoreConfig struct { - Enabled bool - BlobExpiration time.Duration -} - // MemStore is a simple in-memory store for blobs which uses an expiration // time to evict blobs to best emulate the ephemeral nature of blobs dispersed to // EigenDA operators. type MemStore struct { sync.RWMutex - cfg *MemStoreConfig l log.Logger keyStarts map[string]time.Time store map[string][]byte verifier *verify.Verifier codec codecs.BlobCodec - reads int maxBlobSizeBytes uint64 + blobExpiration time.Duration + reads int } var _ Store = (*MemStore)(nil) // NewMemStore ... constructor -func NewMemStore(ctx context.Context, cfg *MemStoreConfig, verifier *verify.Verifier, l log.Logger, maxBlobSizeBytes uint64) (*MemStore, error) { +func NewMemStore(ctx context.Context, verifier *verify.Verifier, l log.Logger, maxBlobSizeBytes uint64, blobExpiration time.Duration) (*MemStore, error) { store := &MemStore{ - cfg: cfg, l: l, keyStarts: make(map[string]time.Time), store: make(map[string][]byte), verifier: verifier, codec: codecs.NewIFFTCodec(codecs.NewDefaultBlobCodec()), maxBlobSizeBytes: maxBlobSizeBytes, + blobExpiration: blobExpiration, } - if cfg.BlobExpiration != 0 { - l.Info("memstore expiration enabled", "time", cfg.BlobExpiration) + if store.blobExpiration != 0 { + l.Info("memstore expiration enabled", "time", store.blobExpiration) go store.EventLoop(ctx) } @@ -91,7 +80,7 @@ func (e *MemStore) pruneExpired() { defer e.Unlock() for commit, dur := range e.keyStarts { - if time.Since(dur) >= e.cfg.BlobExpiration { + if time.Since(dur) >= e.blobExpiration { delete(e.keyStarts, commit) delete(e.store, commit) @@ -102,12 +91,12 @@ func (e *MemStore) pruneExpired() { } // Get fetches a value from the store. -func (e *MemStore) Get(ctx context.Context, commit []byte, domain eigendacommon.DomainType) ([]byte, error) { +func (e *MemStore) Get(ctx context.Context, commit []byte, domain DomainType) ([]byte, error) { e.reads += 1 - e.Lock() - defer e.Unlock() + e.RLock() + defer e.RUnlock() - var cert common.Certificate + var cert verify.Certificate err := rlp.DecodeBytes(commit, &cert) if err != nil { return nil, fmt.Errorf("failed to decode DA cert to RLP format: %w", err) @@ -126,9 +115,9 @@ func (e *MemStore) Get(ctx context.Context, commit []byte, domain eigendacommon. } switch domain { - case eigendacommon.BinaryDomain: + case BinaryDomain: return e.codec.DecodeBlob(encodedBlob) - case eigendacommon.PolyDomain: + case PolyDomain: return encodedBlob, nil default: return nil, fmt.Errorf("unexpected domain type: %d", domain) @@ -163,9 +152,9 @@ func (e *MemStore) Put(ctx context.Context, value []byte) ([]byte, error) { mockBatchHeaderHash := crypto.Keccak256Hash(entropy) // only filling out commitment fields for now - cert := &common.Certificate{ + cert := &verify.Certificate{ BlobHeader: &disperser.BlobHeader{ - Commitment: &grpccommon.G1Commitment{ + Commitment: &common.G1Commitment{ X: commitment.X.Marshal(), Y: commitment.Y.Marshal(), }, @@ -209,37 +198,11 @@ func (e *MemStore) Put(ctx context.Context, value []byte) ([]byte, error) { return certBytes, nil } -func (e *MemStore) Stats() *common.Stats { +func (e *MemStore) Stats() *Stats { e.RLock() defer e.RUnlock() - return &common.Stats{ + return &Stats{ Entries: len(e.store), Reads: e.reads, } } - -func ReadConfig(ctx *cli.Context) MemStoreConfig { - cfg := MemStoreConfig{ - /* Required Flags */ - Enabled: ctx.Bool(MemStoreFlagName), - BlobExpiration: ctx.Duration(ExpirationFlagName), - } - return cfg -} - -func CLIFlags(envPrefix string) []cli.Flag { - - return []cli.Flag{ - &cli.BoolFlag{ - Name: MemStoreFlagName, - Usage: "Whether to use mem-store for DA logic.", - EnvVars: []string{"MEMSTORE_ENABLED"}, - }, - &cli.DurationFlag{ - Name: ExpirationFlagName, - Usage: "Duration that a blob/commitment pair are allowed to live.", - Value: 25 * time.Minute, - EnvVars: []string{"MEMSTORE_EXPIRATION"}, - }, - } -} diff --git a/store/memory_test.go b/server/memory_store_test.go similarity index 70% rename from store/memory_test.go rename to server/memory_store_test.go index 3af5fb7f..ef63082c 100644 --- a/store/memory_test.go +++ b/server/memory_store_test.go @@ -1,4 +1,4 @@ -package store +package server import ( "context" @@ -6,7 +6,6 @@ import ( "testing" "time" - "github.com/Layr-Labs/eigenda-proxy/common" "github.com/Layr-Labs/eigenda-proxy/verify" "github.com/Layr-Labs/eigenda/encoding/kzg" "github.com/ethereum/go-ethereum/log" @@ -22,9 +21,9 @@ func TestGetSet(t *testing.T) { defer cancel() kzgConfig := &kzg.KzgConfig{ - G1Path: "../e2e/resources/kzg/g1.point", - G2PowerOf2Path: "../e2e/resources/kzg/g2.point.powerOf2", - CacheDir: "../e2e/resources/kzg/SRSTables", + G1Path: "../operator-setup/resources/g1_abbr.point", + G2PowerOf2Path: "../operator-setup/resources/g2_abbr.point.powerOf2", + CacheDir: "../test/resources/SRSTables/", SRSOrder: 3000, SRSNumberToLoad: 3000, NumWorker: uint64(runtime.GOMAXPROCS(0)), @@ -40,13 +39,10 @@ func TestGetSet(t *testing.T) { ms, err := NewMemStore( ctx, - &MemStoreConfig{ - Enabled: true, - BlobExpiration: time.Hour * 1000, - }, verifier, log.New(), 1024*1024*2, + time.Hour*1000, ) assert.NoError(t, err) @@ -55,21 +51,19 @@ func TestGetSet(t *testing.T) { key, err := ms.Put(ctx, expected) assert.NoError(t, err) - actual, err := ms.Get(ctx, key, common.BinaryDomain) + actual, err := ms.Get(ctx, key, BinaryDomain) assert.NoError(t, err) assert.Equal(t, actual, expected) } func TestExpiration(t *testing.T) { - t.Parallel() - ctx, cancel := context.WithCancel(context.Background()) defer cancel() kzgConfig := &kzg.KzgConfig{ - G1Path: "../e2e/resources/kzg/g1.point", - G2PowerOf2Path: "../e2e/resources/kzg/g2.point.powerOf2", - CacheDir: "../e2e/resources/kzg/SRSTables", + G1Path: "../operator-setup/resources/g1_abbr.point", + G2PowerOf2Path: "../operator-setup/resources/g2_abbr.point.powerOf2", + CacheDir: "../test/resources/SRSTables/", SRSOrder: 3000, SRSNumberToLoad: 3000, NumWorker: uint64(runtime.GOMAXPROCS(0)), @@ -85,13 +79,10 @@ func TestExpiration(t *testing.T) { ms, err := NewMemStore( ctx, - &MemStoreConfig{ - Enabled: true, - BlobExpiration: time.Millisecond * 10, - }, verifier, log.New(), 1024*1024*2, + time.Millisecond*10, ) assert.NoError(t, err) @@ -103,7 +94,7 @@ func TestExpiration(t *testing.T) { // sleep 1 second and verify that older blob entries are removed time.Sleep(time.Second * 1) - _, err = ms.Get(ctx, key, common.BinaryDomain) + _, err = ms.Get(ctx, key, BinaryDomain) assert.Error(t, err) } diff --git a/server/server.go b/server/server.go index d8d60206..1e70d939 100644 --- a/server/server.go +++ b/server/server.go @@ -11,10 +11,7 @@ import ( "strconv" "time" - "github.com/Layr-Labs/eigenda-proxy/common" - "github.com/Layr-Labs/eigenda-proxy/eigenda" "github.com/Layr-Labs/eigenda-proxy/metrics" - "github.com/Layr-Labs/eigenda-proxy/store" "github.com/ethereum-optimism/optimism/op-service/rpc" "github.com/ethereum/go-ethereum/log" ) @@ -24,27 +21,41 @@ var ( ) const ( - invalidDomain = "invalid domain type" -) + invalidDomain = "invalid domain type" + invalidCommitmentMode = "invalid commitment mode" -const ( GetRoute = "/get/" PutRoute = "/put/" - DomainFilterKey = "domain" + DomainFilterKey = "domain" + CommitmentModeKey = "commitment_mode" ) +type Store interface { + // Get retrieves the given key if it's present in the key-value data store. + Get(ctx context.Context, key []byte, domain DomainType) ([]byte, error) + // Put inserts the given value into the key-value data store. + Put(ctx context.Context, value []byte) (key []byte, err error) + // Stats returns the current usage metrics of the key-value data store. + Stats() *Stats +} + +type Stats struct { + Entries int + Reads int +} + type Server struct { log log.Logger endpoint string - store store.Store + store Store m metrics.Metricer tls *rpc.ServerTLSConfig httpServer *http.Server listener net.Listener } -func NewServer(host string, port int, store store.Store, log log.Logger, m metrics.Metricer) *Server { +func NewServer(host string, port int, store Store, log log.Logger, m metrics.Metricer) *Server { endpoint := net.JoinHostPort(host, strconv.Itoa(port)) return &Server{ m: m, @@ -147,9 +158,14 @@ func (svr *Server) HandleGet(w http.ResponseWriter, r *http.Request) error { svr.WriteBadRequest(w, invalidDomain) return err } + commitmentType, err := ReadCommitmentMode(r) + if err != nil { + svr.WriteBadRequest(w, invalidCommitmentMode) + return err + } key := path.Base(r.URL.Path) - comm, err := eigenda.StringToCommit(key) + comm, err := StringToCommitment(key, commitmentType) if err != nil { svr.log.Info("failed to decode commitment", "err", err, "key", key) w.WriteHeader(http.StatusBadRequest) @@ -172,6 +188,12 @@ func (svr *Server) HandleGet(w http.ResponseWriter, r *http.Request) error { } func (svr *Server) HandlePut(w http.ResponseWriter, r *http.Request) error { + commitmentType, err := ReadCommitmentMode(r) + if err != nil { + svr.WriteBadRequest(w, invalidCommitmentMode) + return err + } + input, err := io.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -184,8 +206,15 @@ func (svr *Server) HandlePut(w http.ResponseWriter, r *http.Request) error { return err } + comm, err = EncodeCommitment(comm, commitmentType) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return err + } + + fmt.Printf("write cert: %x\n", comm) // write out encoded commitment - svr.WriteResponse(w, eigenda.Commitment.Encode(comm)) + svr.WriteResponse(w, comm) return nil } @@ -210,27 +239,13 @@ func (svr *Server) WriteBadRequest(w http.ResponseWriter, msg string) { w.WriteHeader(http.StatusBadRequest) } -func ReadDomainFilter(r *http.Request) (common.DomainType, error) { - query := r.URL.Query() - key := query.Get(DomainFilterKey) - if key == "" { // default - return common.BinaryDomain, nil - } - dt := common.StrToDomainType(key) - if dt == common.UnknownDomain { - return common.UnknownDomain, common.ErrInvalidDomainType - } - - return dt, nil -} - -func (svr *Server) Store() store.Store { - return svr.store -} - func (svr *Server) Port() int { // read from listener _, portStr, _ := net.SplitHostPort(svr.listener.Addr().String()) port, _ := strconv.Atoi(portStr) return port } + +func (svr *Server) Store() Store { + return svr.store +} diff --git a/store/types.go b/store/types.go deleted file mode 100644 index 5a283d18..00000000 --- a/store/types.go +++ /dev/null @@ -1,16 +0,0 @@ -package store - -import ( - "context" - - "github.com/Layr-Labs/eigenda-proxy/common" -) - -type Store interface { - // Get retrieves the given key if it's present in the key-value data store. - Get(ctx context.Context, key []byte, domain common.DomainType) ([]byte, error) - // Put inserts the given value into the key-value data store. - Put(ctx context.Context, value []byte) (key []byte, err error) - // Stats returns the current usage metrics of the key-value data store. - Stats() *common.Stats -} diff --git a/utils/parse_bytes.go b/utils/parse_bytes.go new file mode 100644 index 00000000..5f9067d1 --- /dev/null +++ b/utils/parse_bytes.go @@ -0,0 +1,70 @@ +package utils + +import ( + "fmt" + "strconv" + "strings" +) + +// Helper utility functions // + +func EqualSlices[P comparable](s1, s2 []P) bool { + if len(s1) != len(s2) { + return false + } + + for i := 0; i < len(s1); i++ { + if s1[i] != s2[i] { + return false + } + } + + return true +} + +func ParseBytesAmount(s string) (uint64, error) { + s = strings.TrimSpace(s) + + // Extract numeric part and unit + numStr := s + unit := "" + for i, r := range s { + if !('0' <= r && r <= '9' || r == '.') { + numStr = s[:i] + unit = s[i:] + break + } + } + + // Convert numeric part to float64 + num, err := strconv.ParseFloat(numStr, 64) + if err != nil { + return 0, fmt.Errorf("invalid numeric value: %v", err) + } + + unit = strings.ToLower(strings.TrimSpace(unit)) + + // Convert to uint64 based on the unit (case-insensitive) + switch unit { + case "b", "": + return uint64(num), nil + case "kib": + return uint64(num * 1024), nil + case "kb": + return uint64(num * 1000), nil // Decimal kilobyte + case "mib": + return uint64(num * 1024 * 1024), nil + case "mb": + return uint64(num * 1000 * 1000), nil // Decimal megabyte + case "gib": + return uint64(num * 1024 * 1024 * 1024), nil + case "gb": + return uint64(num * 1000 * 1000 * 1000), nil // Decimal gigabyte + case "tib": + return uint64(num * 1024 * 1024 * 1024 * 1024), nil + case "tb": + return uint64(num * 1000 * 1000 * 1000 * 1000), nil // Decimal terabyte + default: + return 0, fmt.Errorf("unsupported unit: %s", unit) + } +} diff --git a/common/common_test.go b/utils/parse_bytes_test.go similarity index 92% rename from common/common_test.go rename to utils/parse_bytes_test.go index 11632c89..aab53866 100644 --- a/common/common_test.go +++ b/utils/parse_bytes_test.go @@ -1,15 +1,14 @@ -package common_test +package utils_test import ( "fmt" "testing" - "github.com/Layr-Labs/eigenda-proxy/common" + "github.com/Layr-Labs/eigenda-proxy/utils" ) func TestParseByteAmount(t *testing.T) { t.Parallel() - testCases := []struct { input string expected uint64 @@ -44,7 +43,7 @@ func TestParseByteAmount(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("Input: %s", tc.input), func(t *testing.T) { - got, err := common.ParseBytesAmount(tc.input) + got, err := utils.ParseBytesAmount(tc.input) if (err != nil) != tc.wantErr { t.Errorf("wantErr: %v, got error: %v", tc.wantErr, err) } diff --git a/verify/cert.go b/verify/cert.go index 35314c36..6f55b430 100644 --- a/verify/cert.go +++ b/verify/cert.go @@ -3,12 +3,12 @@ package verify import ( "fmt" - proxy_common "github.com/Layr-Labs/eigenda-proxy/common" binding "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAServiceManager" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" + "golang.org/x/exp/slices" ) // CertVerifier verifies the DA certificate against on-chain EigenDA contracts @@ -52,7 +52,7 @@ func (cv *CertVerifier) VerifyBatch(header *binding.IEigenDAServiceManagerBatchH return err } - equal := proxy_common.EqualSlices(expectedHash[:], actualHash[:]) + equal := slices.Equal(expectedHash[:], actualHash[:]) if !equal { return fmt.Errorf("batch hash mismatch, expected: %x, got: %x", expectedHash, actualHash) } @@ -61,7 +61,7 @@ func (cv *CertVerifier) VerifyBatch(header *binding.IEigenDAServiceManagerBatchH } // VerifyMerkleProof -func (cv *CertVerifier) VerifyMerkleProof(inclusionProof []byte, root []byte, blobIndex uint32, blobHeader proxy_common.BlobHeader) error { +func (cv *CertVerifier) VerifyMerkleProof(inclusionProof []byte, root []byte, blobIndex uint32, blobHeader BlobHeader) error { leafHash, err := HashEncodeBlobHeader(blobHeader) if err != nil { return err @@ -72,7 +72,7 @@ func (cv *CertVerifier) VerifyMerkleProof(inclusionProof []byte, root []byte, bl return err } - equal := proxy_common.EqualSlices(root, generatedRoot.Bytes()) + equal := slices.Equal(root, generatedRoot.Bytes()) if !equal { return fmt.Errorf("root hash mismatch, expected: %x, got: %x", root, generatedRoot) } diff --git a/verify/certificate.go b/verify/certificate.go new file mode 100644 index 00000000..54441a0a --- /dev/null +++ b/verify/certificate.go @@ -0,0 +1,70 @@ +package verify + +import ( + "fmt" + "math/big" + + "github.com/Layr-Labs/eigenda/api/grpc/disperser" +) + +var ( + ErrInvalidDomainType = fmt.Errorf("invalid domain type") +) + +// G1Point struct to represent G1Point in Solidity +type G1Point struct { + X *big.Int + Y *big.Int +} + +// QuorumBlobParam struct to represent QuorumBlobParam in Solidity +type QuorumBlobParam struct { + QuorumNumber uint8 + AdversaryThresholdPercentage uint8 + ConfirmationThresholdPercentage uint8 + ChunkLength uint32 +} + +// BlobHeader struct to represent BlobHeader in Solidity +type BlobHeader struct { + Commitment G1Point + DataLength uint32 + QuorumBlobParams []QuorumBlobParam +} + +type Certificate disperser.BlobInfo + +func (c *Certificate) BlobIndex() uint32 { + return c.BlobVerificationProof.BlobIndex +} + +func (c *Certificate) BatchHeaderRoot() []byte { + return c.BlobVerificationProof.BatchMetadata.BatchHeader.BatchRoot +} + +func (c *Certificate) ReadBlobHeader() BlobHeader { + // parse quorum params + + qps := make([]QuorumBlobParam, len(c.BlobHeader.BlobQuorumParams)) + for i, qp := range c.BlobHeader.BlobQuorumParams { + qps[i] = QuorumBlobParam{ + QuorumNumber: uint8(qp.QuorumNumber), + AdversaryThresholdPercentage: uint8(qp.AdversaryThresholdPercentage), + ConfirmationThresholdPercentage: uint8(qp.ConfirmationThresholdPercentage), + ChunkLength: qp.ChunkLength, + } + } + + return BlobHeader{ + Commitment: G1Point{ + X: new(big.Int).SetBytes(c.BlobHeader.Commitment.X), + Y: new(big.Int).SetBytes(c.BlobHeader.Commitment.Y), + }, + DataLength: c.BlobHeader.DataLength, + QuorumBlobParams: qps, + } +} + +func (c *Certificate) Proof() *disperser.BlobVerificationProof { + return c.BlobVerificationProof +} diff --git a/verify/hasher.go b/verify/hasher.go index ca2353c6..70a4aae6 100644 --- a/verify/hasher.go +++ b/verify/hasher.go @@ -3,7 +3,6 @@ package verify import ( "encoding/binary" - common "github.com/Layr-Labs/eigenda-proxy/common" binding "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAServiceManager" "github.com/ethereum/go-ethereum/accounts/abi" geth_common "github.com/ethereum/go-ethereum/common" @@ -98,7 +97,7 @@ func HashBatchHashedMetadata(batchHeaderHash [32]byte, signatoryRecordHash [32]b } // HashBlobHeader function to hash BlobHeader -func HashBlobHeader(blobHeader common.BlobHeader) (geth_common.Hash, error) { +func HashBlobHeader(blobHeader BlobHeader) (geth_common.Hash, error) { blobHeaderType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ {Name: "commitment", Type: "tuple", Components: []abi.ArgumentMarshaling{ @@ -133,7 +132,7 @@ func HashBlobHeader(blobHeader common.BlobHeader) (geth_common.Hash, error) { } // Function to hash and encode header -func HashEncodeBlobHeader(header common.BlobHeader) (geth_common.Hash, error) { +func HashEncodeBlobHeader(header BlobHeader) (geth_common.Hash, error) { // Hash the BlobHeader blobHash, err := HashBlobHeader(header) if err != nil { diff --git a/verify/hasher_test.go b/verify/hasher_test.go index db4d09e6..3c1c1b5b 100644 --- a/verify/hasher_test.go +++ b/verify/hasher_test.go @@ -4,7 +4,6 @@ import ( "math/big" "testing" - "github.com/Layr-Labs/eigenda-proxy/common" eigenda_common "github.com/Layr-Labs/eigenda/api/grpc/common" "github.com/Layr-Labs/eigenda/api/grpc/disperser" binding "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAServiceManager" @@ -88,7 +87,7 @@ func TestHashBlobHeader(t *testing.T) { }, } - cert := &common.Certificate{ + cert := &Certificate{ BlobHeader: header, } @@ -124,7 +123,7 @@ func TestHashEncodeBlobHeader(t *testing.T) { }, } - cert := &common.Certificate{ + cert := &Certificate{ BlobHeader: header, } diff --git a/verify/verifier.go b/verify/verifier.go index 651d5eb7..8b69d0b7 100644 --- a/verify/verifier.go +++ b/verify/verifier.go @@ -3,16 +3,14 @@ package verify import ( "fmt" - "github.com/Layr-Labs/eigenda/api/grpc/common" "github.com/Layr-Labs/eigenda/encoding" "github.com/consensys/gnark-crypto/ecc/bn254" "github.com/consensys/gnark-crypto/ecc/bn254/fp" "github.com/ethereum/go-ethereum/log" - proxy_common "github.com/Layr-Labs/eigenda-proxy/common" - binding "github.com/Layr-Labs/eigenda/contracts/bindings/EigenDAServiceManager" + "github.com/Layr-Labs/eigenda/api/grpc/common" "github.com/Layr-Labs/eigenda/encoding/kzg" "github.com/Layr-Labs/eigenda/encoding/kzg/prover" "github.com/Layr-Labs/eigenda/encoding/rs" @@ -54,7 +52,7 @@ func NewVerifier(cfg *Config, l log.Logger) (*Verifier, error) { }, nil } -func (v *Verifier) VerifyCert(cert *proxy_common.Certificate) error { +func (v *Verifier) VerifyCert(cert *Certificate) error { if !v.verifyCert { return nil } @@ -119,7 +117,6 @@ func (v *Verifier) VerifyCommitment(expectedCommit *common.G1Commitment, blob [] return err } - // convert to field elements expectedX := &fp.Element{} expectedX.Unmarshal(expectedCommit.X) expectedY := &fp.Element{} @@ -136,7 +133,7 @@ func (v *Verifier) VerifyCommitment(expectedCommit *common.G1Commitment, blob [] } // VerifySecurityParams ensures that returned security parameters are valid -func (v *Verifier) VerifySecurityParams(blobHeader proxy_common.BlobHeader, batchHeader binding.IEigenDAServiceManagerBatchHeader) error { +func (v *Verifier) VerifySecurityParams(blobHeader BlobHeader, batchHeader binding.IEigenDAServiceManagerBatchHeader) error { confirmedQuorums := make(map[uint8]bool) diff --git a/verify/verify_test.go b/verify/verify_test.go index 0e7ccdb3..ac700af7 100644 --- a/verify/verify_test.go +++ b/verify/verify_test.go @@ -16,10 +16,10 @@ func TestCommitmentVerification(t *testing.T) { var data = []byte("inter-subjective and not objective!") - x, err := hex.DecodeString("2fc55f968a2d29d22aebf55b382528d1d9401577c166483e162355b19d8bc446") + x, err := hex.DecodeString("1021d699eac68ce312196d480266e8b82fd5fe5c4311e53313837b64db6df178") assert.NoError(t, err) - y, err := hex.DecodeString("149e2241c21c391e069b9f317710c7f57f31ee88245a5e61f0d294b11acf9aff") + y, err := hex.DecodeString("02efa5a7813233ae13f32bae9b8f48252fa45c1b06a5d70bed471a9bea8d98ae") assert.NoError(t, err) c := &common.G1Commitment{ @@ -28,9 +28,9 @@ func TestCommitmentVerification(t *testing.T) { } kzgConfig := &kzg.KzgConfig{ - G1Path: "../e2e/resources/kzg/g1.point", - G2PowerOf2Path: "../e2e/resources/kzg/g2.point.powerOf2", - CacheDir: "../e2e/resources/kzg/SRSTables", + G1Path: "../operator-setup/resources/g1_abbr.point", + G2PowerOf2Path: "../operator-setup/resources/g2_abbr.point.powerOf2", + CacheDir: "../test/resources/SRSTables/", SRSOrder: 3000, SRSNumberToLoad: 3000, NumWorker: uint64(runtime.GOMAXPROCS(0)),