Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Go

on: [pull_request]

jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Setup Go
if: matrix.os == 'ubuntu-latest'
uses: actions/setup-go@v5
with:
cache: false
go-version-file: go.mod
- name: Setup Go
if: matrix.os == 'macos-latest' || matrix.os == 'windows-latest'
uses: actions/setup-go@v5
with:
cache: true
go-version-file: go.mod
- name: Set git to use LF
# make sure that line endings are not converted on windows
# as gofmt linter will report that they need to be changed
run: git config --global core.autocrlf false
- name: Lint
if: matrix.os == 'ubuntu-latest'
uses: golangci/golangci-lint-action@v6
with:
version: v1.64.5
args: --timeout 10m
skip-cache: false
- name: Vet
if: matrix.os == 'ubuntu-latest'
run: make vet
- name: Build
run: make build
- name: Test
run: make test

7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
Expand All @@ -23,3 +20,7 @@ go.work.sum

# env file
.env

# Folders
dist/
.vscode/
17 changes: 17 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
run:
timeout: 10m
linters:
enable:
- copyloopvar
- errcheck
- errname
- errorlint
- goconst
- gofmt
- gofumpt
- govet
- misspell
- nilerr
- staticcheck
- unconvert
- unused
45 changes: 45 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
GO ?= go
GOLANGCI_LINT ?= golangci-lint
GOLANGCI_LINT_VERSION ?= v1.64.5

.PHONY: binary

binary: dist FORCE
$(GO) version
ifeq ($(OS),Windows_NT)
$(GO) build -o dist/batch-export.exe .
else
$(GO) build -o dist/batch-export .
endif

dist:
mkdir $@

.PHONY: lint
lint: linter
$(GOLANGCI_LINT) run

.PHONY: linter
linter:
which $(GOLANGCI_LINT) || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $$($(GO) env GOPATH)/bin $(GOLANGCI_LINT_VERSION)

.PHONY: vet
vet:
$(GO) vet ./...

.PHONY: build
build: export CGO_ENABLED=0
build:
$(GO) build -trimpath -ldflags "$(LDFLAGS)" ./...

.PHONY: clean
clean:
$(GO) clean
rm -rf dist/


.PHONY: test
test:
$(GO) test -v ./pkg/...

FORCE:
54 changes: 53 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,53 @@
# batch-export
# batch-export

batch-export is a tool to retrieve Ethereum event logs for specific contracts, particularly designed for Swarm's Postage Stamp contract on the Gnosis Chain. It fetches logs within a specified block range using the `export` command and saves them to a file.

## Features

- Retrieve event logs for a specified contract address and block range.
- Handles large block ranges by querying in smaller chunks.
- Supports rate limiting for RPC requests.
- Saves retrieved logs to a specified output file (default: `export.ndjson`) in NDJSON format.
- Graceful shutdown on interrupt signals (Ctrl+C).

## Requirements

- Go 1.24 or later

## Installation

```sh
git clone https://github.com/ethersphere/batch-export.git
cd batch-export
make binary
# The binary will be located in the dist/ folder
```

## Usage

The primary command is export.

```sh
./dist/batch-export export --help
```

```sh
./dist/batch-export export \
--start 31306381 \
--endpoint <YOUR_GNOSIS_RPC_ENDPOINT> \
--output my_logs.ndjson
```

## Flags

```sh
-b, --block-range-limit uint32 Max blocks per log query (default 5)
-c, --compress Compress to GZIP
--end uint End block (optional, uses latest block if 0) (default 39810670)
-e, --endpoint string Ethereum RPC endpoint URL
-h, --help help for export
-m, --max-request int Max RPC requests/sec (default 15)
-o, --output string Output file path (NDJSON) (default "export.ndjson")
--start uint Start block (optional, uses contract start block if 0) (default 31306381)
-v, --verbosity string Log verbosity (silent, error, warn, info, debug) (default "info")
```
76 changes: 76 additions & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package cmd

import (
"context"
"fmt"
"strings"

"github.com/ethersphere/bee/v2/pkg/log"
"github.com/spf13/cobra"
)

type command struct {
root *cobra.Command
verbosity string
log log.Logger
}

func (c *command) Execute(ctx context.Context) (err error) {
return c.root.ExecuteContext(ctx)
}

func Execute(ctx context.Context) (err error) {
c, err := newCommand()
if err != nil {
return err
}
return c.Execute(ctx)
}

func newCommand() (c *command, err error) {
c = &command{
root: &cobra.Command{
Use: "batch-export",
Short: "A tool to track logs of a swarm node",
Long: "A tool to track logs of a swarm node",
SilenceErrors: true,
SilenceUsage: true,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
var err error
c.log, err = newLogger(c.verbosity)
if err != nil {
return fmt.Errorf("failed to create logger: %w", err)
}
return nil
},
},
}

c.root.PersistentFlags().StringVarP(&c.verbosity, "verbosity", "v", "info", "Log verbosity (silent, error, warn, info, debug)")

if err := c.initExportCmd(); err != nil {
return nil, err
}

return c, nil
}

func newLogger(verbosity string) (logger log.Logger, err error) {
var level log.Level
switch strings.ToLower(verbosity) {
case "0", "silent":
level = log.VerbosityNone
case "1", "error":
level = log.VerbosityError
case "2", "warn":
level = log.VerbosityWarning
case "3", "info":
level = log.VerbosityInfo
case "4", "debug":
level = log.VerbosityDebug
default:
return nil, fmt.Errorf("invalid verbosity level: %s", verbosity)
}

return log.NewLogger("batch-export", log.WithVerbosity(level), log.WithTimestamp()).Register(), nil
}
Loading