-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from base-org/initial-archiver
Initial Archiver Service Implementation
- Loading branch information
Showing
37 changed files
with
3,286 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# To get started, copy this file to .env and set your beacon http endpoint | ||
|
||
BLOB_ARCHIVER_L1_BEACON_HTTP=<unset> | ||
BLOB_ARCHIVER_DATA_STORE=s3 | ||
BLOB_ARCHIVER_S3_ENDPOINT=172.17.0.1:9000 | ||
BLOB_ARCHIVER_S3_ACCESS_KEY=admin | ||
BLOB_ARCHIVER_S3_SECRET_ACCESS_KEY=password | ||
BLOB_ARCHIVER_S3_ENDPOINT_HTTPS=false | ||
BLOB_ARCHIVER_S3_BUCKET=blobs | ||
BLOB_ARCHIVER_METRICS_ENABLED=true | ||
BLOB_ARCHIVER_METRICS_PORT=7300 | ||
BLOB_ARCHIVER_ORIGIN_BLOCK=0x0 | ||
|
||
BLOB_API_L1_BEACON_HTTP=<unset> | ||
BLOB_API_DATA_STORE=s3 | ||
BLOB_API_S3_ENDPOINT=172.17.0.1:9000 | ||
BLOB_API_S3_ACCESS_KEY=admin | ||
BLOB_API_S3_SECRET_ACCESS_KEY=password | ||
BLOB_API_S3_ENDPOINT_HTTPS=false | ||
BLOB_API_S3_BUCKET=blobs | ||
BLOB_API_METRICS_ENABLED=true | ||
BLOB_API_METRICS_PORT=7301 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.idea/ | ||
.DS_Store | ||
.swp | ||
.env | ||
api/bin | ||
archiver/bin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
FROM golang:1.21.6-alpine3.19 as builder | ||
|
||
RUN apk add --no-cache make gcc musl-dev linux-headers jq bash | ||
|
||
WORKDIR /app | ||
|
||
COPY ./go.mod ./go.sum /app/ | ||
|
||
RUN go mod download | ||
|
||
COPY . /app | ||
|
||
RUN make build | ||
|
||
FROM alpine:3.19 | ||
|
||
COPY --from=builder /app/archiver/bin/blob-archiver /usr/local/bin/blob-archiver | ||
COPY --from=builder /app/api/bin/blob-api /usr/local/bin/blob-api |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
build: | ||
make -C ./archiver blob-archiver | ||
make -C ./api blob-api | ||
.PHONY: build | ||
|
||
build-docker: | ||
docker-compose build | ||
.PHONY: build-docker | ||
|
||
clean: | ||
make -C ./archiver clean | ||
make -C ./api clean | ||
.PHONY: clean | ||
|
||
test: | ||
make -C ./archiver test | ||
make -C ./api test | ||
.PHONY: test | ||
|
||
integration: | ||
docker-compose down | ||
docker-compose up -d minio create-buckets | ||
RUN_INTEGRATION_TESTS=true go test -v ./... | ||
.PHONY: integration | ||
|
||
fmt: | ||
gofmt -s -w . | ||
.PHONY: fmt | ||
|
||
check: fmt clean build build-docker lint test integration | ||
.PHONY: check | ||
|
||
lint: | ||
golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint --timeout 5m -e "errors.As" -e "errors.Is" ./... | ||
.PHONY: lint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,58 @@ | ||
# Blob Archiver | ||
The Blob Archiver is a service to archive and allow querying of all historical blobs from the beacon chain. It consists | ||
of two components: | ||
|
||
* **Archiver** - Tracks the beacon chain and writes blobs to a storage backend | ||
* **API** - Implements the blob sidecars [API](https://ethereum.github.io/beacon-APIs/#/Beacon/getBlobSidecars), which | ||
allows clients to retrieve blobs from the storage backend | ||
|
||
### Storage | ||
There are currently two supported storage options: | ||
|
||
* On-disk storage - Blobs are written to disk in a directory | ||
* S3 storage - Blobs are written to an S3 bucket | ||
|
||
You can control which storage backend is used by setting the `BLOB_API_DATA_STORE` and `BLOB_ARCHIVER_DATA_STORE` to | ||
either `disk` or `s3`. | ||
|
||
### Data Validity | ||
Currently, the archiver and api do not validate the beacon node's data. Therefore, it's important to either trust the | ||
Beacon node, or validate the data in the client. There is an open [issue](https://github.com/base-org/blob-archiver/issues/4) | ||
to add data validation to the archiver and api. | ||
|
||
### Development | ||
The `Makefile` contains a number of commands for development: | ||
|
||
```sh | ||
# Run the tests | ||
make test | ||
# Run the integration tests (will start a local S3 bucket) | ||
make integration | ||
|
||
# Lint the project | ||
make lint | ||
|
||
# Build the project | ||
make build | ||
|
||
# Check all tests, formatting, building | ||
make check | ||
``` | ||
|
||
#### Run Locally | ||
To run the project locally, you should first copy `.env.template` to `.env` and then modify the environment variables | ||
to your beacon client and storage backend of choice. Then you can run the project with: | ||
|
||
```sh | ||
docker-compose up | ||
``` | ||
|
||
You can see a full list of configuration options by running: | ||
```sh | ||
# API | ||
go run api/cmd/main.go | ||
|
||
# Archiver | ||
go run archiver/cmd/main.go | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
blob-api: | ||
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/blob-api ./cmd/main.go | ||
|
||
clean: | ||
rm -f bin/blob-api | ||
|
||
test: | ||
go test -v -race ./... | ||
|
||
.PHONY: \ | ||
blob-api \ | ||
clean \ | ||
test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/base-org/blob-archiver/api/flags" | ||
"github.com/base-org/blob-archiver/api/metrics" | ||
"github.com/base-org/blob-archiver/api/service" | ||
"github.com/base-org/blob-archiver/common/beacon" | ||
"github.com/base-org/blob-archiver/common/storage" | ||
opservice "github.com/ethereum-optimism/optimism/op-service" | ||
"github.com/ethereum-optimism/optimism/op-service/cliapp" | ||
oplog "github.com/ethereum-optimism/optimism/op-service/log" | ||
"github.com/ethereum/go-ethereum/log" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
var ( | ||
Version = "v0.0.1" | ||
GitCommit = "" | ||
GitDate = "" | ||
) | ||
|
||
func main() { | ||
oplog.SetupDefaults() | ||
|
||
app := cli.NewApp() | ||
app.Flags = cliapp.ProtectFlags(flags.Flags) | ||
app.Version = opservice.FormatVersion(Version, GitCommit, GitDate, "") | ||
app.Name = "blob-api" | ||
app.Usage = "API service for Ethereum blobs" | ||
app.Description = "Service for fetching blob sidecars from a datastore" | ||
app.Action = cliapp.LifecycleCmd(Main()) | ||
|
||
err := app.Run(os.Args) | ||
if err != nil { | ||
log.Crit("Application failed", "message", err) | ||
} | ||
} | ||
|
||
// Main is the entrypoint into the API. | ||
// This method returns a cliapp.LifecycleAction, to create an op-service CLI-lifecycle-managed API Server. | ||
func Main() cliapp.LifecycleAction { | ||
return func(cliCtx *cli.Context, closeApp context.CancelCauseFunc) (cliapp.Lifecycle, error) { | ||
cfg := flags.ReadConfig(cliCtx) | ||
if err := cfg.Check(); err != nil { | ||
return nil, fmt.Errorf("config check failed: %w", err) | ||
} | ||
|
||
l := oplog.NewLogger(oplog.AppOut(cliCtx), cfg.LogConfig) | ||
oplog.SetGlobalLogHandler(l.GetHandler()) | ||
opservice.ValidateEnvVars(flags.EnvVarPrefix, flags.Flags, l) | ||
|
||
m := metrics.NewMetrics() | ||
|
||
storageClient, err := storage.NewStorage(cfg.StorageConfig, l) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to initialize storage: %w", err) | ||
} | ||
|
||
beaconClient, err := beacon.NewBeaconClient(context.Background(), cfg.BeaconConfig) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to initialize beacon client: %w", err) | ||
} | ||
|
||
l.Info("Initializing API Service") | ||
api := service.NewAPI(storageClient, beaconClient, m, l) | ||
return service.NewService(l, api, cfg, m.Registry()), nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package flags | ||
|
||
import ( | ||
"fmt" | ||
|
||
common "github.com/base-org/blob-archiver/common/flags" | ||
oplog "github.com/ethereum-optimism/optimism/op-service/log" | ||
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
type APIConfig struct { | ||
LogConfig oplog.CLIConfig | ||
MetricsConfig opmetrics.CLIConfig | ||
BeaconConfig common.BeaconConfig | ||
StorageConfig common.StorageConfig | ||
|
||
ListenAddr string | ||
} | ||
|
||
func (c APIConfig) Check() error { | ||
if err := c.StorageConfig.Check(); err != nil { | ||
return fmt.Errorf("storage config check failed: %w", err) | ||
} | ||
|
||
if err := c.BeaconConfig.Check(); err != nil { | ||
return fmt.Errorf("beacon config check failed: %w", err) | ||
} | ||
|
||
if c.ListenAddr == "" { | ||
return fmt.Errorf("listen address must be set") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func ReadConfig(cliCtx *cli.Context) APIConfig { | ||
return APIConfig{ | ||
LogConfig: oplog.ReadCLIConfig(cliCtx), | ||
MetricsConfig: opmetrics.ReadCLIConfig(cliCtx), | ||
BeaconConfig: common.NewBeaconConfig(cliCtx), | ||
StorageConfig: common.NewStorageConfig(cliCtx), | ||
ListenAddr: cliCtx.String(ListenAddressFlag.Name), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package flags | ||
|
||
import ( | ||
common "github.com/base-org/blob-archiver/common/flags" | ||
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" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
const EnvVarPrefix = "BLOB_API" | ||
|
||
var ( | ||
ListenAddressFlag = &cli.StringFlag{ | ||
Name: "api-listen-address", | ||
Usage: "The address to list for new requests on", | ||
EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "LISTEN_ADDRESS"), | ||
Value: "0.0.0.0:8000", | ||
} | ||
) | ||
|
||
func init() { | ||
Flags = append(Flags, common.CLIFlags(EnvVarPrefix)...) | ||
Flags = append(Flags, opmetrics.CLIFlags(EnvVarPrefix)...) | ||
Flags = append(Flags, oplog.CLIFlags(EnvVarPrefix)...) | ||
Flags = append(Flags, ListenAddressFlag) | ||
} | ||
|
||
// Flags contains the list of configuration options available to the binary. | ||
var Flags []cli.Flag |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package metrics | ||
|
||
import ( | ||
"github.com/ethereum-optimism/optimism/op-service/metrics" | ||
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" | ||
"github.com/prometheus/client_golang/prometheus" | ||
) | ||
|
||
type BlockIdType string | ||
|
||
var ( | ||
MetricsNamespace = "blob_api" | ||
|
||
BlockIdTypeHash BlockIdType = "hash" | ||
BlockIdTypeBeacon BlockIdType = "beacon" | ||
BlockIdTypeInvalid BlockIdType = "invalid" | ||
) | ||
|
||
type Metricer interface { | ||
Registry() *prometheus.Registry | ||
RecordBlockIdType(t BlockIdType) | ||
} | ||
|
||
type metricsRecorder struct { | ||
// blockIdType records the type of block id used to request a block. This could be a hash (BlockIdTypeHash), or a | ||
// beacon block identifier (BlockIdTypeBeacon). | ||
blockIdType *prometheus.CounterVec | ||
registry *prometheus.Registry | ||
} | ||
|
||
func NewMetrics() Metricer { | ||
registry := opmetrics.NewRegistry() | ||
factory := metrics.With(registry) | ||
return &metricsRecorder{ | ||
registry: registry, | ||
blockIdType: factory.NewCounterVec(prometheus.CounterOpts{ | ||
Namespace: MetricsNamespace, | ||
Name: "block_id_type", | ||
Help: "The type of block id used to request a block", | ||
}, []string{"type"}), | ||
} | ||
} | ||
|
||
func (m *metricsRecorder) RecordBlockIdType(t BlockIdType) { | ||
m.blockIdType.WithLabelValues(string(t)).Inc() | ||
} | ||
|
||
func (m *metricsRecorder) Registry() *prometheus.Registry { | ||
return m.registry | ||
} |
Oops, something went wrong.