op-signer: add to this repo (#51)
* op-signer: add to this repo * circleci: add op-signer jobs/workflows * ops: update tag service to include op-signer * readme: add op-signer one sentence description * ci: add op-signer option to github action * ops: add op-signer min version
This commit is contained in:
parent
7f8095c9ad
commit
75b02dff3d
@ -16,6 +16,7 @@ workflows:
|
|||||||
# <regex path-to-test> <parameter-to-set> <value-of-pipeline-parameter>
|
# <regex path-to-test> <parameter-to-set> <value-of-pipeline-parameter>
|
||||||
mapping: |
|
mapping: |
|
||||||
op-conductor-mon/.* run-build-op-conductor-mon true
|
op-conductor-mon/.* run-build-op-conductor-mon true
|
||||||
|
op-signer/.* run-build-op-signer true
|
||||||
op-ufm/.* run-build-op-ufm true
|
op-ufm/.* run-build-op-ufm true
|
||||||
proxyd/.* run-build-proxyd true
|
proxyd/.* run-build-proxyd true
|
||||||
.circleci/.* run-all true
|
.circleci/.* run-all true
|
||||||
|
@ -10,6 +10,9 @@ parameters:
|
|||||||
run-build-op-conductor-mon:
|
run-build-op-conductor-mon:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
run-build-op-signer:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
run-build-op-ufm:
|
run-build-op-ufm:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
@ -76,6 +79,7 @@ jobs:
|
|||||||
command: |
|
command: |
|
||||||
echo "Configuration Results:"
|
echo "Configuration Results:"
|
||||||
echo "run-build-op-conductor-mon: << pipeline.parameters.run-build-op-conductor-mon >>"
|
echo "run-build-op-conductor-mon: << pipeline.parameters.run-build-op-conductor-mon >>"
|
||||||
|
echo "run-build-op-signer: << pipeline.parameters.run-build-op-signer >>"
|
||||||
echo "run-build-op-ufm: << pipeline.parameters.run-build-op-ufm >>"
|
echo "run-build-op-ufm: << pipeline.parameters.run-build-op-ufm >>"
|
||||||
echo "run-build-proxyd: << pipeline.parameters.run-build-proxyd >>"
|
echo "run-build-proxyd: << pipeline.parameters.run-build-proxyd >>"
|
||||||
echo "run-all: << pipeline.parameters.run-all >>"
|
echo "run-all: << pipeline.parameters.run-all >>"
|
||||||
@ -100,6 +104,12 @@ jobs:
|
|||||||
echo "op-conductor-mon tag regex match: false"
|
echo "op-conductor-mon tag regex match: false"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ $CURRENT_TAG =~ ^op-signer/v.* ]]; then
|
||||||
|
echo "op-signer tag regex match: true"
|
||||||
|
else
|
||||||
|
echo "op-signer tag regex match: false"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $CURRENT_TAG =~ ^op-ufm/v.* ]]; then
|
if [[ $CURRENT_TAG =~ ^op-ufm/v.* ]]; then
|
||||||
echo "op-ufm tag regex match: true"
|
echo "op-ufm tag regex match: true"
|
||||||
else
|
else
|
||||||
@ -408,6 +418,22 @@ workflows:
|
|||||||
docker_name: op-conductor-mon
|
docker_name: op-conductor-mon
|
||||||
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
|
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
|
||||||
docker_context: .
|
docker_context: .
|
||||||
|
op-signer:
|
||||||
|
when:
|
||||||
|
or: [<< pipeline.parameters.run-build-op-signer >>, << pipeline.parameters.run-all >>]
|
||||||
|
jobs:
|
||||||
|
- go-lint:
|
||||||
|
name: op-signer-lint
|
||||||
|
module: op-signer
|
||||||
|
- go-test:
|
||||||
|
name: op-signer-tests
|
||||||
|
module: op-signer
|
||||||
|
- docker-build:
|
||||||
|
name: op-signer-docker-build
|
||||||
|
docker_file: op-signer/Dockerfile
|
||||||
|
docker_name: op-signer
|
||||||
|
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
|
||||||
|
docker_context: .
|
||||||
op-ufm:
|
op-ufm:
|
||||||
when:
|
when:
|
||||||
or: [<< pipeline.parameters.run-build-op-ufm >>, << pipeline.parameters.run-all >>]
|
or: [<< pipeline.parameters.run-build-op-ufm >>, << pipeline.parameters.run-all >>]
|
||||||
@ -455,6 +481,38 @@ workflows:
|
|||||||
only: /^(proxyd|ufm-[a-z0-9\-]*|op-[a-z0-9\-]*)\/v.*/
|
only: /^(proxyd|ufm-[a-z0-9\-]*|op-[a-z0-9\-]*)\/v.*/
|
||||||
branches:
|
branches:
|
||||||
ignore: /.*/
|
ignore: /.*/
|
||||||
|
- docker-build:
|
||||||
|
name: op-signer-docker-build
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /^op-signer\/v.*/
|
||||||
|
docker_name: op-signer
|
||||||
|
docker_tags: <<pipeline.git.revision>>
|
||||||
|
docker_context: .
|
||||||
|
docker_file: op-signer/Dockerfile
|
||||||
|
context:
|
||||||
|
- oplabs-gcr-release
|
||||||
|
requires:
|
||||||
|
- hold
|
||||||
|
- docker-publish:
|
||||||
|
name: op-signer-docker-publish
|
||||||
|
docker_name: op-signer
|
||||||
|
docker_tags: <<pipeline.git.revision>>
|
||||||
|
context:
|
||||||
|
- oplabs-gcr-release
|
||||||
|
requires:
|
||||||
|
- op-signer-docker-build
|
||||||
|
- docker-tag-op-stack-release:
|
||||||
|
name: docker-tag-op-signer-release
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /^op-signer\/v.*/
|
||||||
|
branches:
|
||||||
|
ignore: /.*/
|
||||||
|
context:
|
||||||
|
- oplabs-gcr-release
|
||||||
|
requires:
|
||||||
|
- op-signer-docker-publish
|
||||||
- docker-build:
|
- docker-build:
|
||||||
name: op-ufm-docker-build
|
name: op-ufm-docker-build
|
||||||
filters:
|
filters:
|
||||||
|
1
.github/workflows/tag-service.yml
vendored
1
.github/workflows/tag-service.yml
vendored
@ -18,6 +18,7 @@ on:
|
|||||||
required: true
|
required: true
|
||||||
type: choice
|
type: choice
|
||||||
options:
|
options:
|
||||||
|
- op-signer
|
||||||
- op-ufm
|
- op-ufm
|
||||||
- proxyd
|
- proxyd
|
||||||
prerelease:
|
prerelease:
|
||||||
|
@ -4,4 +4,5 @@ This repository is an extension of the [Optimism monorepo](https://github.com/et
|
|||||||
|
|
||||||
## Components
|
## Components
|
||||||
- op-conductor-mon: Monitors multiple op-conductor instances and provides a unified interface for reporting metrics.
|
- op-conductor-mon: Monitors multiple op-conductor instances and provides a unified interface for reporting metrics.
|
||||||
|
- op-signer: Thin gateway that supports `eth_signTransaction` RPC endpoint to sign ethereum tx payloads using private key stored in KMS
|
||||||
- op-ufm: User facing monitoring creates transactions at regular intervals and observe transaction propagation across different RPC providers.
|
- op-ufm: User facing monitoring creates transactions at regular intervals and observe transaction propagation across different RPC providers.
|
||||||
|
37
op-signer/.air.toml
Normal file
37
op-signer/.air.toml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
root = "."
|
||||||
|
testdata_dir = "testdata"
|
||||||
|
tmp_dir = "bin"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
args_bin = []
|
||||||
|
bin = "./bin/op-signer"
|
||||||
|
cmd = "make build"
|
||||||
|
delay = 1000
|
||||||
|
exclude_dir = ["bin"]
|
||||||
|
exclude_file = []
|
||||||
|
exclude_regex = ["_test.go"]
|
||||||
|
exclude_unchanged = false
|
||||||
|
follow_symlink = false
|
||||||
|
full_bin = ""
|
||||||
|
include_dir = []
|
||||||
|
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||||
|
kill_delay = "0s"
|
||||||
|
log = "build-errors.log"
|
||||||
|
send_interrupt = false
|
||||||
|
stop_on_error = true
|
||||||
|
|
||||||
|
[color]
|
||||||
|
app = ""
|
||||||
|
build = "yellow"
|
||||||
|
main = "magenta"
|
||||||
|
runner = "green"
|
||||||
|
watcher = "cyan"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
time = false
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
clean_on_exit = false
|
||||||
|
|
||||||
|
[screen]
|
||||||
|
clear_on_rebuild = false
|
4
op-signer/.envrc
Normal file
4
op-signer/.envrc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export OP_SIGNER_LOG_LEVEL=debug
|
||||||
|
export OP_SIGNER_LOG_COLOR=true
|
||||||
|
export OP_SIGNER_RPC_PORT=8080
|
||||||
|
export OP_SIGNER_METRICS_ENABLED=true
|
2
op-signer/.gitignore
vendored
Normal file
2
op-signer/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
bin/
|
||||||
|
tls/
|
1
op-signer/CHANGELOG.md
Normal file
1
op-signer/CHANGELOG.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# @eth-optimism/signer
|
18
op-signer/Dockerfile
Normal file
18
op-signer/Dockerfile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
FROM golang:1.21.3-alpine3.18 as builder
|
||||||
|
|
||||||
|
COPY ./op-signer /app
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
RUN apk --no-cache add make jq bash git alpine-sdk
|
||||||
|
RUN make build
|
||||||
|
|
||||||
|
FROM alpine:3.18
|
||||||
|
RUN apk --no-cache add ca-certificates
|
||||||
|
|
||||||
|
RUN addgroup -S app && adduser -S app -G app
|
||||||
|
USER app
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /app/bin/op-signer /app
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/op-signer"]
|
37
op-signer/Makefile
Normal file
37
op-signer/Makefile
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
GITCOMMIT := $(shell git rev-parse HEAD)
|
||||||
|
GITDATE := $(shell git show -s --format='%ct')
|
||||||
|
VERSION := v0.0.0
|
||||||
|
|
||||||
|
LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT)
|
||||||
|
LDFLAGSSTRING +=-X main.GitDate=$(GITDATE)
|
||||||
|
LDFLAGSSTRING +=-X main.Version=$(VERSION)
|
||||||
|
LDFLAGS := -ldflags "$(LDFLAGSSTRING)"
|
||||||
|
|
||||||
|
all: build
|
||||||
|
|
||||||
|
docker:
|
||||||
|
docker build ../ -f Dockerfile -t op-signer:latest
|
||||||
|
|
||||||
|
build:
|
||||||
|
env GO111MODULE=on go build -v $(LDFLAGS) -o ./bin/op-signer ./cmd
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm ./bin/op-signer
|
||||||
|
|
||||||
|
generate:
|
||||||
|
[ '$(shell mockgen --version)' = 'v1.6.0' ] || go install github.com/golang/mock/mockgen@v1.6.0
|
||||||
|
go generate ./...
|
||||||
|
|
||||||
|
test: generate
|
||||||
|
go test -v ./...
|
||||||
|
|
||||||
|
lint:
|
||||||
|
golangci-lint run ./...
|
||||||
|
|
||||||
|
.PHONY: \
|
||||||
|
build \
|
||||||
|
clean \
|
||||||
|
test \
|
||||||
|
generate \
|
||||||
|
lint \
|
||||||
|
docker
|
14
op-signer/README.md
Normal file
14
op-signer/README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# @eth-optimism/signer
|
||||||
|
|
||||||
|
Signer service and client library
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Install go1.18
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make build
|
||||||
|
|
||||||
|
source .env.example # (or copy to .envrc if using direnv)
|
||||||
|
./bin/signer
|
||||||
|
```
|
239
op-signer/app.go
Normal file
239
op-signer/app.go
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
||||||
|
"github.com/ethereum-optimism/optimism/op-service/cliapp"
|
||||||
|
"github.com/ethereum-optimism/optimism/op-service/httputil"
|
||||||
|
oplog "github.com/ethereum-optimism/optimism/op-service/log"
|
||||||
|
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
|
||||||
|
"github.com/ethereum-optimism/optimism/op-service/oppprof"
|
||||||
|
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
|
||||||
|
"github.com/ethereum-optimism/optimism/op-service/tls/certman"
|
||||||
|
|
||||||
|
"github.com/ethereum-optimism/infra/op-signer/client"
|
||||||
|
"github.com/ethereum-optimism/infra/op-signer/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SignerApp struct {
|
||||||
|
log log.Logger
|
||||||
|
|
||||||
|
version string
|
||||||
|
|
||||||
|
pprofServer *oppprof.Service
|
||||||
|
metricsServer *httputil.HTTPServer
|
||||||
|
registry *prometheus.Registry
|
||||||
|
|
||||||
|
signer *service.SignerService
|
||||||
|
|
||||||
|
rpc *oprpc.Server
|
||||||
|
|
||||||
|
stopped atomic.Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitFromConfig(ctx context.Context, log log.Logger, cfg *Config, version string) (*SignerApp, error) {
|
||||||
|
if err := cfg.Check(); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid config: %w", err)
|
||||||
|
}
|
||||||
|
app := &SignerApp{log: log, version: version}
|
||||||
|
if err := app.init(cfg); err != nil {
|
||||||
|
return nil, errors.Join(err, app.Stop(ctx)) // clean up the failed init attempt
|
||||||
|
}
|
||||||
|
return app, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerApp) init(cfg *Config) error {
|
||||||
|
if err := s.initPprof(cfg); err != nil {
|
||||||
|
return fmt.Errorf("pprof error: %w", err)
|
||||||
|
}
|
||||||
|
if err := s.initMetrics(cfg); err != nil {
|
||||||
|
return fmt.Errorf("metrics error: %w", err)
|
||||||
|
}
|
||||||
|
if err := s.initRPC(cfg); err != nil {
|
||||||
|
return fmt.Errorf("metrics error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerApp) initPprof(cfg *Config) error {
|
||||||
|
if !cfg.PprofConfig.ListenEnabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.pprofServer = oppprof.New(
|
||||||
|
cfg.PprofConfig.ListenEnabled,
|
||||||
|
cfg.PprofConfig.ListenAddr,
|
||||||
|
cfg.PprofConfig.ListenPort,
|
||||||
|
cfg.PprofConfig.ProfileType,
|
||||||
|
cfg.PprofConfig.ProfileDir,
|
||||||
|
cfg.PprofConfig.ProfileFilename,
|
||||||
|
)
|
||||||
|
s.log.Info("Starting pprof server", "addr", cfg.PprofConfig.ListenAddr, "port", cfg.PprofConfig.ListenPort)
|
||||||
|
if err := s.pprofServer.Start(); err != nil {
|
||||||
|
return fmt.Errorf("failed to start pprof server: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerApp) initMetrics(cfg *Config) error {
|
||||||
|
registry := opmetrics.NewRegistry()
|
||||||
|
registry.MustRegister(service.MetricSignTransactionTotal)
|
||||||
|
s.registry = registry // some things require metrics registry
|
||||||
|
|
||||||
|
if !cfg.MetricsConfig.Enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
metricsCfg := cfg.MetricsConfig
|
||||||
|
s.log.Info("Starting metrics server", "addr", metricsCfg.ListenAddr, "port", metricsCfg.ListenPort)
|
||||||
|
metricsServer, err := opmetrics.StartServer(registry, metricsCfg.ListenAddr, metricsCfg.ListenPort)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to start metrics server: %w", err)
|
||||||
|
}
|
||||||
|
s.log.Info("Started metrics server", "endpoint", metricsServer.Addr())
|
||||||
|
s.metricsServer = metricsServer
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerApp) initRPC(cfg *Config) error {
|
||||||
|
caCert, err := os.ReadFile(cfg.TLSConfig.TLSCaCert)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read tls ca cert: %s", string(caCert))
|
||||||
|
}
|
||||||
|
caCertPool := x509.NewCertPool()
|
||||||
|
caCertPool.AppendCertsFromPEM(caCert)
|
||||||
|
|
||||||
|
cm, err := certman.New(s.log, cfg.TLSConfig.TLSCert, cfg.TLSConfig.TLSKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read tls cert or key: %w", err)
|
||||||
|
}
|
||||||
|
if err := cm.Watch(); err != nil {
|
||||||
|
return fmt.Errorf("failed to start certman watcher: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
GetCertificate: cm.GetCertificate,
|
||||||
|
ClientCAs: caCertPool,
|
||||||
|
ClientAuth: tls.VerifyClientCertIfGiven, // necessary for k8s healthz probes, but we check the cert in service/auth.go
|
||||||
|
}
|
||||||
|
serverTlsConfig := &oprpc.ServerTLSConfig{
|
||||||
|
Config: tlsConfig,
|
||||||
|
CLIConfig: &cfg.TLSConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcCfg := cfg.RPCConfig
|
||||||
|
s.rpc = oprpc.NewServer(
|
||||||
|
rpcCfg.ListenAddr,
|
||||||
|
rpcCfg.ListenPort,
|
||||||
|
s.version,
|
||||||
|
oprpc.WithLogger(s.log),
|
||||||
|
oprpc.WithTLSConfig(serverTlsConfig),
|
||||||
|
oprpc.WithMiddleware(service.NewAuthMiddleware()),
|
||||||
|
oprpc.WithHTTPRecorder(opmetrics.NewPromHTTPRecorder(s.registry, "signer")),
|
||||||
|
)
|
||||||
|
|
||||||
|
serviceCfg, err := service.ReadConfig(cfg.ServiceConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read service config: %w", err)
|
||||||
|
}
|
||||||
|
s.signer = service.NewSignerService(s.log, serviceCfg)
|
||||||
|
s.signer.RegisterAPIs(s.rpc)
|
||||||
|
|
||||||
|
if err := s.rpc.Start(); err != nil {
|
||||||
|
return fmt.Errorf("error starting RPC server: %w", err)
|
||||||
|
}
|
||||||
|
s.log.Info("Started op-signer RPC server", "addr", s.rpc.Endpoint())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerApp) Start(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerApp) Stop(ctx context.Context) error {
|
||||||
|
var result error
|
||||||
|
if s.rpc != nil {
|
||||||
|
if err := s.rpc.Stop(); err != nil {
|
||||||
|
result = errors.Join(result, fmt.Errorf("failed to stop RPC server: %w", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.pprofServer != nil {
|
||||||
|
if err := s.pprofServer.Stop(ctx); err != nil {
|
||||||
|
result = errors.Join(result, fmt.Errorf("failed to stop pprof server: %w", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.metricsServer != nil {
|
||||||
|
if err := s.metricsServer.Stop(ctx); err != nil {
|
||||||
|
result = errors.Join(result, fmt.Errorf("failed to stop metrics server: %w", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerApp) Stopped() bool {
|
||||||
|
return s.stopped.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ cliapp.Lifecycle = (*SignerApp)(nil)
|
||||||
|
|
||||||
|
func MainAppAction(version string) cliapp.LifecycleAction {
|
||||||
|
return func(cliCtx *cli.Context, _ context.CancelCauseFunc) (cliapp.Lifecycle, error) {
|
||||||
|
cfg := NewConfig(cliCtx)
|
||||||
|
logger := oplog.NewLogger(cliCtx.App.Writer, cfg.LogConfig)
|
||||||
|
return InitFromConfig(cliCtx.Context, logger, cfg, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClientSign(version string) func(cliCtx *cli.Context) error {
|
||||||
|
return func(cliCtx *cli.Context) error {
|
||||||
|
cfg := NewConfig(cliCtx)
|
||||||
|
if err := cfg.Check(); err != nil {
|
||||||
|
return fmt.Errorf("invalid CLI flags: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l := oplog.NewLogger(os.Stdout, cfg.LogConfig)
|
||||||
|
log.Root().SetHandler(l.GetHandler())
|
||||||
|
|
||||||
|
txarg := cliCtx.Args().First()
|
||||||
|
if txarg == "" {
|
||||||
|
return errors.New("no transaction argument was provided")
|
||||||
|
}
|
||||||
|
txraw, err := hexutil.Decode(txarg)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("failed to decode transaction argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := client.NewSignerClient(l, cfg.ClientEndpoint, cfg.TLSConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := &types.Transaction{}
|
||||||
|
if err := tx.UnmarshalBinary(txraw); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal transaction argument: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err = client.SignTransaction(context.Background(), tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, _ := tx.MarshalJSON()
|
||||||
|
fmt.Println(string(result))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
91
op-signer/client/client.go
Normal file
91
op-signer/client/client.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
||||||
|
"github.com/ethereum-optimism/optimism/op-service/signer"
|
||||||
|
optls "github.com/ethereum-optimism/optimism/op-service/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SignerClient struct {
|
||||||
|
client *rpc.Client
|
||||||
|
status string
|
||||||
|
logger log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSignerClient(logger log.Logger, endpoint string, tlsConfig optls.CLIConfig) (*SignerClient, error) {
|
||||||
|
|
||||||
|
caCert, err := os.ReadFile(tlsConfig.TLSCaCert)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read tls.ca: %w", err)
|
||||||
|
}
|
||||||
|
caCertPool := x509.NewCertPool()
|
||||||
|
caCertPool.AppendCertsFromPEM(caCert)
|
||||||
|
|
||||||
|
cert, err := tls.LoadX509KeyPair(tlsConfig.TLSCert, tlsConfig.TLSKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read tls.cert or tls.key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient := &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
RootCAs: caCertPool,
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcClient, err := rpc.DialOptions(context.Background(), endpoint, rpc.WithHTTPClient(httpClient))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
signerClient := &SignerClient{logger: logger, client: rpcClient}
|
||||||
|
// Check if reachable
|
||||||
|
version, err := signerClient.pingVersion()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signerClient.status = fmt.Sprintf("ok [version=%v]", version)
|
||||||
|
return signerClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerClient) pingVersion() (string, error) {
|
||||||
|
var v string
|
||||||
|
if err := s.client.Call(&v, "health_status"); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerClient) SignTransaction(
|
||||||
|
ctx context.Context,
|
||||||
|
tx *types.Transaction,
|
||||||
|
) (*types.Transaction, error) {
|
||||||
|
|
||||||
|
args := signer.NewTransactionArgsFromTransaction(tx.ChainId(), nil, tx)
|
||||||
|
|
||||||
|
var result hexutil.Bytes
|
||||||
|
|
||||||
|
if err := s.client.Call(&result, "eth_signTransaction", args); err != nil {
|
||||||
|
return nil, fmt.Errorf("eth_signTransaction failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
signed := &types.Transaction{}
|
||||||
|
if err := signed.UnmarshalBinary(result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return signed, nil
|
||||||
|
}
|
51
op-signer/cmd/main.go
Normal file
51
op-signer/cmd/main.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
||||||
|
signer "github.com/ethereum-optimism/infra/op-signer"
|
||||||
|
"github.com/ethereum-optimism/optimism/op-service/cliapp"
|
||||||
|
oplog "github.com/ethereum-optimism/optimism/op-service/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Version = ""
|
||||||
|
GitCommit = ""
|
||||||
|
GitDate = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
oplog.SetupDefaults()
|
||||||
|
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Flags = cliapp.ProtectFlags(signer.CLIFlags("OP_SIGNER"))
|
||||||
|
app.Version = fmt.Sprintf("%s-%s-%s", Version, GitCommit, GitDate)
|
||||||
|
app.Name = "op-signer"
|
||||||
|
app.Usage = "OP Signing Service"
|
||||||
|
app.Description = ""
|
||||||
|
app.Commands = []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "client",
|
||||||
|
Usage: "test client for signer service",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "sign",
|
||||||
|
Usage: "sign a transaction",
|
||||||
|
Action: signer.ClientSign(Version),
|
||||||
|
Flags: cliapp.ProtectFlags(signer.ClientSignCLIFlags("SIGNER")),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Action = cliapp.LifecycleCmd(signer.MainAppAction(Version))
|
||||||
|
err := app.Run(os.Args)
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("Application failed", "message", err)
|
||||||
|
}
|
||||||
|
}
|
85
op-signer/config.go
Normal file
85
op-signer/config.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
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/ethereum-optimism/optimism/op-service/oppprof"
|
||||||
|
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
|
||||||
|
optls "github.com/ethereum-optimism/optimism/op-service/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ServiceConfigPathFlagName = "config"
|
||||||
|
ClientEndpointFlagName = "endpoint"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CLIFlags(envPrefix string) []cli.Flag {
|
||||||
|
flags := []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: ServiceConfigPathFlagName,
|
||||||
|
Usage: "Signer service configuration file path",
|
||||||
|
Value: "config.yaml",
|
||||||
|
EnvVars: opservice.PrefixEnvVar(envPrefix, "SERVICE_CONFIG"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
flags = append(flags, oprpc.CLIFlags(envPrefix)...)
|
||||||
|
flags = append(flags, oplog.CLIFlags(envPrefix)...)
|
||||||
|
flags = append(flags, opmetrics.CLIFlags(envPrefix)...)
|
||||||
|
flags = append(flags, oppprof.CLIFlags(envPrefix)...)
|
||||||
|
flags = append(flags, optls.CLIFlags(envPrefix)...)
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClientSignCLIFlags(envPrefix string) []cli.Flag {
|
||||||
|
flags := []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: ClientEndpointFlagName,
|
||||||
|
Usage: "Signer endpoint the client will connect to",
|
||||||
|
Value: "http://localhost:8080",
|
||||||
|
EnvVars: opservice.PrefixEnvVar(envPrefix, "CLIENT_ENDPOINT"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
ClientEndpoint string
|
||||||
|
ServiceConfigPath string
|
||||||
|
|
||||||
|
TLSConfig optls.CLIConfig
|
||||||
|
RPCConfig oprpc.CLIConfig
|
||||||
|
LogConfig oplog.CLIConfig
|
||||||
|
MetricsConfig opmetrics.CLIConfig
|
||||||
|
PprofConfig oppprof.CLIConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) Check() error {
|
||||||
|
if err := c.RPCConfig.Check(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.MetricsConfig.Check(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.PprofConfig.Check(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.TLSConfig.Check(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfig(ctx *cli.Context) *Config {
|
||||||
|
return &Config{
|
||||||
|
ClientEndpoint: ctx.String(ClientEndpointFlagName),
|
||||||
|
ServiceConfigPath: ctx.String(ServiceConfigPathFlagName),
|
||||||
|
TLSConfig: optls.ReadCLIConfig(ctx),
|
||||||
|
RPCConfig: oprpc.ReadCLIConfig(ctx),
|
||||||
|
LogConfig: oplog.ReadCLIConfig(ctx),
|
||||||
|
MetricsConfig: opmetrics.ReadCLIConfig(ctx),
|
||||||
|
PprofConfig: oppprof.ReadCLIConfig(ctx),
|
||||||
|
}
|
||||||
|
}
|
3
op-signer/config.yaml
Normal file
3
op-signer/config.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
auth:
|
||||||
|
- name: localhost
|
||||||
|
key: projects/my-gcp-project/locations/my-region/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1
|
51
op-signer/gen-local-tls.sh
Executable file
51
op-signer/gen-local-tls.sh
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
TLS_DIR=$SCRIPT_DIR/tls
|
||||||
|
|
||||||
|
version=$(openssl version)
|
||||||
|
|
||||||
|
if [[ "$version" != "LibreSSL"* ]] && [[ "$version" != "OpenSSL 1.1"* ]]; then
|
||||||
|
echo "openssl version: $version"
|
||||||
|
echo "script only works with LibreSSL (darwin) or OpenSSL 1.1*"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Generating mTLS credentials for local development..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
mkdir -p "$TLS_DIR"
|
||||||
|
|
||||||
|
if [ ! -f "$TLS_DIR/ca.crt" ]; then
|
||||||
|
echo 'Generating CA'
|
||||||
|
openssl req -newkey rsa:2048 \
|
||||||
|
-new -nodes -x509 \
|
||||||
|
-days 365 \
|
||||||
|
-sha256 \
|
||||||
|
-out "$TLS_DIR/ca.crt" \
|
||||||
|
-keyout "$TLS_DIR/ca.key" \
|
||||||
|
-subj "/O=OP Labs/CN=root"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo 'Generating TLS certificate request'
|
||||||
|
openssl genrsa -out "$TLS_DIR/tls.key" 2048
|
||||||
|
openssl req -new -key "$TLS_DIR/tls.key" \
|
||||||
|
-days 1 \
|
||||||
|
-sha256 \
|
||||||
|
-out "$TLS_DIR/tls.csr" \
|
||||||
|
-keyout "$TLS_DIR/tls.key" \
|
||||||
|
-subj "/O=OP Labs/CN=localhost" \
|
||||||
|
-extensions san \
|
||||||
|
-config <(echo '[req]'; echo 'distinguished_name=req'; \
|
||||||
|
echo '[san]'; echo 'subjectAltName=DNS:localhost')
|
||||||
|
|
||||||
|
openssl x509 -req -in "$TLS_DIR/tls.csr" \
|
||||||
|
-sha256 \
|
||||||
|
-CA "$TLS_DIR/ca.crt" \
|
||||||
|
-CAkey "$TLS_DIR/ca.key" \
|
||||||
|
-CAcreateserial \
|
||||||
|
-out "$TLS_DIR/tls.crt" \
|
||||||
|
-days 3 \
|
||||||
|
-extfile <(echo 'subjectAltName=DNS:localhost')
|
115
op-signer/go.mod
Normal file
115
op-signer/go.mod
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
module github.com/ethereum-optimism/infra/op-signer
|
||||||
|
|
||||||
|
go 1.21
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go/kms v1.10.1
|
||||||
|
github.com/ethereum-optimism/optimism v1.4.3-0.20240125020216-6a3b1ec12399
|
||||||
|
github.com/ethereum/go-ethereum v1.13.5
|
||||||
|
github.com/golang/mock v1.6.0
|
||||||
|
github.com/googleapis/gax-go v1.0.3
|
||||||
|
github.com/googleapis/gax-go/v2 v2.11.0
|
||||||
|
github.com/holiman/uint256 v1.2.3
|
||||||
|
github.com/prometheus/client_golang v1.18.0
|
||||||
|
github.com/stretchr/testify v1.8.4
|
||||||
|
github.com/urfave/cli/v2 v2.27.1
|
||||||
|
google.golang.org/protobuf v1.33.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go/compute v1.20.1 // indirect
|
||||||
|
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||||
|
cloud.google.com/go/iam v0.13.0 // indirect
|
||||||
|
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||||
|
github.com/DataDog/zstd v1.5.2 // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||||
|
github.com/VictoriaMetrics/fastcache v1.12.1 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/bits-and-blooms/bitset v1.7.0 // indirect
|
||||||
|
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/cockroachdb/errors v1.11.1 // indirect
|
||||||
|
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||||
|
github.com/cockroachdb/pebble v0.0.0-20231018212520-f6cde3fc2fa4 // indirect
|
||||||
|
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||||
|
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
||||||
|
github.com/consensys/bavard v0.1.13 // indirect
|
||||||
|
github.com/consensys/gnark-crypto v0.12.1 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
|
github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/deckarep/golang-set/v2 v2.1.0 // indirect
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||||
|
github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240123193359-a5fc767e225a // indirect
|
||||||
|
github.com/ethereum/c-kzg-4844 v0.4.0 // indirect
|
||||||
|
github.com/fjl/memsize v0.0.1 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
|
github.com/getsentry/sentry-go v0.18.0 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
|
github.com/go-stack/stack v1.8.1 // indirect
|
||||||
|
github.com/gofrs/flock v0.8.1 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
|
||||||
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
|
github.com/google/s2a-go v0.1.4 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
|
github.com/hashicorp/go-bexpr v0.1.11 // indirect
|
||||||
|
github.com/huin/goupnp v1.3.0 // indirect
|
||||||
|
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.2 // indirect
|
||||||
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/mitchellh/pointerstructure v1.2.1 // indirect
|
||||||
|
github.com/mmcloughlin/addchain v0.4.0 // indirect
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.5.0 // indirect
|
||||||
|
github.com/prometheus/common v0.45.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.3 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||||
|
github.com/rs/cors v1.9.0 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||||
|
github.com/supranational/blst v0.3.11 // indirect
|
||||||
|
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
|
||||||
|
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
|
go.opencensus.io v0.24.0 // indirect
|
||||||
|
golang.org/x/crypto v0.18.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
|
||||||
|
golang.org/x/mod v0.14.0 // indirect
|
||||||
|
golang.org/x/net v0.19.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.12.0 // indirect
|
||||||
|
golang.org/x/sync v0.6.0 // indirect
|
||||||
|
golang.org/x/sys v0.16.0 // indirect
|
||||||
|
golang.org/x/term v0.16.0 // indirect
|
||||||
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
golang.org/x/tools v0.16.1 // indirect
|
||||||
|
google.golang.org/api v0.126.0 // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||||
|
google.golang.org/grpc v1.55.0 // indirect
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
|
||||||
|
rsc.io/tmplfunc v0.0.3 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace github.com/ethereum/go-ethereum v1.13.5 => github.com/ethereum-optimism/op-geth v1.101305.3-rc.1.0.20240124221225-5c6f10d449ab
|
496
op-signer/go.sum
Normal file
496
op-signer/go.sum
Normal file
@ -0,0 +1,496 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA=
|
||||||
|
cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw=
|
||||||
|
cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg=
|
||||||
|
cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||||
|
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||||
|
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||||
|
cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
|
||||||
|
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
|
||||||
|
cloud.google.com/go/kms v1.10.1 h1:7hm1bRqGCA1GBRQUrp831TwJ9TWhP+tvLuP497CQS2g=
|
||||||
|
cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||||
|
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
|
github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
|
||||||
|
github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||||
|
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||||
|
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||||
|
github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40=
|
||||||
|
github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o=
|
||||||
|
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||||
|
github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
|
||||||
|
github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||||
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo=
|
||||||
|
github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||||
|
github.com/btcsuite/btcd v0.24.0 h1:gL3uHE/IaFj6fcZSu03SvqPMSx7s/dPzfpG/atRwWdo=
|
||||||
|
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
|
||||||
|
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
|
||||||
|
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ=
|
||||||
|
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
|
||||||
|
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||||
|
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
|
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
|
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
|
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
|
||||||
|
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
|
||||||
|
github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8=
|
||||||
|
github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw=
|
||||||
|
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
|
||||||
|
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
|
||||||
|
github.com/cockroachdb/pebble v0.0.0-20231018212520-f6cde3fc2fa4 h1:PuHFhOUMnD62r80dN+Ik5qco2drekgsUSVdcHsvllec=
|
||||||
|
github.com/cockroachdb/pebble v0.0.0-20231018212520-f6cde3fc2fa4/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E=
|
||||||
|
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
|
||||||
|
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
||||||
|
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
|
||||||
|
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
|
||||||
|
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
|
||||||
|
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
|
||||||
|
github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M=
|
||||||
|
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA=
|
||||||
|
github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI=
|
||||||
|
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||||
|
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
||||||
|
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/ethereum-optimism/op-geth v1.101305.3-rc.1.0.20240124221225-5c6f10d449ab h1:SpoQiEG5mteZnaPCo5Pm5QrberdODUefwLjdAtL9LAE=
|
||||||
|
github.com/ethereum-optimism/op-geth v1.101305.3-rc.1.0.20240124221225-5c6f10d449ab/go.mod h1:/0cPafbmmt3JR0yiuoBJWTRCx8briXUd/mtXr+GJ3Zw=
|
||||||
|
github.com/ethereum-optimism/optimism v1.4.3-0.20240125020216-6a3b1ec12399 h1:b3I8SKmsLaAJn4TzGpKLzCGBIRsB82f1B7trMZk2+qU=
|
||||||
|
github.com/ethereum-optimism/optimism v1.4.3-0.20240125020216-6a3b1ec12399/go.mod h1:VGQGQ38ORldOlb3ifKYacmaCLQuVToCj3jax7RjeXys=
|
||||||
|
github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240123193359-a5fc767e225a h1:mWIRpGyrAlWHUznUHKgAJUafkNGfO7VmeLjilhVhB80=
|
||||||
|
github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240123193359-a5fc767e225a/go.mod h1:/70H/KqrtKcvWvNGVj6S3rAcLC+kUPr3t2aDmYIS+Xk=
|
||||||
|
github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY=
|
||||||
|
github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
|
||||||
|
github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ=
|
||||||
|
github.com/fjl/memsize v0.0.1/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
|
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays=
|
||||||
|
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
||||||
|
github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0=
|
||||||
|
github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||||
|
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||||
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
|
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
||||||
|
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||||
|
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
|
||||||
|
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
|
||||||
|
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||||
|
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||||
|
github.com/googleapis/gax-go v1.0.3 h1:9dMLqhaibYONnDRcnHdUs9P8Mw64jLlZTYlDe3leBtQ=
|
||||||
|
github.com/googleapis/gax-go v1.0.3/go.mod h1:QyXYajJFdARxGzjwUfbDFIse7Spkw81SJ4LrBJXtlQ8=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.2/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
|
||||||
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
|
github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubCI7dY=
|
||||||
|
github.com/hashicorp/go-bexpr v0.1.11/go.mod h1:f03lAo0duBlDIUMGCuad8oLcgejw4m7U+N8T+6Kz1AE=
|
||||||
|
github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw=
|
||||||
|
github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
|
||||||
|
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
|
||||||
|
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
|
||||||
|
github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o=
|
||||||
|
github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
|
||||||
|
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||||
|
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||||
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
||||||
|
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
|
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
|
||||||
|
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
|
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw=
|
||||||
|
github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
|
||||||
|
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
|
||||||
|
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
|
||||||
|
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
|
||||||
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
|
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
|
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||||
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||||
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
|
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
|
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||||
|
github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
|
||||||
|
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
|
||||||
|
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||||
|
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
||||||
|
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||||
|
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||||
|
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
|
||||||
|
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
|
||||||
|
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||||
|
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
|
||||||
|
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
|
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
|
||||||
|
github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||||
|
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||||
|
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
|
||||||
|
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4=
|
||||||
|
github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||||
|
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI=
|
||||||
|
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||||
|
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||||
|
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||||
|
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
|
||||||
|
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
|
||||||
|
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
|
||||||
|
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||||
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190221220918-438050ddec5e/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
|
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||||
|
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
|
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||||
|
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
|
||||||
|
golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||||
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||||
|
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
||||||
|
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||||
|
google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
|
||||||
|
google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||||
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao=
|
||||||
|
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||||
|
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||||
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
|
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||||
|
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
|
||||||
|
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
|
||||||
|
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
|
44
op-signer/service/auth.go
Normal file
44
op-signer/service/auth.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
|
||||||
|
optls "github.com/ethereum-optimism/optimism/op-service/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClientInfo struct {
|
||||||
|
ClientName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type clientInfoContextKey struct{}
|
||||||
|
|
||||||
|
func NewAuthMiddleware() oprpc.Middleware {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
clientInfo := ClientInfo{}
|
||||||
|
|
||||||
|
// PeerTLSInfo is attached to context by upstream op-service middleware
|
||||||
|
peerTlsInfo := optls.PeerTLSInfoFromContext(r.Context())
|
||||||
|
if peerTlsInfo.LeafCertificate == nil {
|
||||||
|
http.Error(w, "client certificate was not provided", 401)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Note that the certificate is already verified by http server if we get here
|
||||||
|
if len(peerTlsInfo.LeafCertificate.DNSNames) < 1 {
|
||||||
|
http.Error(w, "client certificate verified but did not contain DNS SAN extension", 401)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clientInfo.ClientName = peerTlsInfo.LeafCertificate.DNSNames[0]
|
||||||
|
|
||||||
|
ctx := context.WithValue(r.Context(), clientInfoContextKey{}, clientInfo)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClientInfoFromContext(ctx context.Context) ClientInfo {
|
||||||
|
info, _ := ctx.Value(clientInfoContextKey{}).(ClientInfo)
|
||||||
|
return info
|
||||||
|
}
|
63
op-signer/service/auth_test.go
Normal file
63
op-signer/service/auth_test.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
optls "github.com/ethereum-optimism/optimism/op-service/tls"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAuthMiddleware_NoCertificate(t *testing.T) {
|
||||||
|
var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
assert.NotNil(t, nil, "handler should not have been invoked")
|
||||||
|
})
|
||||||
|
handler = NewAuthMiddleware()(handler)
|
||||||
|
handler = optls.NewPeerTLSMiddleware(handler)
|
||||||
|
assert.HTTPStatusCode(t, handler.ServeHTTP, "GET", "/", nil, 401)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthMiddleware_CertificateMissingDNS(t *testing.T) {
|
||||||
|
req, _ := http.NewRequest("GET", "/", nil)
|
||||||
|
req.TLS = &tls.ConnectionState{PeerCertificates: []*x509.Certificate{}}
|
||||||
|
|
||||||
|
var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
assert.NotNil(t, nil, "handler should not have been invoked")
|
||||||
|
})
|
||||||
|
|
||||||
|
handler = NewAuthMiddleware()(handler)
|
||||||
|
handler = optls.NewPeerTLSMiddleware(handler)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(rr, req)
|
||||||
|
result := rr.Result()
|
||||||
|
defer result.Body.Close()
|
||||||
|
assert.Equal(t, 401, result.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthMiddleware_HappyPath(t *testing.T) {
|
||||||
|
req, _ := http.NewRequest("GET", "/", nil)
|
||||||
|
req.TLS = &tls.ConnectionState{
|
||||||
|
PeerCertificates: []*x509.Certificate{{DNSNames: []string{"client.oplabs.co"}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerInvoked := false
|
||||||
|
var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
clientInfo := ClientInfoFromContext(r.Context())
|
||||||
|
assert.Equal(t, "client.oplabs.co", clientInfo.ClientName)
|
||||||
|
handlerInvoked = true
|
||||||
|
})
|
||||||
|
|
||||||
|
handler = NewAuthMiddleware()(handler)
|
||||||
|
handler = optls.NewPeerTLSMiddleware(handler)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
handler.ServeHTTP(rr, req)
|
||||||
|
result := rr.Result()
|
||||||
|
defer result.Body.Close()
|
||||||
|
assert.Equal(t, 200, result.StatusCode)
|
||||||
|
assert.True(t, handlerInvoked)
|
||||||
|
}
|
62
op-signer/service/config.go
Normal file
62
op-signer/service/config.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthConfig struct {
|
||||||
|
ClientName string `yaml:"name"`
|
||||||
|
KeyName string `yaml:"key"`
|
||||||
|
ToAddresses []string `yaml:"toAddresses"`
|
||||||
|
MaxValue string `yaml:"maxValue"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c AuthConfig) MaxValueToInt() *big.Int {
|
||||||
|
return hexutil.MustDecodeBig(c.MaxValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignerServiceConfig struct {
|
||||||
|
Auth []AuthConfig `yaml:"auth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadConfig(path string) (SignerServiceConfig, error) {
|
||||||
|
config := SignerServiceConfig{}
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
if err := yaml.Unmarshal(data, &config); err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
for _, authConfig := range config.Auth {
|
||||||
|
for _, toAddress := range authConfig.ToAddresses {
|
||||||
|
if _, err := hexutil.Decode(toAddress); err != nil {
|
||||||
|
return config, fmt.Errorf("invalid toAddress '%s' in auth config: %w", toAddress, err)
|
||||||
|
}
|
||||||
|
if authConfig.MaxValue != "" {
|
||||||
|
if _, err := hexutil.DecodeBig(authConfig.MaxValue); err != nil {
|
||||||
|
return config, fmt.Errorf("invalid maxValue '%s' in auth config: %w", toAddress, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SignerServiceConfig) GetAuthConfigForClient(clientName string) (*AuthConfig, error) {
|
||||||
|
if clientName == "" {
|
||||||
|
return nil, errors.New("client name is empty")
|
||||||
|
}
|
||||||
|
for _, ac := range s.Auth {
|
||||||
|
if ac.ClientName == clientName {
|
||||||
|
return &ac, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("client '%s' is not authorized to use any keys", clientName)
|
||||||
|
}
|
11
op-signer/service/errors.go
Normal file
11
op-signer/service/errors.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
type InvalidTransactionError struct{ message string }
|
||||||
|
|
||||||
|
func (e *InvalidTransactionError) Error() string { return e.message }
|
||||||
|
func (e *InvalidTransactionError) ErrorCode() int { return -32010 }
|
||||||
|
|
||||||
|
type UnauthorizedTransactionError struct{ message string }
|
||||||
|
|
||||||
|
func (e *UnauthorizedTransactionError) Error() string { return e.message }
|
||||||
|
func (e *UnauthorizedTransactionError) ErrorCode() int { return -32011 }
|
12
op-signer/service/metrics.go
Normal file
12
op-signer/service/metrics.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import "github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
var (
|
||||||
|
MetricSignTransactionTotal = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "signer_signtransaction_total",
|
||||||
|
Help: ""},
|
||||||
|
[]string{"client", "status", "error"},
|
||||||
|
)
|
||||||
|
)
|
275
op-signer/service/provider/cloudkms.go
Normal file
275
op-signer/service/provider/cloudkms.go
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
//go:generate mockgen -destination=mock_kms.go -package=provider github.com/ethereum-optimism/infra/op-signer/service/provider CloudKMSClient
|
||||||
|
package provider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"hash/crc32"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
kms "cloud.google.com/go/kms/apiv1"
|
||||||
|
"cloud.google.com/go/kms/apiv1/kmspb"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
gax "github.com/googleapis/gax-go"
|
||||||
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
|
||||||
|
oidNamedCurveSECP256K1 = asn1.ObjectIdentifier{1, 3, 132, 0, 10}
|
||||||
|
)
|
||||||
|
|
||||||
|
type publicKeyInfo struct {
|
||||||
|
Raw asn1.RawContent
|
||||||
|
Algorithm pkix.AlgorithmIdentifier
|
||||||
|
PublicKey asn1.BitString
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloudKMSClient interface {
|
||||||
|
GetPublicKey(ctx context.Context, req *kmspb.GetPublicKeyRequest, opts ...gax.CallOption) (*kmspb.PublicKey, error)
|
||||||
|
AsymmetricSign(context context.Context, req *kmspb.AsymmetricSignRequest, opts ...gax.CallOption) (*kmspb.AsymmetricSignResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloudKMSSignatureProvider struct {
|
||||||
|
logger log.Logger
|
||||||
|
client CloudKMSClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCloudKMSSignatureProvider(logger log.Logger) SignatureProvider {
|
||||||
|
ctx := context.Background()
|
||||||
|
client, err := kms.NewKeyManagementClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to initialize kms client", "error", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &CloudKMSSignatureProvider{logger, client}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCloudKMSSignatureProviderWithClient(logger log.Logger, client CloudKMSClient) SignatureProvider {
|
||||||
|
return &CloudKMSSignatureProvider{logger, client}
|
||||||
|
}
|
||||||
|
|
||||||
|
func crc32c(data []byte) uint32 {
|
||||||
|
t := crc32.MakeTable(crc32.Castagnoli)
|
||||||
|
return crc32.Checksum(data, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSignRequestFromDigest(keyName string, digest []byte) *kmspb.AsymmetricSignRequest {
|
||||||
|
digestCRC32C := crc32c(digest)
|
||||||
|
return &kmspb.AsymmetricSignRequest{
|
||||||
|
Name: keyName,
|
||||||
|
Digest: &kmspb.Digest{
|
||||||
|
Digest: &kmspb.Digest_Sha256{
|
||||||
|
Sha256: digest,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DigestCrc32C: wrapperspb.Int64(int64(digestCRC32C)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignDigest signs the digest with a given Cloud KMS keyname and returns a compact recoverable signature.
|
||||||
|
// If the keyName provided is not a EC_SIGN_SECP256K1_SHA256 key, the result will be an error.
|
||||||
|
func (c *CloudKMSSignatureProvider) SignDigest(
|
||||||
|
ctx context.Context,
|
||||||
|
keyName string,
|
||||||
|
digest []byte,
|
||||||
|
) ([]byte, error) {
|
||||||
|
publicKey, err := c.GetPublicKey(ctx, keyName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get public key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
request := createSignRequestFromDigest(keyName, digest)
|
||||||
|
result, err := c.client.AsymmetricSign(ctx, request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cloud kms sign request failed: %w", err)
|
||||||
|
}
|
||||||
|
if result.Name != request.Name {
|
||||||
|
return nil, errors.New("cloud kms sign request corrupted in transit")
|
||||||
|
}
|
||||||
|
if !result.VerifiedDigestCrc32C {
|
||||||
|
return nil, errors.New("cloud kms sign request corrupted in transit")
|
||||||
|
}
|
||||||
|
if int64(crc32c(result.Signature)) != result.SignatureCrc32C.Value {
|
||||||
|
return nil, errors.New("cloud kms sign response corrupted in transit")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.logger.Debug(fmt.Sprintf("der signature: %s", hexutil.Encode(result.Signature)))
|
||||||
|
|
||||||
|
return convertToCompactRecoverableSignature(result.Signature, digest, publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertToCompactRecoverableSignature(derSignature, digest, publicKey []byte) ([]byte, error) {
|
||||||
|
signature, err := convertToCompactSignature(derSignature)
|
||||||
|
if err != nil {
|
||||||
|
// should never happen
|
||||||
|
return nil, fmt.Errorf("failed to convert to compact signature: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: so far I haven't seen CloudKMS produce a malleable signature
|
||||||
|
// but if it does happen, this can be handled as a retryable error by the client
|
||||||
|
if err := compactSignatureMalleabilityCheck(signature); err != nil {
|
||||||
|
// should never happen
|
||||||
|
return nil, fmt.Errorf("signature failed malleability check: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !secp256k1.VerifySignature(publicKey, digest, signature) {
|
||||||
|
// should never happen
|
||||||
|
return nil, errors.New("signature could not be verified with public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
recId, err := calculateRecoveryID(signature, digest, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
// should never happen
|
||||||
|
return nil, fmt.Errorf("failed to calculate recovery id: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
signature = append(signature, byte(recId))
|
||||||
|
|
||||||
|
return signature, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertToCompactSignature compacts a DER signature output from kms (>70 bytes) into 64 bytes
|
||||||
|
func convertToCompactSignature(derSignature []byte) ([]byte, error) {
|
||||||
|
var parsedSig struct{ R, S *big.Int }
|
||||||
|
if _, err := asn1.Unmarshal(derSignature, &parsedSig); err != nil {
|
||||||
|
return nil, fmt.Errorf("asn1.Unmarshal error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
curveOrderLen := 32
|
||||||
|
signature := make([]byte, 2*curveOrderLen)
|
||||||
|
|
||||||
|
// if S is non-canonical, lower it
|
||||||
|
curveOrder := secp256k1.S256().Params().Params().N
|
||||||
|
if parsedSig.S.Cmp(new(big.Int).Div(curveOrder, big.NewInt(2))) > 0 {
|
||||||
|
parsedSig.S = new(big.Int).Sub(curveOrder, parsedSig.S)
|
||||||
|
}
|
||||||
|
|
||||||
|
// left pad R and S with zeroes
|
||||||
|
rBytes := parsedSig.R.Bytes()
|
||||||
|
sBytes := parsedSig.S.Bytes()
|
||||||
|
copy(signature[curveOrderLen-len(rBytes):], rBytes)
|
||||||
|
copy(signature[len(signature)-len(sBytes):], sBytes)
|
||||||
|
|
||||||
|
return signature, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateRecoveryID calculates the signature recovery id (65th byte, [0-3])
|
||||||
|
func calculateRecoveryID(signature, digest, pubKey []byte) (int, error) {
|
||||||
|
recId := -1
|
||||||
|
var errorRes error
|
||||||
|
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
recSig := append(signature, byte(i))
|
||||||
|
publicKey, err := secp256k1.RecoverPubkey(digest, recSig)
|
||||||
|
if err != nil {
|
||||||
|
errorRes = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if bytes.Equal(publicKey, pubKey) {
|
||||||
|
recId = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if recId == -1 {
|
||||||
|
return recId, fmt.Errorf("failed to calculate recovery id, should never happen: %w", errorRes)
|
||||||
|
}
|
||||||
|
return recId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// compactSignatureMalleabilityCheck checks if signature can be used to produce a new valid signature
|
||||||
|
// pulled from go-ethereum/crypto/secp256k1/secp256_test.go
|
||||||
|
// see: http://coders-errand.com/malleability-ecdsa-signatures/
|
||||||
|
func compactSignatureMalleabilityCheck(sig []byte) error {
|
||||||
|
b := int(sig[32])
|
||||||
|
if b < 0 {
|
||||||
|
return fmt.Errorf("highest bit is negative: %d", b)
|
||||||
|
}
|
||||||
|
if ((b >> 7) == 1) != ((b & 0x80) == 0x80) {
|
||||||
|
return fmt.Errorf("highest bit: %d bit >> 7: %d", b, b>>7)
|
||||||
|
}
|
||||||
|
if (b & 0x80) == 0x80 {
|
||||||
|
return fmt.Errorf("highest bit: %d bit & 0x80: %d", b, b&0x80)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicKey returns a decoded secp256k1 public key.
|
||||||
|
func (c *CloudKMSSignatureProvider) GetPublicKey(
|
||||||
|
ctx context.Context,
|
||||||
|
keyName string,
|
||||||
|
) ([]byte, error) {
|
||||||
|
request := kmspb.GetPublicKeyRequest{
|
||||||
|
Name: keyName,
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := c.client.GetPublicKey(ctx, &request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("kms get public key request failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := []byte(result.Pem)
|
||||||
|
if int64(crc32c(key)) != result.PemCrc32C.Value {
|
||||||
|
return nil, errors.New("cloud kms public key response corrupted in transit")
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodePublicKeyPEM(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodePublicKeyPEM decodes a PEM ECDSA public key with secp256k1 curve
|
||||||
|
func decodePublicKeyPEM(key []byte) ([]byte, error) {
|
||||||
|
block, rest := pem.Decode([]byte(key))
|
||||||
|
if len(rest) > 0 {
|
||||||
|
return nil, fmt.Errorf("crypto: failed to parse PEM string, not all bytes in PEM key were decoded: %x", rest)
|
||||||
|
}
|
||||||
|
|
||||||
|
pkBytes, err := x509ParseECDSAPublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("crypto: failed to parse PEM string: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkBytes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// x509ParseECDSAPublicKey parses a DER-encoded public key and ensures secp256k1 curve
|
||||||
|
func x509ParseECDSAPublicKey(derBytes []byte) ([]byte, error) {
|
||||||
|
var pki publicKeyInfo
|
||||||
|
if rest, err := asn1.Unmarshal(derBytes, &pki); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if len(rest) != 0 {
|
||||||
|
return nil, errors.New("x509: trailing data after ASN.1 of public-key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pki.Algorithm.Algorithm.Equal(oidPublicKeyECDSA) {
|
||||||
|
return nil, errors.New("x509: unknown public key algorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
asn1Data := pki.PublicKey.RightAlign()
|
||||||
|
paramsData := pki.Algorithm.Parameters.FullBytes
|
||||||
|
namedCurveOID := new(asn1.ObjectIdentifier)
|
||||||
|
rest, err := asn1.Unmarshal(paramsData, namedCurveOID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("x509: failed to parse ECDSA parameters as named curve: %w", err)
|
||||||
|
}
|
||||||
|
if len(rest) != 0 {
|
||||||
|
return nil, errors.New("x509: trailing data after ECDSA parameters")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !namedCurveOID.Equal(oidNamedCurveSECP256K1) {
|
||||||
|
return nil, errors.New("x509: unsupported elliptic curve")
|
||||||
|
}
|
||||||
|
|
||||||
|
if asn1Data[0] != 4 { // uncompressed form
|
||||||
|
return nil, errors.New("x509: only uncompressed keys are supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
return asn1Data, nil
|
||||||
|
}
|
194
op-signer/service/provider/cloudkms_test.go
Normal file
194
op-signer/service/provider/cloudkms_test.go
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
package provider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"cloud.google.com/go/kms/apiv1/kmspb"
|
||||||
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CloudKMSPemPublicKey = `-----BEGIN PUBLIC KEY-----
|
||||||
|
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEQpdToIk9lwjBdl0VcqXl7AwqhB9NwRf+
|
||||||
|
IHRNqIUNa8vAH/5l5MGXO/qVT5D/4sOTfpd29BQAkDVOgTAneA2Vrg==
|
||||||
|
-----END PUBLIC KEY-----`
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateKey() *ecdsa.PrivateKey {
|
||||||
|
key, err := ecdsa.GenerateKey(secp256k1.S256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
// pulled from go-ethereum/crypto/secp256k1/secp256_test.go
|
||||||
|
func csprngEntropy(n int) []byte {
|
||||||
|
buf := make([]byte, n)
|
||||||
|
if _, err := io.ReadFull(rand.Reader, buf); err != nil {
|
||||||
|
panic("reading from crypto/rand failed: " + err.Error())
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloudKMS_SignDigest(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
mockClient := NewMockCloudKMSClient(ctrl)
|
||||||
|
provider := NewCloudKMSSignatureProviderWithClient(log.Root(), mockClient)
|
||||||
|
|
||||||
|
keyName := "keyName"
|
||||||
|
digest, _ := hexutil.Decode("0x8dabbae6d856bb7ab93bc35b74c1303975a3f70f942d033e8591a9f8c897ae42")
|
||||||
|
derSignature, _ := hexutil.Decode("0x30450221008680faa49fd6653d273fb34393a47efac44b8f4a4de62bbe11a65ee53739e9bb0220350897677c32d67dc1e520d7458c5cca4a7fe49a3e9d74bdef1ec96836148661")
|
||||||
|
pemPublicKey := []byte(CloudKMSPemPublicKey)
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
testName string
|
||||||
|
keyName string
|
||||||
|
digest []byte
|
||||||
|
respError error
|
||||||
|
respVerifiedDigestCrc32C bool
|
||||||
|
respSignatureCrc32 uint32
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"happy path", keyName, digest, nil, true, crc32c(derSignature), false},
|
||||||
|
{"req failure", keyName, digest, assert.AnError, true, crc32c(derSignature), true},
|
||||||
|
{"invalid req keyName", "wrongKeyName", digest, nil, true, crc32c(derSignature), true},
|
||||||
|
{"invalid req crc32", keyName, digest, nil, false, crc32c(derSignature), true},
|
||||||
|
{"invalid resp crc32", keyName, digest, nil, true, crc32c([]byte("")), true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.testName, func(t *testing.T) {
|
||||||
|
signRequest := createSignRequestFromDigest(tt.keyName, tt.digest)
|
||||||
|
mockClient.EXPECT().AsymmetricSign(gomock.Any(), signRequest).Return(
|
||||||
|
&kmspb.AsymmetricSignResponse{
|
||||||
|
Name: keyName,
|
||||||
|
Signature: derSignature,
|
||||||
|
VerifiedDigestCrc32C: tt.respVerifiedDigestCrc32C,
|
||||||
|
SignatureCrc32C: wrapperspb.Int64(int64(tt.respSignatureCrc32)),
|
||||||
|
},
|
||||||
|
tt.respError,
|
||||||
|
)
|
||||||
|
|
||||||
|
getPublicKeyRequest := &kmspb.GetPublicKeyRequest{Name: tt.keyName}
|
||||||
|
mockClient.EXPECT().GetPublicKey(gomock.Any(), getPublicKeyRequest).Return(
|
||||||
|
&kmspb.PublicKey{
|
||||||
|
Pem: string(pemPublicKey),
|
||||||
|
PemCrc32C: wrapperspb.Int64(int64(crc32c(pemPublicKey))),
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
publicKey, _ := decodePublicKeyPEM(pemPublicKey)
|
||||||
|
wantSignature, _ := convertToCompactRecoverableSignature(derSignature, tt.digest, publicKey)
|
||||||
|
|
||||||
|
signature, err := provider.SignDigest(context.TODO(), tt.keyName, tt.digest)
|
||||||
|
if !tt.wantErr {
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, wantSignature, signature)
|
||||||
|
// make sure recoverable pubkey is as expected
|
||||||
|
recoveredPublicKey, err := crypto.Ecrecover(tt.digest, signature)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, publicKey, recoveredPublicKey)
|
||||||
|
} else {
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, signature)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloudKMS_GetPublicKey(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
mockClient := NewMockCloudKMSClient(ctrl)
|
||||||
|
provider := NewCloudKMSSignatureProviderWithClient(log.Root(), mockClient)
|
||||||
|
|
||||||
|
keyName := "keyName"
|
||||||
|
pemPublicKey := []byte(CloudKMSPemPublicKey)
|
||||||
|
wantPublicKey, _ := hexutil.Decode("0x04429753a0893d9708c1765d1572a5e5ec0c2a841f4dc117fe20744da8850d6bcbc01ffe65e4c1973bfa954f90ffe2c3937e9776f4140090354e813027780d95ae")
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
testName string
|
||||||
|
keyName string
|
||||||
|
respError error
|
||||||
|
respPemCrc32 uint32
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"happy path", keyName, nil, crc32c(pemPublicKey), false},
|
||||||
|
{"req failure", keyName, assert.AnError, crc32c(pemPublicKey), false},
|
||||||
|
{"invalid resp crc32", keyName, assert.AnError, crc32c([]byte("")), true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.testName, func(t *testing.T) {
|
||||||
|
request := &kmspb.GetPublicKeyRequest{Name: tt.keyName}
|
||||||
|
mockClient.EXPECT().GetPublicKey(gomock.Any(), request).Return(
|
||||||
|
&kmspb.PublicKey{
|
||||||
|
Pem: string(pemPublicKey),
|
||||||
|
PemCrc32C: wrapperspb.Int64(int64(tt.respPemCrc32)),
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
publicKey, err := provider.GetPublicKey(context.TODO(), tt.keyName)
|
||||||
|
if !tt.wantErr {
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, wantPublicKey, publicKey)
|
||||||
|
} else {
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, publicKey)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestVerifySignatureFromRecoveredPublicKey tests that the compact signature can
|
||||||
|
// recover a publicKey that can be used to verify the original DER signature.
|
||||||
|
// Since all other reference implementations produce compact, recoverable signatures already,
|
||||||
|
// this serves as the test that converToCompactSignture and calculateRecoveryID produce expected results,
|
||||||
|
func TestVerifySignatureFromRecoveredPublicKey(t *testing.T) {
|
||||||
|
key := generateKey()
|
||||||
|
pubKey := crypto.FromECDSAPub(&key.PublicKey)
|
||||||
|
|
||||||
|
const TestCount = 1000
|
||||||
|
for i := 0; i < TestCount; i++ {
|
||||||
|
digest := csprngEntropy(32)
|
||||||
|
derSig, err := ecdsa.SignASN1(rand.Reader, key, digest)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := convertToCompactSignature(derSig)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Len(t, sig, 64)
|
||||||
|
assert.Nil(t, compactSignatureMalleabilityCheck(sig))
|
||||||
|
|
||||||
|
recId, err := calculateRecoveryID(sig, digest, pubKey)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.GreaterOrEqual(t, recId, 0)
|
||||||
|
assert.Less(t, recId, 4)
|
||||||
|
|
||||||
|
sig = append(sig, byte(recId))
|
||||||
|
recoveredRawPubKey, err := secp256k1.RecoverPubkey(digest, sig)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
recoveredPubKey, err := crypto.UnmarshalPubkey(recoveredRawPubKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.True(t, ecdsa.VerifyASN1(recoveredPubKey, digest, derSig))
|
||||||
|
}
|
||||||
|
}
|
77
op-signer/service/provider/mock_kms.go
Normal file
77
op-signer/service/provider/mock_kms.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/ethereum-optimism/infra/op-signer/service/provider (interfaces: CloudKMSClient)
|
||||||
|
|
||||||
|
// Package provider is a generated GoMock package.
|
||||||
|
package provider
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
kmspb "cloud.google.com/go/kms/apiv1/kmspb"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
gax "github.com/googleapis/gax-go/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockCloudKMSClient is a mock of CloudKMSClient interface.
|
||||||
|
type MockCloudKMSClient struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockCloudKMSClientMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockCloudKMSClientMockRecorder is the mock recorder for MockCloudKMSClient.
|
||||||
|
type MockCloudKMSClientMockRecorder struct {
|
||||||
|
mock *MockCloudKMSClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockCloudKMSClient creates a new mock instance.
|
||||||
|
func NewMockCloudKMSClient(ctrl *gomock.Controller) *MockCloudKMSClient {
|
||||||
|
mock := &MockCloudKMSClient{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockCloudKMSClientMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockCloudKMSClient) EXPECT() *MockCloudKMSClientMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsymmetricSign mocks base method.
|
||||||
|
func (m *MockCloudKMSClient) AsymmetricSign(arg0 context.Context, arg1 *kmspb.AsymmetricSignRequest, arg2 ...gax.CallOption) (*kmspb.AsymmetricSignResponse, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
varargs := []interface{}{arg0, arg1}
|
||||||
|
for _, a := range arg2 {
|
||||||
|
varargs = append(varargs, a)
|
||||||
|
}
|
||||||
|
ret := m.ctrl.Call(m, "AsymmetricSign", varargs...)
|
||||||
|
ret0, _ := ret[0].(*kmspb.AsymmetricSignResponse)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsymmetricSign indicates an expected call of AsymmetricSign.
|
||||||
|
func (mr *MockCloudKMSClientMockRecorder) AsymmetricSign(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
varargs := append([]interface{}{arg0, arg1}, arg2...)
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AsymmetricSign", reflect.TypeOf((*MockCloudKMSClient)(nil).AsymmetricSign), varargs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicKey mocks base method.
|
||||||
|
func (m *MockCloudKMSClient) GetPublicKey(arg0 context.Context, arg1 *kmspb.GetPublicKeyRequest, arg2 ...gax.CallOption) (*kmspb.PublicKey, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
varargs := []interface{}{arg0, arg1}
|
||||||
|
for _, a := range arg2 {
|
||||||
|
varargs = append(varargs, a)
|
||||||
|
}
|
||||||
|
ret := m.ctrl.Call(m, "GetPublicKey", varargs...)
|
||||||
|
ret0, _ := ret[0].(*kmspb.PublicKey)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicKey indicates an expected call of GetPublicKey.
|
||||||
|
func (mr *MockCloudKMSClientMockRecorder) GetPublicKey(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
varargs := append([]interface{}{arg0, arg1}, arg2...)
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPublicKey", reflect.TypeOf((*MockCloudKMSClient)(nil).GetPublicKey), varargs...)
|
||||||
|
}
|
65
op-signer/service/provider/mock_provider.go
Normal file
65
op-signer/service/provider/mock_provider.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/ethereum-optimism/infra/op-signer/service/provider (interfaces: SignatureProvider)
|
||||||
|
|
||||||
|
// Package provider is a generated GoMock package.
|
||||||
|
package provider
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockSignatureProvider is a mock of SignatureProvider interface.
|
||||||
|
type MockSignatureProvider struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockSignatureProviderMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockSignatureProviderMockRecorder is the mock recorder for MockSignatureProvider.
|
||||||
|
type MockSignatureProviderMockRecorder struct {
|
||||||
|
mock *MockSignatureProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockSignatureProvider creates a new mock instance.
|
||||||
|
func NewMockSignatureProvider(ctrl *gomock.Controller) *MockSignatureProvider {
|
||||||
|
mock := &MockSignatureProvider{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockSignatureProviderMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockSignatureProvider) EXPECT() *MockSignatureProviderMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicKey mocks base method.
|
||||||
|
func (m *MockSignatureProvider) GetPublicKey(arg0 context.Context, arg1 string) ([]byte, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetPublicKey", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].([]byte)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicKey indicates an expected call of GetPublicKey.
|
||||||
|
func (mr *MockSignatureProviderMockRecorder) GetPublicKey(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPublicKey", reflect.TypeOf((*MockSignatureProvider)(nil).GetPublicKey), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignDigest mocks base method.
|
||||||
|
func (m *MockSignatureProvider) SignDigest(arg0 context.Context, arg1 string, arg2 []byte) ([]byte, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SignDigest", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].([]byte)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignDigest indicates an expected call of SignDigest.
|
||||||
|
func (mr *MockSignatureProviderMockRecorder) SignDigest(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignDigest", reflect.TypeOf((*MockSignatureProvider)(nil).SignDigest), arg0, arg1, arg2)
|
||||||
|
}
|
9
op-signer/service/provider/provider.go
Normal file
9
op-signer/service/provider/provider.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
//go:generate mockgen -destination=mock_provider.go -package=provider github.com/ethereum-optimism/infra/op-signer/service/provider SignatureProvider
|
||||||
|
package provider
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type SignatureProvider interface {
|
||||||
|
SignDigest(ctx context.Context, keyName string, digest []byte) ([]byte, error)
|
||||||
|
GetPublicKey(ctx context.Context, keyName string) ([]byte, error)
|
||||||
|
}
|
148
op-signer/service/service.go
Normal file
148
op-signer/service/service.go
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
"github.com/ethereum-optimism/infra/op-signer/service/provider"
|
||||||
|
oprpc "github.com/ethereum-optimism/optimism/op-service/rpc"
|
||||||
|
"github.com/ethereum-optimism/optimism/op-service/signer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SignerService struct {
|
||||||
|
logger log.Logger
|
||||||
|
config SignerServiceConfig
|
||||||
|
provider provider.SignatureProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSignerService(logger log.Logger, config SignerServiceConfig) *SignerService {
|
||||||
|
return NewSignerServiceWithProvider(logger, config, provider.NewCloudKMSSignatureProvider(logger))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSignerServiceWithProvider(
|
||||||
|
logger log.Logger,
|
||||||
|
config SignerServiceConfig,
|
||||||
|
provider provider.SignatureProvider,
|
||||||
|
) *SignerService {
|
||||||
|
return &SignerService{logger, config, provider}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SignerService) RegisterAPIs(server *oprpc.Server) {
|
||||||
|
server.AddAPI(rpc.API{
|
||||||
|
Namespace: "eth",
|
||||||
|
Service: s,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsNormalized(s []string, e string) bool {
|
||||||
|
for _, a := range s {
|
||||||
|
if strings.EqualFold(a, e) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignTransaction will sign the given transaction with the key configured for the authenticated client
|
||||||
|
func (s *SignerService) SignTransaction(ctx context.Context, args signer.TransactionArgs) (hexutil.Bytes, error) {
|
||||||
|
clientInfo := ClientInfoFromContext(ctx)
|
||||||
|
authConfig, err := s.config.GetAuthConfigForClient(clientInfo.ClientName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, rpc.HTTPError{StatusCode: 403, Status: "Forbidden", Body: []byte(err.Error())}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := prometheus.Labels{"client": clientInfo.ClientName, "status": "error", "error": ""}
|
||||||
|
defer func() {
|
||||||
|
MetricSignTransactionTotal.With(labels).Inc()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := args.Check(); err != nil {
|
||||||
|
s.logger.Warn("invalid signing arguments", "err", err)
|
||||||
|
labels["error"] = "invalid_transaction"
|
||||||
|
return nil, &InvalidTransactionError{message: err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(authConfig.ToAddresses) > 0 && !containsNormalized(authConfig.ToAddresses, args.To.Hex()) {
|
||||||
|
return nil, &UnauthorizedTransactionError{"to address not authorized"}
|
||||||
|
}
|
||||||
|
if len(authConfig.MaxValue) > 0 && args.Value.ToInt().Cmp(authConfig.MaxValueToInt()) > 0 {
|
||||||
|
return nil, &UnauthorizedTransactionError{"value exceeds maximum"}
|
||||||
|
}
|
||||||
|
|
||||||
|
txData, err := args.ToTransactionData()
|
||||||
|
if err != nil {
|
||||||
|
labels["error"] = "transaction_args_error"
|
||||||
|
return nil, &InvalidTransactionError{err.Error()}
|
||||||
|
}
|
||||||
|
tx := types.NewTx(txData)
|
||||||
|
|
||||||
|
txSigner := types.LatestSignerForChainID(tx.ChainId())
|
||||||
|
digest := txSigner.Hash(tx)
|
||||||
|
|
||||||
|
signature, err := s.provider.SignDigest(ctx, authConfig.KeyName, digest.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
labels["error"] = "sign_error"
|
||||||
|
return nil, &InvalidTransactionError{err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
signed, err := tx.WithSignature(txSigner, signature)
|
||||||
|
if err != nil {
|
||||||
|
labels["error"] = "invalid_transaction_error"
|
||||||
|
return nil, &InvalidTransactionError{err.Error()}
|
||||||
|
}
|
||||||
|
signerFrom, err := txSigner.Sender(signed)
|
||||||
|
if err != nil {
|
||||||
|
labels["error"] = "sign_error"
|
||||||
|
return nil, &InvalidTransactionError{err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanity check that we used the right account
|
||||||
|
if args.From != nil && *args.From != signerFrom {
|
||||||
|
s.logger.Warn("user is trying to sign with different account than actual signer-provider",
|
||||||
|
"provider", signerFrom, "request", *args.From)
|
||||||
|
labels["error"] = "sign_error"
|
||||||
|
return nil, &InvalidTransactionError{"unexpected from address"}
|
||||||
|
}
|
||||||
|
|
||||||
|
txraw, err := signed.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
labels["error"] = "transaction_marshal_error"
|
||||||
|
return nil, &InvalidTransactionError{err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels["status"] = "success"
|
||||||
|
txTo := ""
|
||||||
|
if tx.To() != nil {
|
||||||
|
txTo = tx.To().Hex()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Info(
|
||||||
|
"Signed transaction",
|
||||||
|
"digest", hexutil.Encode(digest.Bytes()),
|
||||||
|
"client.name", clientInfo.ClientName,
|
||||||
|
"client.keyname", authConfig.KeyName,
|
||||||
|
"tx.type", tx.Type(),
|
||||||
|
"tx.raw", hexutil.Encode(txraw),
|
||||||
|
"tx.value", tx.Value(),
|
||||||
|
"tx.to", txTo,
|
||||||
|
"tx.nonce", tx.Nonce(),
|
||||||
|
"tx.gas", tx.Gas(),
|
||||||
|
"tx.gasprice", tx.GasPrice(),
|
||||||
|
"tx.gastipcap", tx.GasTipCap(),
|
||||||
|
"tx.gasfeecap", tx.GasFeeCap(),
|
||||||
|
"tx.type", tx.Type(),
|
||||||
|
"tx.hash", tx.Hash().Hex(),
|
||||||
|
"tx.chainid", tx.ChainId(),
|
||||||
|
"tx.blobhashes", tx.BlobHashes(),
|
||||||
|
"tx.blobfeecap", tx.BlobGasFeeCap(),
|
||||||
|
"signature", hexutil.Encode(signature),
|
||||||
|
)
|
||||||
|
|
||||||
|
return hexutil.Bytes(txraw), nil
|
||||||
|
}
|
182
op-signer/service/service_test.go
Normal file
182
op-signer/service/service_test.go
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
|
||||||
|
"github.com/ethereum-optimism/infra/op-signer/service/provider"
|
||||||
|
clientSigner "github.com/ethereum-optimism/optimism/op-service/signer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createEIP1559Tx() *types.Transaction {
|
||||||
|
aa := common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||||
|
accesses := types.AccessList{types.AccessTuple{
|
||||||
|
Address: aa,
|
||||||
|
StorageKeys: []common.Hash{{0}},
|
||||||
|
}}
|
||||||
|
txdata := &types.DynamicFeeTx{
|
||||||
|
ChainID: params.AllEthashProtocolChanges.ChainID,
|
||||||
|
Nonce: 0,
|
||||||
|
To: &aa,
|
||||||
|
Gas: 30000,
|
||||||
|
GasFeeCap: big.NewInt(1),
|
||||||
|
GasTipCap: big.NewInt(1),
|
||||||
|
AccessList: accesses,
|
||||||
|
Data: []byte{},
|
||||||
|
Value: big.NewInt(1),
|
||||||
|
}
|
||||||
|
tx := types.NewTx(txdata)
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
|
func createBlobTx() *types.Transaction {
|
||||||
|
aa := common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||||
|
accesses := types.AccessList{types.AccessTuple{
|
||||||
|
Address: aa,
|
||||||
|
StorageKeys: []common.Hash{{0}},
|
||||||
|
}}
|
||||||
|
|
||||||
|
txdata := &types.BlobTx{
|
||||||
|
ChainID: uint256.MustFromBig(params.AllEthashProtocolChanges.ChainID),
|
||||||
|
Nonce: 0,
|
||||||
|
To: aa,
|
||||||
|
Gas: 30000,
|
||||||
|
GasFeeCap: uint256.NewInt(1),
|
||||||
|
GasTipCap: uint256.NewInt(1),
|
||||||
|
AccessList: accesses,
|
||||||
|
Data: []byte{},
|
||||||
|
Value: uint256.NewInt(1),
|
||||||
|
BlobFeeCap: uint256.NewInt(1),
|
||||||
|
BlobHashes: []common.Hash{common.HexToHash("c0ffee")},
|
||||||
|
}
|
||||||
|
tx := types.NewTx(txdata)
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = SignerServiceConfig{
|
||||||
|
Auth: []AuthConfig{
|
||||||
|
{ClientName: "client.oplabs.co", KeyName: "keyName"},
|
||||||
|
{ClientName: "alt-client.oplabs.co", KeyName: "altKeyName"},
|
||||||
|
{ClientName: "authorized-to.oplabs.co", KeyName: "keyName", ToAddresses: []string{"0x000000000000000000000000000000000000Aaaa"}},
|
||||||
|
{ClientName: "unauthorized-to.oplabs.co", KeyName: "keyName", ToAddresses: []string{"0x000000000000000000000000000000000000bbbb"}},
|
||||||
|
{ClientName: "within-max-value.oplabs.co", KeyName: "keyName", MaxValue: hexutil.EncodeBig(big.NewInt(2))},
|
||||||
|
{ClientName: "exceeds-max-value.oplabs.co", KeyName: "keyName", MaxValue: hexutil.EncodeBig(big.NewInt(0))},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
name string
|
||||||
|
template func() *types.Transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
var testTxs = []testCase{
|
||||||
|
{"regular", createEIP1559Tx},
|
||||||
|
{"blob-tx", createBlobTx},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignTransaction(t *testing.T) {
|
||||||
|
for _, tc := range testTxs {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
testSignTransaction(t, tc.template())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSignTransaction(t *testing.T, tx *types.Transaction) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
signer := types.LatestSignerForChainID(tx.ChainId())
|
||||||
|
digest := signer.Hash(tx).Bytes()
|
||||||
|
|
||||||
|
priv, err := crypto.GenerateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
sender := crypto.PubkeyToAddress(priv.PublicKey)
|
||||||
|
|
||||||
|
signature, err := crypto.Sign(digest, priv)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
args := clientSigner.NewTransactionArgsFromTransaction(tx.ChainId(), nil, tx)
|
||||||
|
missingNonce := clientSigner.NewTransactionArgsFromTransaction(tx.ChainId(), nil, tx)
|
||||||
|
missingNonce.Nonce = nil
|
||||||
|
|
||||||
|
validFrom := clientSigner.NewTransactionArgsFromTransaction(tx.ChainId(), nil, tx)
|
||||||
|
validFrom.From = &sender
|
||||||
|
|
||||||
|
invalidFrom := clientSigner.NewTransactionArgsFromTransaction(tx.ChainId(), nil, tx)
|
||||||
|
random := common.HexToAddress("1234")
|
||||||
|
invalidFrom.From = &random
|
||||||
|
|
||||||
|
// signature, _ := hexutil.Decode("0x5392c93b50eb9e3412ab43d378048d4f7d644f3cea02acb529f07e2babba1d3a332377f4abe24a40030b3ff6bff3413a44364aad4665f4e24117466328ce8d3600")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
testName string
|
||||||
|
args clientSigner.TransactionArgs
|
||||||
|
digest []byte
|
||||||
|
clientName string
|
||||||
|
wantKeyName string
|
||||||
|
wantErrCode int
|
||||||
|
}{
|
||||||
|
{"happy path", *args, digest, "client.oplabs.co", "keyName", 0},
|
||||||
|
{"nonce not specified", *missingNonce, digest, "client.oplabs.co", "keyName", -32010},
|
||||||
|
{"happy path - different client and key", *args, digest, "alt-client.oplabs.co", "altKeyName", 0},
|
||||||
|
{"client not authorized", *args, digest, "forbidden-client.oplabs.co", "keyName", 403},
|
||||||
|
{"client empty", *args, digest, "", "", 403},
|
||||||
|
{"authorized to address", *args, digest, "authorized-to.oplabs.co", "keyName", 0},
|
||||||
|
{"unauthorized to address", *args, digest, "unauthorized-to.oplabs.co", "keyName", -32011},
|
||||||
|
{"within max value", *args, digest, "within-max-value.oplabs.co", "keyName", 0},
|
||||||
|
{"exceeds max value", *args, digest, "exceeds-max-value.oplabs.co", "keyName", -32011},
|
||||||
|
{"valid from", *validFrom, digest, "client.oplabs.co", "keyName", 0},
|
||||||
|
{"invalid from", *invalidFrom, digest, "client.oplabs.co", "keyName", -32010},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.testName, func(t *testing.T) {
|
||||||
|
mockSignatureProvider := provider.NewMockSignatureProvider(ctrl)
|
||||||
|
service := NewSignerServiceWithProvider(log.Root(), config, mockSignatureProvider)
|
||||||
|
|
||||||
|
ctx := context.WithValue(context.TODO(), clientInfoContextKey{}, ClientInfo{ClientName: tt.clientName})
|
||||||
|
if tt.wantErrCode == 0 || tt.testName == "invalid from" {
|
||||||
|
mockSignatureProvider.EXPECT().
|
||||||
|
SignDigest(ctx, tt.wantKeyName, tt.digest).
|
||||||
|
Return(signature, nil)
|
||||||
|
}
|
||||||
|
resp, err := service.SignTransaction(ctx, tt.args)
|
||||||
|
if tt.wantErrCode == 0 {
|
||||||
|
assert.Nil(t, err)
|
||||||
|
if assert.NotNil(t, resp) {
|
||||||
|
assert.NotEmpty(t, resp)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
assert.Nil(t, resp)
|
||||||
|
var rpcErr rpc.Error
|
||||||
|
var httpErr rpc.HTTPError
|
||||||
|
if errors.As(err, &rpcErr) {
|
||||||
|
assert.Equal(t, tt.wantErrCode, rpcErr.ErrorCode())
|
||||||
|
} else if errors.As(err, &httpErr) {
|
||||||
|
assert.Equal(t, tt.wantErrCode, httpErr.StatusCode)
|
||||||
|
} else {
|
||||||
|
assert.Fail(t, "returned error is not an rpc.Error or rpc.HTTPError")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
53
op-signer/test-rpc.json
Normal file
53
op-signer/test-rpc.json
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_signTransaction",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"to": "0x000000000000000000000000000000000000aaaa",
|
||||||
|
"gas": "0x7530",
|
||||||
|
"gasPrice": null,
|
||||||
|
"maxFeePerGas": "0x1",
|
||||||
|
"maxPriorityFeePerGas": "0x1",
|
||||||
|
"value": "0x1",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"data": "0x",
|
||||||
|
"accessList": [
|
||||||
|
{
|
||||||
|
"address": "0x000000000000000000000000000000000000aaaa",
|
||||||
|
"storageKeys": [
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"chainId": "0x539"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_signTransaction",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"to": "0x000000000000000000000000000000000000aaaa",
|
||||||
|
"gas": "0x7530",
|
||||||
|
"gasPrice": null,
|
||||||
|
"maxFeePerGas": "0x1",
|
||||||
|
"maxPriorityFeePerGas": "0x1",
|
||||||
|
"value": "0x1",
|
||||||
|
"data": "0x",
|
||||||
|
"accessList": [
|
||||||
|
{
|
||||||
|
"address": "0x000000000000000000000000000000000000aaaa",
|
||||||
|
"storageKeys": [
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"chainId": "0x539"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
@ -11,6 +11,7 @@ import semver
|
|||||||
# Minimum version numbers for packages migrating from legacy versioning.
|
# Minimum version numbers for packages migrating from legacy versioning.
|
||||||
MIN_VERSIONS = {
|
MIN_VERSIONS = {
|
||||||
'proxyd': '4.6.1',
|
'proxyd': '4.6.1',
|
||||||
|
'op-signer': '0.0.1',
|
||||||
}
|
}
|
||||||
|
|
||||||
VALID_BUMPS = ('major', 'minor', 'patch', 'prerelease', 'finalize-prerelease')
|
VALID_BUMPS = ('major', 'minor', 'patch', 'prerelease', 'finalize-prerelease')
|
||||||
|
@ -6,6 +6,7 @@ import semver
|
|||||||
SERVICES = [
|
SERVICES = [
|
||||||
'proxyd',
|
'proxyd',
|
||||||
'op-ufm',
|
'op-ufm',
|
||||||
|
'op-signer',
|
||||||
'op-conductor-mon',
|
'op-conductor-mon',
|
||||||
]
|
]
|
||||||
VERSION_PATTERN = '^{service}/v\\d+\\.\\d+\\.\\d+(-rc\\.\\d+)?$'
|
VERSION_PATTERN = '^{service}/v\\d+\\.\\d+\\.\\d+(-rc\\.\\d+)?$'
|
||||||
|
Loading…
Reference in New Issue
Block a user