From 6aecbcccfa77a8fa731b1b0db4f512097197bfe3 Mon Sep 17 00:00:00 2001 From: Borja Aranda Date: Thu, 15 Jun 2023 18:07:00 +0200 Subject: [PATCH] Add timelock-worker codebase --- .github/dependabot.yml | 13 + .github/workflows/build.yml | 30 + .github/workflows/lint.yml | 29 + .github/workflows/release.yml | 36 + .github/workflows/test.yml | 32 + .gitignore | 23 + .golangci.yml | 52 + .goreleaser.yml | 45 + .tool-versions | 2 + Makefile | 44 + README.md | 18 + builds/Dockerfile | 24 + cmd/root.go | 58 + cmd/start.go | 86 + cmd/version.go | 26 + coverage.out | 167 ++ docs/BUILD.md | 39 + docs/CONFIG.md | 121 ++ docs/DEVELOPMENT.md | 46 + docs/README.md | 6 + docs/USAGE.md | 73 + docs/timelock.abi.md | 62 + go.mod | 55 + go.sum | 644 +++++++ main.go | 7 + pkg/cli/config.go | 69 + pkg/cli/config_test.go | 157 ++ pkg/logger/logger.go | 53 + pkg/logger/logger_test.go | 58 + pkg/timelock/const.go | 19 + pkg/timelock/const_test.go | 18 + pkg/timelock/contract/Timelock.go | 2617 +++++++++++++++++++++++++++ pkg/timelock/contract/Timelock.json | 611 +++++++ pkg/timelock/contract/Timelock.sol | 491 +++++ pkg/timelock/operations.go | 144 ++ pkg/timelock/operations_test.go | 205 +++ pkg/timelock/scheduler.go | 174 ++ pkg/timelock/scheduler_test.go | 78 + pkg/timelock/timelock.go | 286 +++ pkg/timelock/timelock_test.go | 193 ++ scripts/golangci.yml | 52 + scripts/golangci_html_report.sh | 5 + timelock.env | 6 + 43 files changed, 6974 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100644 .golangci.yml create mode 100644 .goreleaser.yml create mode 100644 .tool-versions create mode 100644 Makefile create mode 100644 README.md create mode 100644 builds/Dockerfile create mode 100644 cmd/root.go create mode 100644 cmd/start.go create mode 100644 cmd/version.go create mode 100644 coverage.out create mode 100644 docs/BUILD.md create mode 100644 docs/CONFIG.md create mode 100644 docs/DEVELOPMENT.md create mode 100644 docs/README.md create mode 100644 docs/USAGE.md create mode 100644 docs/timelock.abi.md create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 pkg/cli/config.go create mode 100644 pkg/cli/config_test.go create mode 100644 pkg/logger/logger.go create mode 100644 pkg/logger/logger_test.go create mode 100644 pkg/timelock/const.go create mode 100644 pkg/timelock/const_test.go create mode 100644 pkg/timelock/contract/Timelock.go create mode 100644 pkg/timelock/contract/Timelock.json create mode 100644 pkg/timelock/contract/Timelock.sol create mode 100644 pkg/timelock/operations.go create mode 100644 pkg/timelock/operations_test.go create mode 100644 pkg/timelock/scheduler.go create mode 100644 pkg/timelock/scheduler_test.go create mode 100644 pkg/timelock/timelock.go create mode 100644 pkg/timelock/timelock_test.go create mode 100644 scripts/golangci.yml create mode 100755 scripts/golangci_html_report.sh create mode 100644 timelock.env diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..74c3d55 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + - package-ecosystem: gomod + directory: '/' + schedule: + interval: weekly + open-pull-requests-limit: 10 + + - package-ecosystem: github-actions + directory: '/' + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..b9db0c1 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,30 @@ +name: Build + +on: + push: + paths: + - "cmd/**" + - "pkg/**" + - ".github/workflows/build.yml" + +jobs: + build_bif: + name: Build + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - uses: smartcontractkit/tool-versions-to-env-action@v1.0.8 + id: tool-versions + + - name: Setup go ${{ steps.tool-versions.outputs.golang_version }} + uses: actions/setup-go@v4 + with: + go-version: ${{ steps.tool-versions.outputs.golang_version }} + + - name: Make build + run: make build + + - name: Make clean + run: make clean diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..56f3907 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,29 @@ +name: Verify code + +on: + push: + paths: + - "**.go" + +jobs: + golang_lint: + name: Golang Lint + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v3 + + - uses: smartcontractkit/tool-versions-to-env-action@v1.0.8 + id: tool-versions + + - name: Setup go ${{ steps.tool-versions.outputs.golang_version }} + uses: actions/setup-go@v4 + with: + go-version: ${{ steps.tool-versions.outputs.golang_version }} + + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: v${{ steps.tool-versions.outputs.golangci-lint_version }} + args: -v --timeout=5m + only-new-issues: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..bea8543 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,36 @@ +name: Timelock Worker Release + +on: + push: + tags: + - "v*" + +jobs: + goreleaser: + runs-on: ubuntu-latest + permissions: + contents: write # Required to upload dist files + steps: + - name: Checkout sources + uses: actions/checkout@v3 + + - uses: smartcontractkit/tool-versions-to-env-action@v1.0.8 + id: tool-versions + + - name: Setup go ${{ steps.tool-versions.outputs.golang_version }} + uses: actions/setup-go@v4 + with: + go-version: ${{ steps.tool-versions.outputs.golang_version }} + + - name: Run goreleaser + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - uses: actions/upload-artifact@v3 + with: + name: dist + path: ./dist/* \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e1a2f9f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,32 @@ +name: Test + +on: + push: + paths: + - "cmd/**" + - "pkg/**" + - ".github/workflows/test.yml" + +jobs: + test_bif: + name: Unit test + runs-on: ubuntu-latest + env: + TEST_NODE_URL: '${{ secrets.TEST_NODE_URL }}' + TEST_TIMELOCK_ADDRESS: '${{ secrets.TEST_TIMELOCK_ADDRESS }}' + TEST_PROXY_ADDRESS: '${{ secrets.TEST_PROXY_ADDRESS }}' + TEST_PRIVATE_KEY: '${{ secrets.TEST_PRIVATE_KEY }}' + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - uses: smartcontractkit/tool-versions-to-env-action@v1.0.8 + id: tool-versions + + - name: Setup go ${{ steps.tool-versions.outputs.golang_version }} + uses: actions/setup-go@v4 + with: + go-version: ${{ steps.tool-versions.outputs.golang_version }} + + - name: Test + run: make test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f1f2704 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +bin/ +.idea/ +dist/ +MacOSX* + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +!*.c +report.html +timelock-worker.log + +# Test binary, built with `go test -c` +*.test + +# env file +.env + + diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..b139663 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,52 @@ +run: + timeout: 10m + tests: false + modules-download-mode: readonly +output: + format: github-actions +linters: + disable-all: true + enable: + - asasalint + - bidichk + - bodyclose + - containedctx + - contextcheck + - dogsled + - dupl + - errcheck + - errname + - errorlint + - exhaustive + - exportloopref + - gochecknoinits + - godot + - gofmt + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - makezero + - misspell + - nakedret + - nilerr + - nilnil + - noctx + - nolintlint + - predeclared + - promlinter + - staticcheck + - tenv + - testpackage + - thelper + - tparallel + - typecheck + - unconvert + - unparam + - whitespace + issues: + max-issues-per-linter: 0 + max-same-issues: 0 + severity: + default-severity: error diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..20b045e --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,45 @@ +project_name: timelock-worker + +before: + hooks: + - go mod tidy + +builds: + - binary: timelock-worker + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + goarch: + - amd64 + - arm64 + main: ./main.go + flags: + - -trimpath + - -buildmode=pie + ldflags: + - -s -w + - -X github.com/smartcontractkit/timelock-worker/cmd.Version={{ .Version }} + - -X github.com/smartcontractkit/timelock-worker/cmd.Commit={{ .FullCommit }} +archives: + - id: timelock-worker + name_template: >- + {{ .ProjectName }}_ + {{- if eq .Os "darwin" }}macos + {{- else }}{{ .Os }}{{ end }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} +source: + enabled: true +checksum: + name_template: "checksums.txt" +snapshot: + name_template: "{{ .Tag }}-dev" +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..c3eabc1 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +golang 1.20 +golangci-lint 1.52.2 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5dd0551 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +# environment variables +BINARY := timelock-worker +PACKAGE := github.com/smartcontractkit/timelock-worker +VERSION := $(shell git describe --tags --always --abbrev=0 --match='v[0-9]*.[0-9]*.[0-9]*' 2> /dev/null | sed 's/^.//') +COMMIT_HASH := $(shell git rev-parse HEAD) +LDFLAGS := "-X $(PACKAGE)/cmd.Version=$(VERSION) -X $(PACKAGE)/cmd.Commit=$(COMMIT_HASH) -w -s" + +# Folders +BIN_FOLDER := bin +SCRIPTS_F := scripts + +# assets +C_GREEN=\033[0;32m +C_RED=\033[0;31m +C_BLUE=\033[0;34m +C_END=\033[0m + +########## +# builds # +########## + +.PHONY: all +all: clean test build + +.PHONY: clean +clean: + @echo "\n\t$(C_GREEN)# Cleaning environment$(C_END)" + go clean -x + rm -rf $(BIN_FOLDER) + rm -f $(SCRIPTS_F)/report.html + +.PHONY: test +test: + @echo "\n\t$(C_GREEN)# Run test and generate new coverage.out$(C_END)" + go test -short -coverprofile=coverage.out -covermode=atomic -race ./... + +.PHONY: build +build: clean + @echo "\n\t$(C_GREEN)# Build binary $(BINARY)$(C_END)" + go build -trimpath -ldflags $(LDFLAGS) -o $(BIN_FOLDER)/$(BINARY) main.go + +.PHONY: lint +lint: + $(SCRIPTS_F)/golangci_html_report.sh \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f387e87 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +
+

Timelock Worker

+ + + +
+
+ Documentation +   •   + Build +   •   + Configuration +   •   + Usage +   •   + Development +
+
diff --git a/builds/Dockerfile b/builds/Dockerfile new file mode 100644 index 0000000..5d3949f --- /dev/null +++ b/builds/Dockerfile @@ -0,0 +1,24 @@ +# Builder image +FROM quay.io/centos/centos:stream8 as builder + +USER root + +# Dependencies +RUN dnf install -y golang git +RUN mkdir /build +WORKDIR /build + +# Golang build +COPY go.mod . +COPY go.sum . +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -a -installsuffix cgo -ldflags '-w -s -extldflags "-static"' -o timelock-worker . + +# Final image +FROM quay.io/centos/centos:stream8 +COPY --from=builder /build/timelock-worker /app/ +WORKDIR /app + +ENTRYPOINT ["./timelock-worker"] +CMD ["start", "--log-level", "${LOGLEVEL:-debug}"] diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..ae2e4d6 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,58 @@ +package cmd + +import ( + "os" + + "github.com/rs/zerolog" + "github.com/smartcontractkit/timelock-worker/pkg/logger" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var ( + rootCmd = &cobra.Command{ + Use: "timelock-worker", + Short: "Pull and execute scheduled transactions from Timelock contract", + } + + logs *zerolog.Logger + logLevel, output string +) + +func Execute() { + if err := configureRootCmd(); err != nil { + os.Exit(1) + } + + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} + +func configureRootCmd() error { + cobra.OnInitialize(initConfig) + + rootCmd.AddCommand( + versionCommand(), + startCommand(), + ) + + // Global flags + rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "l", "info", "set logging level (trace, debug, info, warn, error, fatal)") + if err := viper.BindPFlag("log-level", rootCmd.PersistentFlags().Lookup("log-level")); err != nil { + return err + } + + rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "human", "set logging output (human, json)") + if err := viper.BindPFlag("output", rootCmd.PersistentFlags().Lookup("output")); err != nil { + return err + } + + return nil +} + +func initConfig() { + // Output hardcoded to JSON + logs = logger.Logger(viper.GetString("log-level"), viper.GetString("output")) + logs.Debug().Msgf("initialized Logger") +} diff --git a/cmd/start.go b/cmd/start.go new file mode 100644 index 0000000..118775f --- /dev/null +++ b/cmd/start.go @@ -0,0 +1,86 @@ +package cmd + +import ( + "context" + "math/big" + + "github.com/smartcontractkit/timelock-worker/pkg/cli" + "github.com/smartcontractkit/timelock-worker/pkg/timelock" + "github.com/spf13/cobra" +) + +func startCommand() *cobra.Command { + var ( + startCmd = cobra.Command{ + Use: "start", + Short: "Starts the Timelock Worker daemon", + Run: startHandler, + } + + nodeURL, privateKey, timelockAddress, callProxyAddress string + fromBlock, pollPeriod int64 + ) + + // Initialize timelock-worker configuration. + // Precedence: flags > env variables > timelock.env file. + timelockConf, err := cli.NewTimelockCLI() + if err != nil { + logs.Fatal().Msgf("error initializing configuration: %s", err.Error()) + } + + startCmd.Flags().StringVarP(&nodeURL, "node-url", "n", timelockConf.NodeURL, "RPC Endpoint for the target blockchain") + startCmd.Flags().StringVarP(&timelockAddress, "timelock-address", "a", timelockConf.TimelockAddress, "Address of the target Timelock contract") + startCmd.Flags().StringVarP(&callProxyAddress, "call-proxy-address", "f", timelockConf.CallProxyAddress, "Address of the target CallProxyAddress contract") + startCmd.Flags().StringVarP(&privateKey, "private-key", "k", timelockConf.PrivateKey, "Private key used to execute transactions") + startCmd.Flags().Int64Var(&fromBlock, "from-block", timelockConf.FromBlock, "Start watching from this block") + startCmd.Flags().Int64Var(&pollPeriod, "poll-period", timelockConf.PollPeriod, "Poll period in seconds") + + return &startCmd +} + +func startHandler(cmd *cobra.Command, _ []string) { + // Use this ctx as the base context. + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + nodeURL, err := cmd.Flags().GetString("node-url") + if err != nil { + logs.Fatal().Msgf("value of node-url not set: %s", err.Error()) + } + + timelockAddress, err := cmd.Flags().GetString("timelock-address") + if err != nil { + logs.Fatal().Msgf("value of timelock-address not set: %s", err.Error()) + } + + callProxyAddress, err := cmd.Flags().GetString("call-proxy-address") + if err != nil { + logs.Fatal().Msgf("value of call-proxy-address not set: %s", err.Error()) + } + + privateKey, err := cmd.Flags().GetString("private-key") + if err != nil { + logs.Fatal().Msgf("value of private-key not set: %s", err.Error()) + } + + fromBlock, err := cmd.Flags().GetInt64("from-block") + if err != nil { + logs.Fatal().Msgf("value of from-block not set: %s", err.Error()) + } + + pollPeriod, err := cmd.Flags().GetInt64("poll-period") + if err != nil { + logs.Fatal().Msgf("value of poll-period not set: %s", err.Error()) + } + + tWorker, err := timelock.NewTimelockWorker(nodeURL, timelockAddress, callProxyAddress, privateKey, big.NewInt(fromBlock), pollPeriod, logs) + if err != nil { + logs.Fatal().Msgf("error creating the timelock-worker: %s", err.Error()) + } + + if err := tWorker.Listen(ctx); err != nil { + logs.Fatal().Msgf("error while starting timelock-worker: %s", err.Error()) + } + + logs.Info().Msg("shutting down timelock-worker") +} diff --git a/cmd/version.go b/cmd/version.go new file mode 100644 index 0000000..f1a5bde --- /dev/null +++ b/cmd/version.go @@ -0,0 +1,26 @@ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" +) + +var ( + Version = "development" //nolint: gochecknoglobals + Commit = "0.0.0" //nolint: gochecknoglobals +) + +func versionCommand() *cobra.Command { + versionCmd := cobra.Command{ + Use: "version", + Short: "Displays the build version", + Run: versionHandler, + } + + return &versionCmd +} + +func versionHandler(_ *cobra.Command, _ []string) { + logs.Info().Msgf("%s Version: %s Commit: %s", os.Args[0], Version, Commit) +} diff --git a/coverage.out b/coverage.out new file mode 100644 index 0000000..f0f2273 --- /dev/null +++ b/coverage.out @@ -0,0 +1,167 @@ +mode: atomic +github.com/smartcontractkit/timelock-worker/pkg/logger/logger.go:23.61,24.19 1 4 +github.com/smartcontractkit/timelock-worker/pkg/logger/logger.go:24.19,26.11 1 1 +github.com/smartcontractkit/timelock-worker/pkg/logger/logger.go:26.11,28.5 1 1 +github.com/smartcontractkit/timelock-worker/pkg/logger/logger.go:31.2,31.15 1 4 +github.com/smartcontractkit/timelock-worker/pkg/logger/logger.go:35.64,37.16 2 1 +github.com/smartcontractkit/timelock-worker/pkg/logger/logger.go:37.16,40.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/logger/logger.go:42.2,43.16 2 1 +github.com/smartcontractkit/timelock-worker/pkg/logger/logger.go:44.15,45.79 1 1 +github.com/smartcontractkit/timelock-worker/pkg/logger/logger.go:46.14,47.70 1 0 +github.com/smartcontractkit/timelock-worker/pkg/logger/logger.go:48.10,49.79 1 0 +github.com/smartcontractkit/timelock-worker/pkg/logger/logger.go:52.2,52.11 1 1 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:21.40,38.33 7 2 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:38.33,40.3 1 1 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:42.2,42.41 1 2 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:42.41,44.3 1 1 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:46.2,46.43 1 2 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:46.43,48.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:50.2,50.36 1 2 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:50.36,52.3 1 2 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:54.2,54.35 1 2 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:54.35,56.17 2 2 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:56.17,58.4 1 2 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:61.2,61.36 1 2 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:61.36,63.17 2 1 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:63.17,65.4 1 1 +github.com/smartcontractkit/timelock-worker/pkg/cli/config.go:68.2,68.16 1 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:19.86,20.41 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:20.41,24.30 3 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:24.30,26.4 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:26.9,29.66 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:29.66,31.5 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:33.8,35.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:39.175,41.16 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:41.16,43.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:46.2,47.24 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:47.24,53.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:57.2,65.16 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:65.16,67.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:69.2,69.16 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:74.79,76.16 2 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:76.16,78.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:80.2,80.13 1 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:85.75,87.16 2 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:87.16,89.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:91.2,91.16 1 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:95.74,97.16 2 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:97.16,99.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:101.2,101.15 1 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:106.77,108.16 2 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:108.16,110.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:112.2,112.18 1 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:116.88,117.20 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:117.20,119.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:121.2,122.16 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:122.16,124.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:126.2,127.16 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:127.16,129.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:131.2,131.22 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:137.80,139.9 2 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:139.9,141.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/operations.go:143.2,143.53 1 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:30.50,40.2 2 13 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:49.53,50.6 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:50.6,51.10 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:52.22,53.26 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:53.26,55.13 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:58.4,58.29 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:58.29,61.33 3 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:61.33,63.6 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:64.5,64.26 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:65.10,67.5 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:69.23,71.53 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:71.53,73.5 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:74.4,76.60 3 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:78.23,79.33 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:79.33,84.5 4 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:90.57,91.11 1 3 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:91.11,94.3 2 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:94.8,96.3 1 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:100.70,104.2 3 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:107.53,111.2 3 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:113.38,118.2 4 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:120.38,125.2 4 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:127.42,129.2 1 5 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:133.40,134.23 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:134.23,136.17 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:136.17,138.4 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:139.3,145.17 5 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:145.17,147.4 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:150.3,151.31 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:151.31,153.4 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:154.3,156.31 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:156.31,157.47 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:157.47,160.19 3 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:160.19,162.6 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:163.10,166.19 3 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:166.19,168.6 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/scheduler.go:172.3,172.12 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:49.166,52.16 2 18 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:52.16,54.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:56.2,56.47 1 18 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:56.47,58.3 1 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:60.2,60.43 1 17 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:60.43,62.3 1 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:64.2,64.44 1 16 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:64.44,66.3 1 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:68.2,68.21 1 15 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:68.21,70.3 1 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:72.2,72.47 1 14 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:72.47,74.3 1 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:76.2,76.57 1 13 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:76.57,78.3 1 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:81.2,82.16 2 12 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:82.16,84.3 1 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:86.2,89.16 3 11 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:89.16,91.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:95.2,96.16 2 11 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:96.16,98.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:102.2,103.16 2 11 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:103.16,105.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:107.2,108.16 2 11 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:108.16,110.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:112.2,125.21 2 11 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:130.53,131.34 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:131.34,133.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:135.2,145.12 8 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:145.12,146.7 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:146.7,148.4 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:152.2,159.16 3 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:159.16,161.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:164.2,165.16 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:165.16,167.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:169.2,169.12 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:169.12,170.28 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:170.28,172.4 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:179.2,181.12 3 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:181.12,182.12 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:182.12,183.11 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:184.24,187.19 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:187.19,188.14 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:191.5,191.21 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:191.21,192.14 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:195.5,195.23 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:198.29,200.20 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:200.20,201.15 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:204.6,204.82 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:204.82,207.7 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:210.28,212.20 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:212.20,213.15 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:216.6,216.41 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:216.41,219.7 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:222.25,224.20 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:224.20,225.15 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:228.6,228.41 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:228.41,231.7 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:234.28,237.19 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:237.19,240.6 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:242.28,244.17 2 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:247.3,247.12 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:249.2,261.12 8 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:265.59,266.16 1 2 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:267.22,268.36 1 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:269.23,270.37 1 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:274.30,279.16 4 1 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:279.16,281.3 1 0 +github.com/smartcontractkit/timelock-worker/pkg/timelock/timelock.go:282.2,285.102 3 1 diff --git a/docs/BUILD.md b/docs/BUILD.md new file mode 100644 index 0000000..1794447 --- /dev/null +++ b/docs/BUILD.md @@ -0,0 +1,39 @@ +# timelock-worker builds + +- [timelock-worker builds](#timelock-worker-builds) + - [Build as a command line interface](#build-as-a-command-line-interface) + - [Build as a container image](#build-as-a-container-image) + +## Build as a command line interface + +- Run the `build` target in the `Makefile`. + +```bash +make build +``` + +- The executable will be placed in `bin/timelock-worker` + +## Build as a container image + +- An **example** `Dockerfile` is provided in `builds/Dockerfile`. + - Note that it is just **an example** on how to build a container with timelock-worker, and it's **not supported**. + +```bash +docker buildx build -f builds/Dockerfile -t timelock-worker:latest --load . +``` + +- The resulting image `timelock-worker:latest` will be saved into docker. Check it with: + +```bash +docker images +``` + +- The output will show something similar to the following: + +```bash +REPOSITORY TAG IMAGE ID CREATED SIZE +timelock-worker latest 6b59e125d23f 26 hours ago 367MB +``` + +[< back](README.md) diff --git a/docs/CONFIG.md b/docs/CONFIG.md new file mode 100644 index 0000000..a8ac2b0 --- /dev/null +++ b/docs/CONFIG.md @@ -0,0 +1,121 @@ +# Usage + +- [Usage](#usage) + - [Summary of configuration options](#summary-of-configuration-options) + - [Configuration file](#configuration-file) + - [Environment variables](#environment-variables) + - [Command flags](#command-flags) + - [Available flags](#available-flags) + +## Summary of configuration options + +The `timelock-worker` requires certain values to be provided in order to run properly. + +- **Node URL**: WS URL where the Timelock contract is deployed. A websocket connection is needed for the events listener. +- **Timelock address**: the target Timelock contract to watch. +- **Call proxy address**: address of the contract with EXECUTOR role in the Timelock contract. +- **Private Key**: this private key that can sign transactions in the Timelock contract. In order to sign transaction, this wallet has to have ADMIN or EXECUTOR role on the Timelock. +- **From Block**: 0 by default, it's optional. The worker can be run to watch for event starting at an specified block. +- **Poll Period**: Scheduler period, in seconds, to go over all the pending operations. +- **Log Level**: info by default, and doesn't need to be changed for normal usage. It can be info or debug. + +The configuration can be provided via configuration file `timelock.env`, `environment variables` or `flags`, or any combination of those. + +The **precedence order** is `command flags > environment variables > configuration file`. + +## Configuration file + +The file is named `timelock.env`. Create the file in the **same directory** where the `timelock-worker` binary is, with the following format: + +```text +# RPC WebSocket URL. +NODE_URL=wss://myrpchost/foo/bar + +# Timelock contract address to monitor. +TIMELOCK_ADDRESS=0x12345 + +# Contract address with Executor role in TIMELOCK_ADDRESS. +CALL_PROXY_ADDRESS=0x67890 + +# EOA private key. +PRIVATE_KEY=MyPrivateKey + +# Subscription starting block. +FROM_BLOCK=0 + +# Event polling period in seconds. +POLL_PERIOD=600 +``` + +## Environment variables + +It's possible also to set environment variables, which will take precedence over the configuration file, overriding the values. + +The list of available environment variables is: + +```text +NODE_URL +TIMELOCK_ADDRESS +CALL_PROXY_ADDRESS +PRIVATE_KEY +FROM_BLOCK +POLL_PERIOD +``` + +Example: + +- Using the timelock.env file above, and setting the following environment var, would result in using the timelock address 0x09876 instead of 0x12345. + +```bash +export TIMELOCK_ADDRESS=0x09876 +``` + +- When running in a container, the environment variables can be set with `-e ENVVAR=VALUE`: + +```bash +docker run -ti \ +-e LOGLEVEL=info \ +-e TIMELOCK_ADDRESS=TimelockAddress \ +-d timelock-worker:latest +``` + +## Command flags + +Lastly, the `timelock-worker` also can be provided configuration with flags, which take precedence over timelock.env and environment variables. + +- When running `timelock-worker`, specify the flags as usual. + +```bash +timelock-worker start -n wss://rpc-url -k PrivateKey -a 0x12345 +``` + +- When running `timelock-worker` as a container image, the flags can be specified as well in the following format. + +```bash + docker run -d -it timelock-worker:latest \ + start --log-level=info --private-key MyPrivKey +``` + +Note that, if specifying a flag in docker mode, start has to be also in the command execution: `start --log-level=info --private-key MyPrivKey` + +### Available flags + +```bash +Usage: + timelock-worker start [flags] + +Flags: + -f, --call-proxy-address string Address of the target CallProxyAddress contract (default "0x67890") + --from-block int Start watching from this block + -h, --help help for start + -n, --node-url string RPC Endpoint for the target blockchain (default "wss://myrpchost/foo/bar") + --poll-period int Poll period in seconds (default 600) + -k, --private-key string Private key used to execute transactions (default "MyPrivateKey") + -a, --timelock-address string Address of the target Timelock contract (default "0x12345") + +Global Flags: + -l, --log-level string set logging level (trace, debug, info, warn, error, fatal) (default "info") + -o, --output string set logging output (human, json) (default "human") +``` + +[< back](README.md) diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md new file mode 100644 index 0000000..6d5c867 --- /dev/null +++ b/docs/DEVELOPMENT.md @@ -0,0 +1,46 @@ +# Development documentation + +The daemon is composed by an **event listener**, a **scheduler**, and an **executor**. + +## **Event Listener** + +It's constantly watching for [events](timelock.abi.md), and based on the events received it performs different actions: + +- **CallScheduled**: the daemon store these inside the scheduler, to be executed later. +- **CallExecuted**: this event means that a CallSchedule event has been executed, hence the daemon has to remove this event from the scheduler. +- **Cancelled**: same as with CallExecuted, this event means the CallSchedule operation with that ID has been cancelled, hence the scheduler removes it. + +## **Scheduler** + +The scheduler maintains a store of **unexecuted CallSchedule** operations. + +Every certain amount of seconds, defined by the poll period, it checks the operations in the store and sends them to the executor. + +Also, whenever the timelock-worker goes down, either because of an error or manual action, it creates a file in `/tmp/timelock-worker.log`, containing all the pending CallScheduled transactions in the store, and the block number where those transactions reside. + +As an example of `timelock-worker.log`: + +```text +Process stopped at 2023-05-10 07:20:49.435871 +0000 UTC +Earliest CallSchedule pending ID: 14a3e53b7a9575d15dd7dec0cbc3bc71c0c58c099ae71c06f6d23c3ff23562f1 Block Number: 8935613 + Use this block number to ensure all pending operations are properly executed. + Set it as environment variable or in timelock.env with FROM_BLOCK=8935613, or as a flag with --from-block=8935613 +CallSchedule pending ID: 6444b766232d9bacba978f3a03eb7252860584e0d55b5ca2f29b105e2dceb3c7 Block Number: 8935880 +CallSchedule pending ID: d9a1243050dc90e5926d7e9d88244ee0bd54a5bdb67b6235e958de92e84304ad Block Number: 8936764 +CallSchedule pending ID: d91015abae4d4645a7f0269c5455897df8a605097723657bdf812a05d3ad20de Block Number: 8940349 +``` + +Based on this data, the server can be instructed to start listening for events in the earliest CallSchedule event pending. In this example, that would be 8935613. + +## **Executor** + +The executor is where all the logic regarding operations exist. + +It gets the pending operations sent by the scheduler, and checks the following: + +- The operation is in Ready status, otherwise the operation is scheduled to the next cycle. + - Note: The operation could remain pending either because the delay period hasn't passed yet, or the predecessor operation is still pending. + +Based on that data, the operation is executed or sent back to the scheduler, to be checked in the next cycle. + +[< back](README.md) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..01be378 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,6 @@ +# **timelock-worker** + +- [Build](BUILD.md) +- [Configuration](CONFIG.md) +- [Usage](USAGE.md) +- [Development documentation](DEVELOPMENT.md) diff --git a/docs/USAGE.md b/docs/USAGE.md new file mode 100644 index 0000000..3b1f9f1 --- /dev/null +++ b/docs/USAGE.md @@ -0,0 +1,73 @@ +# Usage + +## Recommended usage + +### As a command line interface + +- Run the `timelock-worker` with the combination of timelock.env, environment variables and/or flags wanted. +- There's no restriction on wether to use only one method or a combination of any number of them to run the server. + +Example usage with command line: + +```bash +# in this example, the administrator decides to not provide a timelock.env, but instead provide configuration via environment variables, except for poll-period, which will be provided via flag +$ export TIMELOCK_ADDRESS=0x776FC69c49AA3c067e92B4155394c3027D5B51b8 +$ export CALL_PROXY_ADDRESS=0x776FC69c49AA3c067e92B4155394c3027D5B51b8 +$ export PRIVATE_KEY=04491a4c29c64295b453e53831ab633686b26d8f36a8d0735b1f4bb867f8af78 +$ export NODE_URL=wss://sepolia.infura.io/ws/v3/266402bb492e4276973d3c1b380680fa +$ export FROM_BLOCK=3510520 + +# start the timelock-worker +$ bin/timelock-worker start --log-level debug --poll-period 1 +``` + +Remember that any combination of environment variables, flags and configuration in timelock.env can be provided. + +The **precedence order** is `command flags > environment variables > configuration file`. + +### As a container image + +- Note that, when exiting, `timelock-worker` will leave logs in `/tmp/timelock-worker`. And when working inside a container, that filesystem is inside the container. So, to preserve the log, docker can share a volume with `-v`. +- Also, if the administrator wants to provide its own `timelock.env` configuration file, the container will expect a file in `/app/timelock.env`, which can be provided with `mount`. + +Example using docker: + +- Provide your own timelock.env and the current directory, so the container can leave the log after exiting. + +```bash + docker run -d -it \ + # the file in the current-directory/timelock.env will be mounted inside the container in /app/timelock.env + --mount type=bind,source="$(pwd)"/timelock.env,target=/app/timelock.env,readonly \ + # when the process exits, the timelock-worker.log will be placed in the current working directory + -v "$(pwd)":/tmp \ + timelock-worker:latest +``` + +- Providing some environment variables **and** timelock.env + +```bash + docker run -d -it \ + -e LOGLEVEL=info \ + -e TIMELOCK_ADDRESS=TimelockAddress \ + # the file in the current-directory/timelock.env will be mounted inside the container in /app/timelock.env + --mount type=bind,source="$(pwd)"/timelock.env,target=/app/timelock.env,readonly \ + # when the process exits, the timelock-worker.log will be placed in the current working directory + -v "$(pwd)":/tmp \ + timelock-worker:latest +``` + +- Providing environment variables, timelock.env, and flags. + +```bash + docker run -d -it \ + -e LOGLEVEL=info \ + -e TIMELOCK_ADDRESS=TimelockAddress \ + # the file in the current-directory/timelock.env will be mounted inside the container in /app/timelock.env + --mount type=bind,source="$(pwd)"/timelock.env,target=/app/timelock.env,readonly \ + # when the process exits, the timelock-worker.log will be placed in the current working directory + -v "$(pwd)":/tmp \ + # log level debug takes precedence over log level info in env var. + timelock-worker:latest start -log-level=debug +``` + +[< back](README.md) diff --git a/docs/timelock.abi.md b/docs/timelock.abi.md new file mode 100644 index 0000000..ddbb907 --- /dev/null +++ b/docs/timelock.abi.md @@ -0,0 +1,62 @@ +# timelock abi + +- [timelock abi](#timelock-abi) + - [Events](#events) + - [Functions](#functions) + - [Role](#role) + - [EIP Support](#eip-support) + - [Operations methods](#operations-methods) + - [Operations Management](#operations-management) + - [MinDelay Management](#mindelay-management) + +## Events + +```text +event FunctionSelectorBlocked(bytes4 indexed selector) +event FunctionSelectorUnblocked(bytes4 indexed selector) +event MinDelayChange(uint256 oldDuration, uint256 newDuration) +event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data) +event CallScheduled(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt, uint256 delay) +event Cancelled(bytes32 indexed id) +event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +event BypasserCallExecuted(uint256 indexed index, address target, uint256 value, bytes data) +``` + +## Functions + +```text +function DEFAULT_ADMIN_ROLE() view returns(bytes32) +function getRoleMember(bytes32 role, uint256 index) view returns(address) +function renounceRole(bytes32 role, address account) returns() +function isOperationPending(bytes32 id) view returns(bool pending) +function getBlockedFunctionSelectorCount() view returns(uint256) +function getMinDelay() view returns(uint256 duration) +function getRoleAdmin(bytes32 role) view returns(bytes32) +function getRoleMemberCount(bytes32 role) view returns(uint256) +function grantRole(bytes32 role, address account) returns() +function hashOperationBatch((address,uint256,bytes)[] calls, bytes32 predecessor, bytes32 salt) pure returns(bytes32 hash) +function isOperationDone(bytes32 id) view returns(bool done) +function updateDelay(uint256 newDelay) returns() +function EXECUTOR_ROLE() view returns(bytes32) +function executeBatch((address,uint256,bytes)[] calls, bytes32 predecessor, bytes32 salt) payable returns() +function getBlockedFunctionSelectorAt(uint256 index) view returns(bytes4) +function scheduleBatch((address,uint256,bytes)[] calls, bytes32 predecessor, bytes32 salt, uint256 delay) returns() +function cancel(bytes32 id) returns() +function hasRole(bytes32 role, address account) view returns(bool) +function onERC721Received(address , address , uint256 , bytes ) returns(bytes4) +function PROPOSER_ROLE() view returns(bytes32) +function blockFunctionSelector(bytes4 selector) returns() +function bypasserExecuteBatch((address,uint256,bytes)[] calls) payable returns() +function onERC1155Received(address , address , uint256 , uint256 , bytes ) returns(bytes4) +function revokeRole(bytes32 role, address account) returns() +function supportsInterface(bytes4 interfaceId) view returns(bool) +function ADMIN_ROLE() view returns(bytes32) +function isOperation(bytes32 id) view returns(bool registered) +function isOperationReady(bytes32 id) view returns(bool ready) +function unblockFunctionSelector(bytes4 selector) returns() +function BYPASSER_ROLE() view returns(bytes32) +function CANCELLER_ROLE() view returns(bytes32) +function getTimestamp(bytes32 id) view returns(uint256 timestamp) +``` diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0e0c938 --- /dev/null +++ b/go.mod @@ -0,0 +1,55 @@ +module github.com/smartcontractkit/timelock-worker + +go 1.20 + +require ( + github.com/ethereum/go-ethereum v1.11.6 + github.com/rs/zerolog v1.29.1 + github.com/spf13/cobra v1.7.0 + github.com/spf13/viper v1.16.0 + github.com/stretchr/testify v1.8.4 +) + +require ( + github.com/VictoriaMetrics/fastcache v1.10.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // 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/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/gogo/protobuf v1.3.3 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/holiman/uint256 v1.2.2 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect + github.com/tklauser/go-sysconf v0.3.11 // indirect + github.com/tklauser/numcpus v0.6.0 // indirect + github.com/yusufpapurcu/wmi v1.2.2 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/time v0.3.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..84b5b05 --- /dev/null +++ b/go.sum @@ -0,0 +1,644 @@ +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.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= +github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY= +github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +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/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +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-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= +github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +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/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/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/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= +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.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ethereum/go-ethereum v1.11.6 h1:2VF8Mf7XiSUfmoNOy3D+ocfl9Qu8baQBrCNbo2CXQ8E= +github.com/ethereum/go-ethereum v1.11.6/go.mod h1:+a8pUj1tOyJ2RinsNQD4326YS+leSoKGiG/uVVb0x6Y= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +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.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= +github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +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.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +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/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +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.4.1/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.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/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.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +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/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +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/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/uint256 v1.2.2 h1:TXKcSGc2WaxPD2+bmzAsVthL4+pEN0YwXcL5qED83vk= +github.com/holiman/uint256 v1.2.2/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +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.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +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.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +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.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +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/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/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/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +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.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= +github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= +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/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +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/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +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.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= +github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= +github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= +github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +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.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +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-20190605123033-f99c8df09eb5/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-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/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.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +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-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +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-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/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-20191001151750-bb3f8db39f24/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-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/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-20220405052023-b1e9470b6e64/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.4/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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/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-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +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.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +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.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +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.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +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.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/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-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/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-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..33b67c9 --- /dev/null +++ b/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/smartcontractkit/timelock-worker/cmd" + +func main() { + cmd.Execute() +} diff --git a/pkg/cli/config.go b/pkg/cli/config.go new file mode 100644 index 0000000..730a04b --- /dev/null +++ b/pkg/cli/config.go @@ -0,0 +1,69 @@ +package cli + +import ( + "os" + "strconv" + + "github.com/spf13/viper" +) + +// Config holds the timelock.env configuration structure. +type Config struct { + NodeURL string `mapstructure:"NODE_URL"` + TimelockAddress string `mapstructure:"TIMELOCK_ADDRESS"` + CallProxyAddress string `mapstructure:"CALL_PROXY_ADDRESS"` + PrivateKey string `mapstructure:"PRIVATE_KEY"` + FromBlock int64 `mapstructure:"FROM_BLOCK"` + PollPeriod int64 `mapstructure:"POLL_PERIOD"` +} + +// NewTimelockCLI return a new Timelock instance configured. +func NewTimelockCLI() (*Config, error) { + viper.AddConfigPath(".") + viper.SetConfigName("timelock") + viper.SetConfigType("env") + + var c Config + + // Read timelock.env and unmarshal the config into w. + // timelock.env defines the default configuration + // provided to the cli. + // Do not return errors here, as the CLI can be completely functional + // only by providing env vars. + _ = viper.ReadInConfig() + _ = viper.Unmarshal(&c) + + // Environment variables have precedence over timelock.env + // Get them and apply it into worker whenever they exist. + if os.Getenv("NODE_URL") != "" { + c.NodeURL = os.Getenv("NODE_URL") + } + + if os.Getenv("TIMELOCK_ADDRESS") != "" { + c.TimelockAddress = os.Getenv("TIMELOCK_ADDRESS") + } + + if os.Getenv("CALL_PROXY_ADDRESS") != "" { + c.CallProxyAddress = os.Getenv("CALL_PROXY_ADDRESS") + } + + if os.Getenv("PRIVATE_KEY") != "" { + c.PrivateKey = os.Getenv("PRIVATE_KEY") + } + + if os.Getenv("FROM_BLOCK") != "" { + fb, err := strconv.Atoi(os.Getenv("FROM_BLOCK")) + if err == nil { + c.FromBlock = int64(fb) + } + } + + if os.Getenv("POLL_PERIOD") != "" { + pp, err := strconv.Atoi(os.Getenv("POLL_PERIOD")) + if err == nil { + c.PollPeriod = int64(pp) + } + } + + return &c, nil +} diff --git a/pkg/cli/config_test.go b/pkg/cli/config_test.go new file mode 100644 index 0000000..9dc56e9 --- /dev/null +++ b/pkg/cli/config_test.go @@ -0,0 +1,157 @@ +package cli_test + +import ( + "bufio" + "fmt" + "os" + "reflect" + "testing" + + "github.com/smartcontractkit/timelock-worker/pkg/cli" + "github.com/stretchr/testify/assert" +) + +func TestNewConfigRaw(t *testing.T) { + var newConfig = &cli.Config{ + NodeURL: "foo:test", + TimelockAddress: "0x12345", + PrivateKey: "0123456789", + FromBlock: 0, + } + + if assert.NotNil(t, newConfig) { + assert.Equal(t, "foo:test", newConfig.NodeURL) + assert.Equal(t, "0x12345", newConfig.TimelockAddress) + assert.Equal(t, "0123456789", newConfig.PrivateKey) + assert.Equal(t, int64(0), newConfig.FromBlock) + } +} + +func TestNewTimelockCLIFromEnvVar(t *testing.T) { + os.Unsetenv("NODE_URL") + os.Unsetenv("TIMELOCK_ADDRESS") + os.Unsetenv("CALL_PROXY_ADDRESS") + os.Unsetenv("PRIVATE_KEY") + os.Unsetenv("POLL_PERIOD") + os.Unsetenv("FROM_BLOCK") + + t.Setenv("NODE_URL", "wss://goerli/test") + t.Setenv("TIMELOCK_ADDRESS", "0x2135C499f82d091323E5098Ef8EEb851C17BDf4b") + t.Setenv("PRIVATE_KEY", "1234567890") + t.Setenv("FROM_BLOCK", "1234567890") + + var wantedConfig = cli.Config{ + NodeURL: "wss://goerli/test", + TimelockAddress: "0x2135C499f82d091323E5098Ef8EEb851C17BDf4b", + PrivateKey: "1234567890", + FromBlock: 1234567890, + } + + tests := []struct { + name string + want *cli.Config + wantErr bool + }{ + { + "Environment Vars - should succeed", + &wantedConfig, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := cli.NewTimelockCLI() + if (err != nil) != tt.wantErr { + t.Errorf("NewTimelockCLI() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewTimelockCLI() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNewTimelockCLIFromEnvVarAndFile(t *testing.T) { + os.Unsetenv("NODE_URL") + os.Unsetenv("TIMELOCK_ADDRESS") + os.Unsetenv("CALL_PROXY_ADDRESS") + os.Unsetenv("PRIVATE_KEY") + os.Unsetenv("POLL_PERIOD") + os.Unsetenv("FROM_BLOCK") + + f, err := os.Create("timelock.env") + if err != nil { + return + } + defer f.Close() + + w := bufio.NewWriter(f) + _, err = fmt.Fprintf(w, "NODE_URL=wss://sepolia.infura.io/ws/v3/266402bb492e4276973d3c1b380680fa\n") + if err != nil { + return + } + + _, err = fmt.Fprintf(w, "TIMELOCK_ADDRESS=0x2135C499f82d091323E5098Ef8EEb851C17BDf4b\n") + if err != nil { + return + } + + _, err = fmt.Fprintf(w, "CALL_PROXY_ADDRESS=0x2135C499f82d091323E5098Ef8EEb851C17BDf4b\n") + if err != nil { + return + } + + _, err = fmt.Fprintf(w, "PRIVATE_KEY=9876543210\n") + if err != nil { + return + } + _, err = fmt.Fprintf(w, "FROM_BLOCK=0\n") + if err != nil { + return + } + + w.Flush() + + // envvars should prevail over timelock.env + t.Setenv("PRIVATE_KEY", "27eb33663c16b490a2a53036ce1fc7b346afebad3224bf3e59dc1dcef56b9600") + t.Setenv("POLL_PERIOD", "10") + t.Setenv("FROM_BLOCK", "1234567890") + + var config = cli.Config{ + NodeURL: "wss://sepolia.infura.io/ws/v3/266402bb492e4276973d3c1b380680fa", + TimelockAddress: "0x2135C499f82d091323E5098Ef8EEb851C17BDf4b", + CallProxyAddress: "0x2135C499f82d091323E5098Ef8EEb851C17BDf4b", + PrivateKey: "27eb33663c16b490a2a53036ce1fc7b346afebad3224bf3e59dc1dcef56b9600", + FromBlock: 1234567890, + PollPeriod: 10, + } + + tests := []struct { + name string + want *cli.Config + wantErr bool + }{ + { + "timelock.env and env vars - should succeed", + &config, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := cli.NewTimelockCLI() + if (err != nil) != tt.wantErr { + t.Errorf("NewTimelockCLI() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewTimelockCLI() = %+vv, want %+v", got, tt.want) + } + }) + } + + if err := os.Remove("timelock.env"); err != nil { + return + } +} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 0000000..3190a6f --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,53 @@ +package logger + +import ( + "os" + "sync" + "time" + + "github.com/rs/zerolog" +) + +var ( + once sync.Once //nolint: gochecknoglobals + logger *zerolog.Logger //nolint: gochecknoglobals +) + +// humanConsoleWriter configures the human-readable output. +var humanConsoleWriter = zerolog.ConsoleWriter{ //nolint: gochecknoglobals + Out: os.Stdout, + TimeFormat: time.RFC3339, +} + +// Logger returns an instance of Logger. +func Logger(logLevel string, output string) *zerolog.Logger { + if logger == nil { + once.Do( + func() { + logger = newLogger(logLevel, output) + }) + } + + return logger +} + +// newLogger initializes the Logger with the given arguments. +func newLogger(logLevel string, output string) *zerolog.Logger { + level, err := zerolog.ParseLevel(logLevel) + if err != nil { + // Do not crash on wrong user input, default to InfoLevel. + level = zerolog.InfoLevel + } + + var l zerolog.Logger + switch output { + case "human": + l = zerolog.New(humanConsoleWriter).Level(level).With().Timestamp().Logger() + case "json": + l = zerolog.New(os.Stdout).Level(level).With().Timestamp().Logger() + default: + l = zerolog.New(humanConsoleWriter).Level(level).With().Timestamp().Logger() + } + + return &l +} diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go new file mode 100644 index 0000000..472efa3 --- /dev/null +++ b/pkg/logger/logger_test.go @@ -0,0 +1,58 @@ +package logger_test + +import ( + "reflect" + "testing" + + "github.com/rs/zerolog" + "github.com/smartcontractkit/timelock-worker/pkg/logger" + "github.com/stretchr/testify/assert" +) + +func TestLogger(t *testing.T) { + testLogger := logger.Logger("info", "human") + assert.NotNil(t, testLogger) + + testType := reflect.TypeOf(testLogger) + loggerType := reflect.TypeOf((*zerolog.Logger)(nil)) + + if !(loggerType == testType) { + t.Errorf("testType does not implement loggerType") + } +} + +func TestLoggerJSON(t *testing.T) { + testLogger := logger.Logger("debug", "json") + assert.NotNil(t, testLogger) + + testType := reflect.TypeOf(testLogger) + loggerType := reflect.TypeOf((*zerolog.Logger)(nil)) + + if !(loggerType == testType) { + t.Errorf("testType does not implement loggerType") + } +} + +func TestLoggerWrongLogLevel(t *testing.T) { + testLogger := logger.Logger("wrongLogLevel", "human") + assert.NotNil(t, testLogger) + + testType := reflect.TypeOf(testLogger) + loggerType := reflect.TypeOf((*zerolog.Logger)(nil)) + + if !(loggerType == testType) { + t.Errorf("testType does not implement loggerType") + } +} + +func TestLoggerWrongAll(t *testing.T) { + testLogger := logger.Logger("wrongLogLevel", "wrongOutput") + assert.NotNil(t, testLogger) + + testType := reflect.TypeOf(testLogger) + loggerType := reflect.TypeOf((*zerolog.Logger)(nil)) + + if !(loggerType == testType) { + t.Errorf("testType does not implement loggerType") + } +} diff --git a/pkg/timelock/const.go b/pkg/timelock/const.go new file mode 100644 index 0000000..9038280 --- /dev/null +++ b/pkg/timelock/const.go @@ -0,0 +1,19 @@ +package timelock + +import "time" + +const ( + defaultSchedulerDelay time.Duration = 15 * time.Minute + + eventCallScheduled string = "CallScheduled" + eventCallExecuted string = "CallExecuted" + eventCancelled string = "Cancelled" + eventMinDelayChange string = "MinDelayChange" + + fieldTXHash string = "TX Hash" + fieldBlockNumber string = "Block Number" + + logPath string = "/tmp/" + logFile string = "timelock-worker.log" + logOperationKey string = "Operation ID" +) diff --git a/pkg/timelock/const_test.go b/pkg/timelock/const_test.go new file mode 100644 index 0000000..21600ab --- /dev/null +++ b/pkg/timelock/const_test.go @@ -0,0 +1,18 @@ +package timelock + +import ( + "math/big" + "os" + + "github.com/smartcontractkit/timelock-worker/pkg/logger" +) + +var ( + testNodeURL = os.Getenv("TEST_NODE_URL") + testTimelockAddress = os.Getenv("TEST_TIMELOCK_ADDRESS") + testCallProxyAddress = os.Getenv("TEST_PROXY_ADDRESS") + testPrivateKey = os.Getenv("TEST_PRIVATE_KEY") + testFromBlock = big.NewInt(0) + testPollPeriod = 5 + testLogger = logger.Logger("info", "human") +) diff --git a/pkg/timelock/contract/Timelock.go b/pkg/timelock/contract/Timelock.go new file mode 100644 index 0000000..ea8a062 --- /dev/null +++ b/pkg/timelock/contract/Timelock.go @@ -0,0 +1,2617 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contract + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// RBACTimelockCall is an auto generated low-level Go binding around an user-defined struct. +type RBACTimelockCall struct { + Target common.Address + Value *big.Int + Data []byte +} + +// TimelockMetaData contains all meta data concerning the Timelock contract. +var TimelockMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"minDelay\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"proposers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"executors\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"cancellers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"bypassers\",\"type\":\"address[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"BypasserCallExecuted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"CallExecuted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"predecessor\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"delay\",\"type\":\"uint256\"}],\"name\":\"CallScheduled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"Cancelled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"selector\",\"type\":\"bytes4\"}],\"name\":\"FunctionSelectorBlocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes4\",\"name\":\"selector\",\"type\":\"bytes4\"}],\"name\":\"FunctionSelectorUnblocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldDuration\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newDuration\",\"type\":\"uint256\"}],\"name\":\"MinDelayChange\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"BYPASSER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"CANCELLER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"EXECUTOR_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PROPOSER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"selector\",\"type\":\"bytes4\"}],\"name\":\"blockFunctionSelector\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structRBACTimelock.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"bypasserExecuteBatch\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"cancel\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structRBACTimelock.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes32\",\"name\":\"predecessor\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"}],\"name\":\"executeBatch\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getBlockedFunctionSelectorAt\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBlockedFunctionSelectorCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMinDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"duration\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getRoleMember\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleMemberCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"getTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structRBACTimelock.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes32\",\"name\":\"predecessor\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"}],\"name\":\"hashOperationBatch\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"isOperation\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"registered\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"isOperationDone\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"done\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"isOperationPending\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"pending\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"id\",\"type\":\"bytes32\"}],\"name\":\"isOperationReady\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"ready\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"onERC1155BatchReceived\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"onERC1155Received\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"onERC721Received\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structRBACTimelock.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes32\",\"name\":\"predecessor\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"salt\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"delay\",\"type\":\"uint256\"}],\"name\":\"scheduleBatch\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"selector\",\"type\":\"bytes4\"}],\"name\":\"unblockFunctionSelector\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newDelay\",\"type\":\"uint256\"}],\"name\":\"updateDelay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", +} + +// TimelockABI is the input ABI used to generate the binding from. +// Deprecated: Use TimelockMetaData.ABI instead. +var TimelockABI = TimelockMetaData.ABI + +// Timelock is an auto generated Go binding around an Ethereum contract. +type Timelock struct { + TimelockCaller // Read-only binding to the contract + TimelockTransactor // Write-only binding to the contract + TimelockFilterer // Log filterer for contract events +} + +// TimelockCaller is an auto generated read-only Go binding around an Ethereum contract. +type TimelockCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TimelockTransactor is an auto generated write-only Go binding around an Ethereum contract. +type TimelockTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TimelockFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type TimelockFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TimelockSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type TimelockSession struct { + Contract *Timelock // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TimelockCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type TimelockCallerSession struct { + Contract *TimelockCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// TimelockTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type TimelockTransactorSession struct { + Contract *TimelockTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TimelockRaw is an auto generated low-level Go binding around an Ethereum contract. +type TimelockRaw struct { + Contract *Timelock // Generic contract binding to access the raw methods on +} + +// TimelockCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type TimelockCallerRaw struct { + Contract *TimelockCaller // Generic read-only contract binding to access the raw methods on +} + +// TimelockTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type TimelockTransactorRaw struct { + Contract *TimelockTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewTimelock creates a new instance of Timelock, bound to a specific deployed contract. +func NewTimelock(address common.Address, backend bind.ContractBackend) (*Timelock, error) { + contract, err := bindTimelock(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Timelock{TimelockCaller: TimelockCaller{contract: contract}, TimelockTransactor: TimelockTransactor{contract: contract}, TimelockFilterer: TimelockFilterer{contract: contract}}, nil +} + +// NewTimelockCaller creates a new read-only instance of Timelock, bound to a specific deployed contract. +func NewTimelockCaller(address common.Address, caller bind.ContractCaller) (*TimelockCaller, error) { + contract, err := bindTimelock(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &TimelockCaller{contract: contract}, nil +} + +// NewTimelockTransactor creates a new write-only instance of Timelock, bound to a specific deployed contract. +func NewTimelockTransactor(address common.Address, transactor bind.ContractTransactor) (*TimelockTransactor, error) { + contract, err := bindTimelock(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &TimelockTransactor{contract: contract}, nil +} + +// NewTimelockFilterer creates a new log filterer instance of Timelock, bound to a specific deployed contract. +func NewTimelockFilterer(address common.Address, filterer bind.ContractFilterer) (*TimelockFilterer, error) { + contract, err := bindTimelock(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &TimelockFilterer{contract: contract}, nil +} + +// bindTimelock binds a generic wrapper to an already deployed contract. +func bindTimelock(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := TimelockMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Timelock *TimelockRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Timelock.Contract.TimelockCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Timelock *TimelockRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Timelock.Contract.TimelockTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Timelock *TimelockRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Timelock.Contract.TimelockTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Timelock *TimelockCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Timelock.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Timelock *TimelockTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Timelock.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Timelock *TimelockTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Timelock.Contract.contract.Transact(opts, method, params...) +} + +// ADMINROLE is a free data retrieval call binding the contract method 0x75b238fc. +// +// Solidity: function ADMIN_ROLE() view returns(bytes32) +func (_Timelock *TimelockCaller) ADMINROLE(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "ADMIN_ROLE") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// ADMINROLE is a free data retrieval call binding the contract method 0x75b238fc. +// +// Solidity: function ADMIN_ROLE() view returns(bytes32) +func (_Timelock *TimelockSession) ADMINROLE() ([32]byte, error) { + return _Timelock.Contract.ADMINROLE(&_Timelock.CallOpts) +} + +// ADMINROLE is a free data retrieval call binding the contract method 0x75b238fc. +// +// Solidity: function ADMIN_ROLE() view returns(bytes32) +func (_Timelock *TimelockCallerSession) ADMINROLE() ([32]byte, error) { + return _Timelock.Contract.ADMINROLE(&_Timelock.CallOpts) +} + +// BYPASSERROLE is a free data retrieval call binding the contract method 0x191cb7b3. +// +// Solidity: function BYPASSER_ROLE() view returns(bytes32) +func (_Timelock *TimelockCaller) BYPASSERROLE(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "BYPASSER_ROLE") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// BYPASSERROLE is a free data retrieval call binding the contract method 0x191cb7b3. +// +// Solidity: function BYPASSER_ROLE() view returns(bytes32) +func (_Timelock *TimelockSession) BYPASSERROLE() ([32]byte, error) { + return _Timelock.Contract.BYPASSERROLE(&_Timelock.CallOpts) +} + +// BYPASSERROLE is a free data retrieval call binding the contract method 0x191cb7b3. +// +// Solidity: function BYPASSER_ROLE() view returns(bytes32) +func (_Timelock *TimelockCallerSession) BYPASSERROLE() ([32]byte, error) { + return _Timelock.Contract.BYPASSERROLE(&_Timelock.CallOpts) +} + +// CANCELLERROLE is a free data retrieval call binding the contract method 0xb08e51c0. +// +// Solidity: function CANCELLER_ROLE() view returns(bytes32) +func (_Timelock *TimelockCaller) CANCELLERROLE(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "CANCELLER_ROLE") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// CANCELLERROLE is a free data retrieval call binding the contract method 0xb08e51c0. +// +// Solidity: function CANCELLER_ROLE() view returns(bytes32) +func (_Timelock *TimelockSession) CANCELLERROLE() ([32]byte, error) { + return _Timelock.Contract.CANCELLERROLE(&_Timelock.CallOpts) +} + +// CANCELLERROLE is a free data retrieval call binding the contract method 0xb08e51c0. +// +// Solidity: function CANCELLER_ROLE() view returns(bytes32) +func (_Timelock *TimelockCallerSession) CANCELLERROLE() ([32]byte, error) { + return _Timelock.Contract.CANCELLERROLE(&_Timelock.CallOpts) +} + +// DEFAULTADMINROLE is a free data retrieval call binding the contract method 0xa217fddf. +// +// Solidity: function DEFAULT_ADMIN_ROLE() view returns(bytes32) +func (_Timelock *TimelockCaller) DEFAULTADMINROLE(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "DEFAULT_ADMIN_ROLE") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// DEFAULTADMINROLE is a free data retrieval call binding the contract method 0xa217fddf. +// +// Solidity: function DEFAULT_ADMIN_ROLE() view returns(bytes32) +func (_Timelock *TimelockSession) DEFAULTADMINROLE() ([32]byte, error) { + return _Timelock.Contract.DEFAULTADMINROLE(&_Timelock.CallOpts) +} + +// DEFAULTADMINROLE is a free data retrieval call binding the contract method 0xa217fddf. +// +// Solidity: function DEFAULT_ADMIN_ROLE() view returns(bytes32) +func (_Timelock *TimelockCallerSession) DEFAULTADMINROLE() ([32]byte, error) { + return _Timelock.Contract.DEFAULTADMINROLE(&_Timelock.CallOpts) +} + +// EXECUTORROLE is a free data retrieval call binding the contract method 0x07bd0265. +// +// Solidity: function EXECUTOR_ROLE() view returns(bytes32) +func (_Timelock *TimelockCaller) EXECUTORROLE(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "EXECUTOR_ROLE") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// EXECUTORROLE is a free data retrieval call binding the contract method 0x07bd0265. +// +// Solidity: function EXECUTOR_ROLE() view returns(bytes32) +func (_Timelock *TimelockSession) EXECUTORROLE() ([32]byte, error) { + return _Timelock.Contract.EXECUTORROLE(&_Timelock.CallOpts) +} + +// EXECUTORROLE is a free data retrieval call binding the contract method 0x07bd0265. +// +// Solidity: function EXECUTOR_ROLE() view returns(bytes32) +func (_Timelock *TimelockCallerSession) EXECUTORROLE() ([32]byte, error) { + return _Timelock.Contract.EXECUTORROLE(&_Timelock.CallOpts) +} + +// PROPOSERROLE is a free data retrieval call binding the contract method 0x8f61f4f5. +// +// Solidity: function PROPOSER_ROLE() view returns(bytes32) +func (_Timelock *TimelockCaller) PROPOSERROLE(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "PROPOSER_ROLE") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// PROPOSERROLE is a free data retrieval call binding the contract method 0x8f61f4f5. +// +// Solidity: function PROPOSER_ROLE() view returns(bytes32) +func (_Timelock *TimelockSession) PROPOSERROLE() ([32]byte, error) { + return _Timelock.Contract.PROPOSERROLE(&_Timelock.CallOpts) +} + +// PROPOSERROLE is a free data retrieval call binding the contract method 0x8f61f4f5. +// +// Solidity: function PROPOSER_ROLE() view returns(bytes32) +func (_Timelock *TimelockCallerSession) PROPOSERROLE() ([32]byte, error) { + return _Timelock.Contract.PROPOSERROLE(&_Timelock.CallOpts) +} + +// GetBlockedFunctionSelectorAt is a free data retrieval call binding the contract method 0x03e56155. +// +// Solidity: function getBlockedFunctionSelectorAt(uint256 index) view returns(bytes4) +func (_Timelock *TimelockCaller) GetBlockedFunctionSelectorAt(opts *bind.CallOpts, index *big.Int) ([4]byte, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "getBlockedFunctionSelectorAt", index) + + if err != nil { + return *new([4]byte), err + } + + out0 := *abi.ConvertType(out[0], new([4]byte)).(*[4]byte) + + return out0, err + +} + +// GetBlockedFunctionSelectorAt is a free data retrieval call binding the contract method 0x03e56155. +// +// Solidity: function getBlockedFunctionSelectorAt(uint256 index) view returns(bytes4) +func (_Timelock *TimelockSession) GetBlockedFunctionSelectorAt(index *big.Int) ([4]byte, error) { + return _Timelock.Contract.GetBlockedFunctionSelectorAt(&_Timelock.CallOpts, index) +} + +// GetBlockedFunctionSelectorAt is a free data retrieval call binding the contract method 0x03e56155. +// +// Solidity: function getBlockedFunctionSelectorAt(uint256 index) view returns(bytes4) +func (_Timelock *TimelockCallerSession) GetBlockedFunctionSelectorAt(index *big.Int) ([4]byte, error) { + return _Timelock.Contract.GetBlockedFunctionSelectorAt(&_Timelock.CallOpts, index) +} + +// GetBlockedFunctionSelectorCount is a free data retrieval call binding the contract method 0x26bb2ec5. +// +// Solidity: function getBlockedFunctionSelectorCount() view returns(uint256) +func (_Timelock *TimelockCaller) GetBlockedFunctionSelectorCount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "getBlockedFunctionSelectorCount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetBlockedFunctionSelectorCount is a free data retrieval call binding the contract method 0x26bb2ec5. +// +// Solidity: function getBlockedFunctionSelectorCount() view returns(uint256) +func (_Timelock *TimelockSession) GetBlockedFunctionSelectorCount() (*big.Int, error) { + return _Timelock.Contract.GetBlockedFunctionSelectorCount(&_Timelock.CallOpts) +} + +// GetBlockedFunctionSelectorCount is a free data retrieval call binding the contract method 0x26bb2ec5. +// +// Solidity: function getBlockedFunctionSelectorCount() view returns(uint256) +func (_Timelock *TimelockCallerSession) GetBlockedFunctionSelectorCount() (*big.Int, error) { + return _Timelock.Contract.GetBlockedFunctionSelectorCount(&_Timelock.CallOpts) +} + +// GetMinDelay is a free data retrieval call binding the contract method 0xf27a0c92. +// +// Solidity: function getMinDelay() view returns(uint256 duration) +func (_Timelock *TimelockCaller) GetMinDelay(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "getMinDelay") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetMinDelay is a free data retrieval call binding the contract method 0xf27a0c92. +// +// Solidity: function getMinDelay() view returns(uint256 duration) +func (_Timelock *TimelockSession) GetMinDelay() (*big.Int, error) { + return _Timelock.Contract.GetMinDelay(&_Timelock.CallOpts) +} + +// GetMinDelay is a free data retrieval call binding the contract method 0xf27a0c92. +// +// Solidity: function getMinDelay() view returns(uint256 duration) +func (_Timelock *TimelockCallerSession) GetMinDelay() (*big.Int, error) { + return _Timelock.Contract.GetMinDelay(&_Timelock.CallOpts) +} + +// GetRoleAdmin is a free data retrieval call binding the contract method 0x248a9ca3. +// +// Solidity: function getRoleAdmin(bytes32 role) view returns(bytes32) +func (_Timelock *TimelockCaller) GetRoleAdmin(opts *bind.CallOpts, role [32]byte) ([32]byte, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "getRoleAdmin", role) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// GetRoleAdmin is a free data retrieval call binding the contract method 0x248a9ca3. +// +// Solidity: function getRoleAdmin(bytes32 role) view returns(bytes32) +func (_Timelock *TimelockSession) GetRoleAdmin(role [32]byte) ([32]byte, error) { + return _Timelock.Contract.GetRoleAdmin(&_Timelock.CallOpts, role) +} + +// GetRoleAdmin is a free data retrieval call binding the contract method 0x248a9ca3. +// +// Solidity: function getRoleAdmin(bytes32 role) view returns(bytes32) +func (_Timelock *TimelockCallerSession) GetRoleAdmin(role [32]byte) ([32]byte, error) { + return _Timelock.Contract.GetRoleAdmin(&_Timelock.CallOpts, role) +} + +// GetRoleMember is a free data retrieval call binding the contract method 0x9010d07c. +// +// Solidity: function getRoleMember(bytes32 role, uint256 index) view returns(address) +func (_Timelock *TimelockCaller) GetRoleMember(opts *bind.CallOpts, role [32]byte, index *big.Int) (common.Address, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "getRoleMember", role, index) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GetRoleMember is a free data retrieval call binding the contract method 0x9010d07c. +// +// Solidity: function getRoleMember(bytes32 role, uint256 index) view returns(address) +func (_Timelock *TimelockSession) GetRoleMember(role [32]byte, index *big.Int) (common.Address, error) { + return _Timelock.Contract.GetRoleMember(&_Timelock.CallOpts, role, index) +} + +// GetRoleMember is a free data retrieval call binding the contract method 0x9010d07c. +// +// Solidity: function getRoleMember(bytes32 role, uint256 index) view returns(address) +func (_Timelock *TimelockCallerSession) GetRoleMember(role [32]byte, index *big.Int) (common.Address, error) { + return _Timelock.Contract.GetRoleMember(&_Timelock.CallOpts, role, index) +} + +// GetRoleMemberCount is a free data retrieval call binding the contract method 0xca15c873. +// +// Solidity: function getRoleMemberCount(bytes32 role) view returns(uint256) +func (_Timelock *TimelockCaller) GetRoleMemberCount(opts *bind.CallOpts, role [32]byte) (*big.Int, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "getRoleMemberCount", role) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetRoleMemberCount is a free data retrieval call binding the contract method 0xca15c873. +// +// Solidity: function getRoleMemberCount(bytes32 role) view returns(uint256) +func (_Timelock *TimelockSession) GetRoleMemberCount(role [32]byte) (*big.Int, error) { + return _Timelock.Contract.GetRoleMemberCount(&_Timelock.CallOpts, role) +} + +// GetRoleMemberCount is a free data retrieval call binding the contract method 0xca15c873. +// +// Solidity: function getRoleMemberCount(bytes32 role) view returns(uint256) +func (_Timelock *TimelockCallerSession) GetRoleMemberCount(role [32]byte) (*big.Int, error) { + return _Timelock.Contract.GetRoleMemberCount(&_Timelock.CallOpts, role) +} + +// GetTimestamp is a free data retrieval call binding the contract method 0xd45c4435. +// +// Solidity: function getTimestamp(bytes32 id) view returns(uint256 timestamp) +func (_Timelock *TimelockCaller) GetTimestamp(opts *bind.CallOpts, id [32]byte) (*big.Int, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "getTimestamp", id) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetTimestamp is a free data retrieval call binding the contract method 0xd45c4435. +// +// Solidity: function getTimestamp(bytes32 id) view returns(uint256 timestamp) +func (_Timelock *TimelockSession) GetTimestamp(id [32]byte) (*big.Int, error) { + return _Timelock.Contract.GetTimestamp(&_Timelock.CallOpts, id) +} + +// GetTimestamp is a free data retrieval call binding the contract method 0xd45c4435. +// +// Solidity: function getTimestamp(bytes32 id) view returns(uint256 timestamp) +func (_Timelock *TimelockCallerSession) GetTimestamp(id [32]byte) (*big.Int, error) { + return _Timelock.Contract.GetTimestamp(&_Timelock.CallOpts, id) +} + +// HasRole is a free data retrieval call binding the contract method 0x91d14854. +// +// Solidity: function hasRole(bytes32 role, address account) view returns(bool) +func (_Timelock *TimelockCaller) HasRole(opts *bind.CallOpts, role [32]byte, account common.Address) (bool, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "hasRole", role, account) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// HasRole is a free data retrieval call binding the contract method 0x91d14854. +// +// Solidity: function hasRole(bytes32 role, address account) view returns(bool) +func (_Timelock *TimelockSession) HasRole(role [32]byte, account common.Address) (bool, error) { + return _Timelock.Contract.HasRole(&_Timelock.CallOpts, role, account) +} + +// HasRole is a free data retrieval call binding the contract method 0x91d14854. +// +// Solidity: function hasRole(bytes32 role, address account) view returns(bool) +func (_Timelock *TimelockCallerSession) HasRole(role [32]byte, account common.Address) (bool, error) { + return _Timelock.Contract.HasRole(&_Timelock.CallOpts, role, account) +} + +// HashOperationBatch is a free data retrieval call binding the contract method 0x515a3db3. +// +// Solidity: function hashOperationBatch((address,uint256,bytes)[] calls, bytes32 predecessor, bytes32 salt) pure returns(bytes32 hash) +func (_Timelock *TimelockCaller) HashOperationBatch(opts *bind.CallOpts, calls []RBACTimelockCall, predecessor [32]byte, salt [32]byte) ([32]byte, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "hashOperationBatch", calls, predecessor, salt) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// HashOperationBatch is a free data retrieval call binding the contract method 0x515a3db3. +// +// Solidity: function hashOperationBatch((address,uint256,bytes)[] calls, bytes32 predecessor, bytes32 salt) pure returns(bytes32 hash) +func (_Timelock *TimelockSession) HashOperationBatch(calls []RBACTimelockCall, predecessor [32]byte, salt [32]byte) ([32]byte, error) { + return _Timelock.Contract.HashOperationBatch(&_Timelock.CallOpts, calls, predecessor, salt) +} + +// HashOperationBatch is a free data retrieval call binding the contract method 0x515a3db3. +// +// Solidity: function hashOperationBatch((address,uint256,bytes)[] calls, bytes32 predecessor, bytes32 salt) pure returns(bytes32 hash) +func (_Timelock *TimelockCallerSession) HashOperationBatch(calls []RBACTimelockCall, predecessor [32]byte, salt [32]byte) ([32]byte, error) { + return _Timelock.Contract.HashOperationBatch(&_Timelock.CallOpts, calls, predecessor, salt) +} + +// IsOperation is a free data retrieval call binding the contract method 0x31d50750. +// +// Solidity: function isOperation(bytes32 id) view returns(bool registered) +func (_Timelock *TimelockCaller) IsOperation(opts *bind.CallOpts, id [32]byte) (bool, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "isOperation", id) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsOperation is a free data retrieval call binding the contract method 0x31d50750. +// +// Solidity: function isOperation(bytes32 id) view returns(bool registered) +func (_Timelock *TimelockSession) IsOperation(id [32]byte) (bool, error) { + return _Timelock.Contract.IsOperation(&_Timelock.CallOpts, id) +} + +// IsOperation is a free data retrieval call binding the contract method 0x31d50750. +// +// Solidity: function isOperation(bytes32 id) view returns(bool registered) +func (_Timelock *TimelockCallerSession) IsOperation(id [32]byte) (bool, error) { + return _Timelock.Contract.IsOperation(&_Timelock.CallOpts, id) +} + +// IsOperationDone is a free data retrieval call binding the contract method 0x2ab0f529. +// +// Solidity: function isOperationDone(bytes32 id) view returns(bool done) +func (_Timelock *TimelockCaller) IsOperationDone(opts *bind.CallOpts, id [32]byte) (bool, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "isOperationDone", id) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsOperationDone is a free data retrieval call binding the contract method 0x2ab0f529. +// +// Solidity: function isOperationDone(bytes32 id) view returns(bool done) +func (_Timelock *TimelockSession) IsOperationDone(id [32]byte) (bool, error) { + return _Timelock.Contract.IsOperationDone(&_Timelock.CallOpts, id) +} + +// IsOperationDone is a free data retrieval call binding the contract method 0x2ab0f529. +// +// Solidity: function isOperationDone(bytes32 id) view returns(bool done) +func (_Timelock *TimelockCallerSession) IsOperationDone(id [32]byte) (bool, error) { + return _Timelock.Contract.IsOperationDone(&_Timelock.CallOpts, id) +} + +// IsOperationPending is a free data retrieval call binding the contract method 0x584b153e. +// +// Solidity: function isOperationPending(bytes32 id) view returns(bool pending) +func (_Timelock *TimelockCaller) IsOperationPending(opts *bind.CallOpts, id [32]byte) (bool, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "isOperationPending", id) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsOperationPending is a free data retrieval call binding the contract method 0x584b153e. +// +// Solidity: function isOperationPending(bytes32 id) view returns(bool pending) +func (_Timelock *TimelockSession) IsOperationPending(id [32]byte) (bool, error) { + return _Timelock.Contract.IsOperationPending(&_Timelock.CallOpts, id) +} + +// IsOperationPending is a free data retrieval call binding the contract method 0x584b153e. +// +// Solidity: function isOperationPending(bytes32 id) view returns(bool pending) +func (_Timelock *TimelockCallerSession) IsOperationPending(id [32]byte) (bool, error) { + return _Timelock.Contract.IsOperationPending(&_Timelock.CallOpts, id) +} + +// IsOperationReady is a free data retrieval call binding the contract method 0x13bc9f20. +// +// Solidity: function isOperationReady(bytes32 id) view returns(bool ready) +func (_Timelock *TimelockCaller) IsOperationReady(opts *bind.CallOpts, id [32]byte) (bool, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "isOperationReady", id) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsOperationReady is a free data retrieval call binding the contract method 0x13bc9f20. +// +// Solidity: function isOperationReady(bytes32 id) view returns(bool ready) +func (_Timelock *TimelockSession) IsOperationReady(id [32]byte) (bool, error) { + return _Timelock.Contract.IsOperationReady(&_Timelock.CallOpts, id) +} + +// IsOperationReady is a free data retrieval call binding the contract method 0x13bc9f20. +// +// Solidity: function isOperationReady(bytes32 id) view returns(bool ready) +func (_Timelock *TimelockCallerSession) IsOperationReady(id [32]byte) (bool, error) { + return _Timelock.Contract.IsOperationReady(&_Timelock.CallOpts, id) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_Timelock *TimelockCaller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) { + var out []interface{} + err := _Timelock.contract.Call(opts, &out, "supportsInterface", interfaceId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_Timelock *TimelockSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _Timelock.Contract.SupportsInterface(&_Timelock.CallOpts, interfaceId) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_Timelock *TimelockCallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _Timelock.Contract.SupportsInterface(&_Timelock.CallOpts, interfaceId) +} + +// BlockFunctionSelector is a paid mutator transaction binding the contract method 0x9f5a23f7. +// +// Solidity: function blockFunctionSelector(bytes4 selector) returns() +func (_Timelock *TimelockTransactor) BlockFunctionSelector(opts *bind.TransactOpts, selector [4]byte) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "blockFunctionSelector", selector) +} + +// BlockFunctionSelector is a paid mutator transaction binding the contract method 0x9f5a23f7. +// +// Solidity: function blockFunctionSelector(bytes4 selector) returns() +func (_Timelock *TimelockSession) BlockFunctionSelector(selector [4]byte) (*types.Transaction, error) { + return _Timelock.Contract.BlockFunctionSelector(&_Timelock.TransactOpts, selector) +} + +// BlockFunctionSelector is a paid mutator transaction binding the contract method 0x9f5a23f7. +// +// Solidity: function blockFunctionSelector(bytes4 selector) returns() +func (_Timelock *TimelockTransactorSession) BlockFunctionSelector(selector [4]byte) (*types.Transaction, error) { + return _Timelock.Contract.BlockFunctionSelector(&_Timelock.TransactOpts, selector) +} + +// BypasserExecuteBatch is a paid mutator transaction binding the contract method 0x0db866b1. +// +// Solidity: function bypasserExecuteBatch((address,uint256,bytes)[] calls) payable returns() +func (_Timelock *TimelockTransactor) BypasserExecuteBatch(opts *bind.TransactOpts, calls []RBACTimelockCall) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "bypasserExecuteBatch", calls) +} + +// BypasserExecuteBatch is a paid mutator transaction binding the contract method 0x0db866b1. +// +// Solidity: function bypasserExecuteBatch((address,uint256,bytes)[] calls) payable returns() +func (_Timelock *TimelockSession) BypasserExecuteBatch(calls []RBACTimelockCall) (*types.Transaction, error) { + return _Timelock.Contract.BypasserExecuteBatch(&_Timelock.TransactOpts, calls) +} + +// BypasserExecuteBatch is a paid mutator transaction binding the contract method 0x0db866b1. +// +// Solidity: function bypasserExecuteBatch((address,uint256,bytes)[] calls) payable returns() +func (_Timelock *TimelockTransactorSession) BypasserExecuteBatch(calls []RBACTimelockCall) (*types.Transaction, error) { + return _Timelock.Contract.BypasserExecuteBatch(&_Timelock.TransactOpts, calls) +} + +// Cancel is a paid mutator transaction binding the contract method 0xc4d252f5. +// +// Solidity: function cancel(bytes32 id) returns() +func (_Timelock *TimelockTransactor) Cancel(opts *bind.TransactOpts, id [32]byte) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "cancel", id) +} + +// Cancel is a paid mutator transaction binding the contract method 0xc4d252f5. +// +// Solidity: function cancel(bytes32 id) returns() +func (_Timelock *TimelockSession) Cancel(id [32]byte) (*types.Transaction, error) { + return _Timelock.Contract.Cancel(&_Timelock.TransactOpts, id) +} + +// Cancel is a paid mutator transaction binding the contract method 0xc4d252f5. +// +// Solidity: function cancel(bytes32 id) returns() +func (_Timelock *TimelockTransactorSession) Cancel(id [32]byte) (*types.Transaction, error) { + return _Timelock.Contract.Cancel(&_Timelock.TransactOpts, id) +} + +// ExecuteBatch is a paid mutator transaction binding the contract method 0x6ceef480. +// +// Solidity: function executeBatch((address,uint256,bytes)[] calls, bytes32 predecessor, bytes32 salt) payable returns() +func (_Timelock *TimelockTransactor) ExecuteBatch(opts *bind.TransactOpts, calls []RBACTimelockCall, predecessor [32]byte, salt [32]byte) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "executeBatch", calls, predecessor, salt) +} + +// ExecuteBatch is a paid mutator transaction binding the contract method 0x6ceef480. +// +// Solidity: function executeBatch((address,uint256,bytes)[] calls, bytes32 predecessor, bytes32 salt) payable returns() +func (_Timelock *TimelockSession) ExecuteBatch(calls []RBACTimelockCall, predecessor [32]byte, salt [32]byte) (*types.Transaction, error) { + return _Timelock.Contract.ExecuteBatch(&_Timelock.TransactOpts, calls, predecessor, salt) +} + +// ExecuteBatch is a paid mutator transaction binding the contract method 0x6ceef480. +// +// Solidity: function executeBatch((address,uint256,bytes)[] calls, bytes32 predecessor, bytes32 salt) payable returns() +func (_Timelock *TimelockTransactorSession) ExecuteBatch(calls []RBACTimelockCall, predecessor [32]byte, salt [32]byte) (*types.Transaction, error) { + return _Timelock.Contract.ExecuteBatch(&_Timelock.TransactOpts, calls, predecessor, salt) +} + +// GrantRole is a paid mutator transaction binding the contract method 0x2f2ff15d. +// +// Solidity: function grantRole(bytes32 role, address account) returns() +func (_Timelock *TimelockTransactor) GrantRole(opts *bind.TransactOpts, role [32]byte, account common.Address) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "grantRole", role, account) +} + +// GrantRole is a paid mutator transaction binding the contract method 0x2f2ff15d. +// +// Solidity: function grantRole(bytes32 role, address account) returns() +func (_Timelock *TimelockSession) GrantRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _Timelock.Contract.GrantRole(&_Timelock.TransactOpts, role, account) +} + +// GrantRole is a paid mutator transaction binding the contract method 0x2f2ff15d. +// +// Solidity: function grantRole(bytes32 role, address account) returns() +func (_Timelock *TimelockTransactorSession) GrantRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _Timelock.Contract.GrantRole(&_Timelock.TransactOpts, role, account) +} + +// OnERC1155BatchReceived is a paid mutator transaction binding the contract method 0xbc197c81. +// +// Solidity: function onERC1155BatchReceived(address , address , uint256[] , uint256[] , bytes ) returns(bytes4) +func (_Timelock *TimelockTransactor) OnERC1155BatchReceived(opts *bind.TransactOpts, arg0 common.Address, arg1 common.Address, arg2 []*big.Int, arg3 []*big.Int, arg4 []byte) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "onERC1155BatchReceived", arg0, arg1, arg2, arg3, arg4) +} + +// OnERC1155BatchReceived is a paid mutator transaction binding the contract method 0xbc197c81. +// +// Solidity: function onERC1155BatchReceived(address , address , uint256[] , uint256[] , bytes ) returns(bytes4) +func (_Timelock *TimelockSession) OnERC1155BatchReceived(arg0 common.Address, arg1 common.Address, arg2 []*big.Int, arg3 []*big.Int, arg4 []byte) (*types.Transaction, error) { + return _Timelock.Contract.OnERC1155BatchReceived(&_Timelock.TransactOpts, arg0, arg1, arg2, arg3, arg4) +} + +// OnERC1155BatchReceived is a paid mutator transaction binding the contract method 0xbc197c81. +// +// Solidity: function onERC1155BatchReceived(address , address , uint256[] , uint256[] , bytes ) returns(bytes4) +func (_Timelock *TimelockTransactorSession) OnERC1155BatchReceived(arg0 common.Address, arg1 common.Address, arg2 []*big.Int, arg3 []*big.Int, arg4 []byte) (*types.Transaction, error) { + return _Timelock.Contract.OnERC1155BatchReceived(&_Timelock.TransactOpts, arg0, arg1, arg2, arg3, arg4) +} + +// OnERC1155Received is a paid mutator transaction binding the contract method 0xf23a6e61. +// +// Solidity: function onERC1155Received(address , address , uint256 , uint256 , bytes ) returns(bytes4) +func (_Timelock *TimelockTransactor) OnERC1155Received(opts *bind.TransactOpts, arg0 common.Address, arg1 common.Address, arg2 *big.Int, arg3 *big.Int, arg4 []byte) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "onERC1155Received", arg0, arg1, arg2, arg3, arg4) +} + +// OnERC1155Received is a paid mutator transaction binding the contract method 0xf23a6e61. +// +// Solidity: function onERC1155Received(address , address , uint256 , uint256 , bytes ) returns(bytes4) +func (_Timelock *TimelockSession) OnERC1155Received(arg0 common.Address, arg1 common.Address, arg2 *big.Int, arg3 *big.Int, arg4 []byte) (*types.Transaction, error) { + return _Timelock.Contract.OnERC1155Received(&_Timelock.TransactOpts, arg0, arg1, arg2, arg3, arg4) +} + +// OnERC1155Received is a paid mutator transaction binding the contract method 0xf23a6e61. +// +// Solidity: function onERC1155Received(address , address , uint256 , uint256 , bytes ) returns(bytes4) +func (_Timelock *TimelockTransactorSession) OnERC1155Received(arg0 common.Address, arg1 common.Address, arg2 *big.Int, arg3 *big.Int, arg4 []byte) (*types.Transaction, error) { + return _Timelock.Contract.OnERC1155Received(&_Timelock.TransactOpts, arg0, arg1, arg2, arg3, arg4) +} + +// OnERC721Received is a paid mutator transaction binding the contract method 0x150b7a02. +// +// Solidity: function onERC721Received(address , address , uint256 , bytes ) returns(bytes4) +func (_Timelock *TimelockTransactor) OnERC721Received(opts *bind.TransactOpts, arg0 common.Address, arg1 common.Address, arg2 *big.Int, arg3 []byte) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "onERC721Received", arg0, arg1, arg2, arg3) +} + +// OnERC721Received is a paid mutator transaction binding the contract method 0x150b7a02. +// +// Solidity: function onERC721Received(address , address , uint256 , bytes ) returns(bytes4) +func (_Timelock *TimelockSession) OnERC721Received(arg0 common.Address, arg1 common.Address, arg2 *big.Int, arg3 []byte) (*types.Transaction, error) { + return _Timelock.Contract.OnERC721Received(&_Timelock.TransactOpts, arg0, arg1, arg2, arg3) +} + +// OnERC721Received is a paid mutator transaction binding the contract method 0x150b7a02. +// +// Solidity: function onERC721Received(address , address , uint256 , bytes ) returns(bytes4) +func (_Timelock *TimelockTransactorSession) OnERC721Received(arg0 common.Address, arg1 common.Address, arg2 *big.Int, arg3 []byte) (*types.Transaction, error) { + return _Timelock.Contract.OnERC721Received(&_Timelock.TransactOpts, arg0, arg1, arg2, arg3) +} + +// RenounceRole is a paid mutator transaction binding the contract method 0x36568abe. +// +// Solidity: function renounceRole(bytes32 role, address account) returns() +func (_Timelock *TimelockTransactor) RenounceRole(opts *bind.TransactOpts, role [32]byte, account common.Address) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "renounceRole", role, account) +} + +// RenounceRole is a paid mutator transaction binding the contract method 0x36568abe. +// +// Solidity: function renounceRole(bytes32 role, address account) returns() +func (_Timelock *TimelockSession) RenounceRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _Timelock.Contract.RenounceRole(&_Timelock.TransactOpts, role, account) +} + +// RenounceRole is a paid mutator transaction binding the contract method 0x36568abe. +// +// Solidity: function renounceRole(bytes32 role, address account) returns() +func (_Timelock *TimelockTransactorSession) RenounceRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _Timelock.Contract.RenounceRole(&_Timelock.TransactOpts, role, account) +} + +// RevokeRole is a paid mutator transaction binding the contract method 0xd547741f. +// +// Solidity: function revokeRole(bytes32 role, address account) returns() +func (_Timelock *TimelockTransactor) RevokeRole(opts *bind.TransactOpts, role [32]byte, account common.Address) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "revokeRole", role, account) +} + +// RevokeRole is a paid mutator transaction binding the contract method 0xd547741f. +// +// Solidity: function revokeRole(bytes32 role, address account) returns() +func (_Timelock *TimelockSession) RevokeRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _Timelock.Contract.RevokeRole(&_Timelock.TransactOpts, role, account) +} + +// RevokeRole is a paid mutator transaction binding the contract method 0xd547741f. +// +// Solidity: function revokeRole(bytes32 role, address account) returns() +func (_Timelock *TimelockTransactorSession) RevokeRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _Timelock.Contract.RevokeRole(&_Timelock.TransactOpts, role, account) +} + +// ScheduleBatch is a paid mutator transaction binding the contract method 0xa944142d. +// +// Solidity: function scheduleBatch((address,uint256,bytes)[] calls, bytes32 predecessor, bytes32 salt, uint256 delay) returns() +func (_Timelock *TimelockTransactor) ScheduleBatch(opts *bind.TransactOpts, calls []RBACTimelockCall, predecessor [32]byte, salt [32]byte, delay *big.Int) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "scheduleBatch", calls, predecessor, salt, delay) +} + +// ScheduleBatch is a paid mutator transaction binding the contract method 0xa944142d. +// +// Solidity: function scheduleBatch((address,uint256,bytes)[] calls, bytes32 predecessor, bytes32 salt, uint256 delay) returns() +func (_Timelock *TimelockSession) ScheduleBatch(calls []RBACTimelockCall, predecessor [32]byte, salt [32]byte, delay *big.Int) (*types.Transaction, error) { + return _Timelock.Contract.ScheduleBatch(&_Timelock.TransactOpts, calls, predecessor, salt, delay) +} + +// ScheduleBatch is a paid mutator transaction binding the contract method 0xa944142d. +// +// Solidity: function scheduleBatch((address,uint256,bytes)[] calls, bytes32 predecessor, bytes32 salt, uint256 delay) returns() +func (_Timelock *TimelockTransactorSession) ScheduleBatch(calls []RBACTimelockCall, predecessor [32]byte, salt [32]byte, delay *big.Int) (*types.Transaction, error) { + return _Timelock.Contract.ScheduleBatch(&_Timelock.TransactOpts, calls, predecessor, salt, delay) +} + +// UnblockFunctionSelector is a paid mutator transaction binding the contract method 0x3a98b4e4. +// +// Solidity: function unblockFunctionSelector(bytes4 selector) returns() +func (_Timelock *TimelockTransactor) UnblockFunctionSelector(opts *bind.TransactOpts, selector [4]byte) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "unblockFunctionSelector", selector) +} + +// UnblockFunctionSelector is a paid mutator transaction binding the contract method 0x3a98b4e4. +// +// Solidity: function unblockFunctionSelector(bytes4 selector) returns() +func (_Timelock *TimelockSession) UnblockFunctionSelector(selector [4]byte) (*types.Transaction, error) { + return _Timelock.Contract.UnblockFunctionSelector(&_Timelock.TransactOpts, selector) +} + +// UnblockFunctionSelector is a paid mutator transaction binding the contract method 0x3a98b4e4. +// +// Solidity: function unblockFunctionSelector(bytes4 selector) returns() +func (_Timelock *TimelockTransactorSession) UnblockFunctionSelector(selector [4]byte) (*types.Transaction, error) { + return _Timelock.Contract.UnblockFunctionSelector(&_Timelock.TransactOpts, selector) +} + +// UpdateDelay is a paid mutator transaction binding the contract method 0x64d62353. +// +// Solidity: function updateDelay(uint256 newDelay) returns() +func (_Timelock *TimelockTransactor) UpdateDelay(opts *bind.TransactOpts, newDelay *big.Int) (*types.Transaction, error) { + return _Timelock.contract.Transact(opts, "updateDelay", newDelay) +} + +// UpdateDelay is a paid mutator transaction binding the contract method 0x64d62353. +// +// Solidity: function updateDelay(uint256 newDelay) returns() +func (_Timelock *TimelockSession) UpdateDelay(newDelay *big.Int) (*types.Transaction, error) { + return _Timelock.Contract.UpdateDelay(&_Timelock.TransactOpts, newDelay) +} + +// UpdateDelay is a paid mutator transaction binding the contract method 0x64d62353. +// +// Solidity: function updateDelay(uint256 newDelay) returns() +func (_Timelock *TimelockTransactorSession) UpdateDelay(newDelay *big.Int) (*types.Transaction, error) { + return _Timelock.Contract.UpdateDelay(&_Timelock.TransactOpts, newDelay) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_Timelock *TimelockTransactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Timelock.contract.RawTransact(opts, nil) // calldata is disallowed for receive function +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_Timelock *TimelockSession) Receive() (*types.Transaction, error) { + return _Timelock.Contract.Receive(&_Timelock.TransactOpts) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_Timelock *TimelockTransactorSession) Receive() (*types.Transaction, error) { + return _Timelock.Contract.Receive(&_Timelock.TransactOpts) +} + +// TimelockBypasserCallExecutedIterator is returned from FilterBypasserCallExecuted and is used to iterate over the raw logs and unpacked data for BypasserCallExecuted events raised by the Timelock contract. +type TimelockBypasserCallExecutedIterator struct { + Event *TimelockBypasserCallExecuted // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *TimelockBypasserCallExecutedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(TimelockBypasserCallExecuted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(TimelockBypasserCallExecuted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *TimelockBypasserCallExecutedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *TimelockBypasserCallExecutedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// TimelockBypasserCallExecuted represents a BypasserCallExecuted event raised by the Timelock contract. +type TimelockBypasserCallExecuted struct { + Index *big.Int + Target common.Address + Value *big.Int + Data []byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterBypasserCallExecuted is a free log retrieval operation binding the contract event 0x6b983f337bab73dfe37faca733adf3ea35b45b8b144ec8ee2de3a1b224564b0c. +// +// Solidity: event BypasserCallExecuted(uint256 indexed index, address target, uint256 value, bytes data) +func (_Timelock *TimelockFilterer) FilterBypasserCallExecuted(opts *bind.FilterOpts, index []*big.Int) (*TimelockBypasserCallExecutedIterator, error) { + + var indexRule []interface{} + for _, indexItem := range index { + indexRule = append(indexRule, indexItem) + } + + logs, sub, err := _Timelock.contract.FilterLogs(opts, "BypasserCallExecuted", indexRule) + if err != nil { + return nil, err + } + return &TimelockBypasserCallExecutedIterator{contract: _Timelock.contract, event: "BypasserCallExecuted", logs: logs, sub: sub}, nil +} + +// WatchBypasserCallExecuted is a free log subscription operation binding the contract event 0x6b983f337bab73dfe37faca733adf3ea35b45b8b144ec8ee2de3a1b224564b0c. +// +// Solidity: event BypasserCallExecuted(uint256 indexed index, address target, uint256 value, bytes data) +func (_Timelock *TimelockFilterer) WatchBypasserCallExecuted(opts *bind.WatchOpts, sink chan<- *TimelockBypasserCallExecuted, index []*big.Int) (event.Subscription, error) { + + var indexRule []interface{} + for _, indexItem := range index { + indexRule = append(indexRule, indexItem) + } + + logs, sub, err := _Timelock.contract.WatchLogs(opts, "BypasserCallExecuted", indexRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(TimelockBypasserCallExecuted) + if err := _Timelock.contract.UnpackLog(event, "BypasserCallExecuted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseBypasserCallExecuted is a log parse operation binding the contract event 0x6b983f337bab73dfe37faca733adf3ea35b45b8b144ec8ee2de3a1b224564b0c. +// +// Solidity: event BypasserCallExecuted(uint256 indexed index, address target, uint256 value, bytes data) +func (_Timelock *TimelockFilterer) ParseBypasserCallExecuted(log types.Log) (*TimelockBypasserCallExecuted, error) { + event := new(TimelockBypasserCallExecuted) + if err := _Timelock.contract.UnpackLog(event, "BypasserCallExecuted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// TimelockCallExecutedIterator is returned from FilterCallExecuted and is used to iterate over the raw logs and unpacked data for CallExecuted events raised by the Timelock contract. +type TimelockCallExecutedIterator struct { + Event *TimelockCallExecuted // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *TimelockCallExecutedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(TimelockCallExecuted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(TimelockCallExecuted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *TimelockCallExecutedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *TimelockCallExecutedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// TimelockCallExecuted represents a CallExecuted event raised by the Timelock contract. +type TimelockCallExecuted struct { + Id [32]byte + Index *big.Int + Target common.Address + Value *big.Int + Data []byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterCallExecuted is a free log retrieval operation binding the contract event 0xc2617efa69bab66782fa219543714338489c4e9e178271560a91b82c3f612b58. +// +// Solidity: event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data) +func (_Timelock *TimelockFilterer) FilterCallExecuted(opts *bind.FilterOpts, id [][32]byte, index []*big.Int) (*TimelockCallExecutedIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var indexRule []interface{} + for _, indexItem := range index { + indexRule = append(indexRule, indexItem) + } + + logs, sub, err := _Timelock.contract.FilterLogs(opts, "CallExecuted", idRule, indexRule) + if err != nil { + return nil, err + } + return &TimelockCallExecutedIterator{contract: _Timelock.contract, event: "CallExecuted", logs: logs, sub: sub}, nil +} + +// WatchCallExecuted is a free log subscription operation binding the contract event 0xc2617efa69bab66782fa219543714338489c4e9e178271560a91b82c3f612b58. +// +// Solidity: event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data) +func (_Timelock *TimelockFilterer) WatchCallExecuted(opts *bind.WatchOpts, sink chan<- *TimelockCallExecuted, id [][32]byte, index []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var indexRule []interface{} + for _, indexItem := range index { + indexRule = append(indexRule, indexItem) + } + + logs, sub, err := _Timelock.contract.WatchLogs(opts, "CallExecuted", idRule, indexRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(TimelockCallExecuted) + if err := _Timelock.contract.UnpackLog(event, "CallExecuted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseCallExecuted is a log parse operation binding the contract event 0xc2617efa69bab66782fa219543714338489c4e9e178271560a91b82c3f612b58. +// +// Solidity: event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data) +func (_Timelock *TimelockFilterer) ParseCallExecuted(log types.Log) (*TimelockCallExecuted, error) { + event := new(TimelockCallExecuted) + if err := _Timelock.contract.UnpackLog(event, "CallExecuted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// TimelockCallScheduledIterator is returned from FilterCallScheduled and is used to iterate over the raw logs and unpacked data for CallScheduled events raised by the Timelock contract. +type TimelockCallScheduledIterator struct { + Event *TimelockCallScheduled // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *TimelockCallScheduledIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(TimelockCallScheduled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(TimelockCallScheduled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *TimelockCallScheduledIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *TimelockCallScheduledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// TimelockCallScheduled represents a CallScheduled event raised by the Timelock contract. +type TimelockCallScheduled struct { + Id [32]byte + Index *big.Int + Target common.Address + Value *big.Int + Data []byte + Predecessor [32]byte + Salt [32]byte + Delay *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterCallScheduled is a free log retrieval operation binding the contract event 0x4f4da6666f52e3b6dbc3638d8eae4017722678fe58bca79cd8320817807a65be. +// +// Solidity: event CallScheduled(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt, uint256 delay) +func (_Timelock *TimelockFilterer) FilterCallScheduled(opts *bind.FilterOpts, id [][32]byte, index []*big.Int) (*TimelockCallScheduledIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var indexRule []interface{} + for _, indexItem := range index { + indexRule = append(indexRule, indexItem) + } + + logs, sub, err := _Timelock.contract.FilterLogs(opts, "CallScheduled", idRule, indexRule) + if err != nil { + return nil, err + } + return &TimelockCallScheduledIterator{contract: _Timelock.contract, event: "CallScheduled", logs: logs, sub: sub}, nil +} + +// WatchCallScheduled is a free log subscription operation binding the contract event 0x4f4da6666f52e3b6dbc3638d8eae4017722678fe58bca79cd8320817807a65be. +// +// Solidity: event CallScheduled(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt, uint256 delay) +func (_Timelock *TimelockFilterer) WatchCallScheduled(opts *bind.WatchOpts, sink chan<- *TimelockCallScheduled, id [][32]byte, index []*big.Int) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + var indexRule []interface{} + for _, indexItem := range index { + indexRule = append(indexRule, indexItem) + } + + logs, sub, err := _Timelock.contract.WatchLogs(opts, "CallScheduled", idRule, indexRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(TimelockCallScheduled) + if err := _Timelock.contract.UnpackLog(event, "CallScheduled", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseCallScheduled is a log parse operation binding the contract event 0x4f4da6666f52e3b6dbc3638d8eae4017722678fe58bca79cd8320817807a65be. +// +// Solidity: event CallScheduled(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt, uint256 delay) +func (_Timelock *TimelockFilterer) ParseCallScheduled(log types.Log) (*TimelockCallScheduled, error) { + event := new(TimelockCallScheduled) + if err := _Timelock.contract.UnpackLog(event, "CallScheduled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// TimelockCancelledIterator is returned from FilterCancelled and is used to iterate over the raw logs and unpacked data for Cancelled events raised by the Timelock contract. +type TimelockCancelledIterator struct { + Event *TimelockCancelled // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *TimelockCancelledIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(TimelockCancelled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(TimelockCancelled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *TimelockCancelledIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *TimelockCancelledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// TimelockCancelled represents a Cancelled event raised by the Timelock contract. +type TimelockCancelled struct { + Id [32]byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterCancelled is a free log retrieval operation binding the contract event 0xbaa1eb22f2a492ba1a5fea61b8df4d27c6c8b5f3971e63bb58fa14ff72eedb70. +// +// Solidity: event Cancelled(bytes32 indexed id) +func (_Timelock *TimelockFilterer) FilterCancelled(opts *bind.FilterOpts, id [][32]byte) (*TimelockCancelledIterator, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _Timelock.contract.FilterLogs(opts, "Cancelled", idRule) + if err != nil { + return nil, err + } + return &TimelockCancelledIterator{contract: _Timelock.contract, event: "Cancelled", logs: logs, sub: sub}, nil +} + +// WatchCancelled is a free log subscription operation binding the contract event 0xbaa1eb22f2a492ba1a5fea61b8df4d27c6c8b5f3971e63bb58fa14ff72eedb70. +// +// Solidity: event Cancelled(bytes32 indexed id) +func (_Timelock *TimelockFilterer) WatchCancelled(opts *bind.WatchOpts, sink chan<- *TimelockCancelled, id [][32]byte) (event.Subscription, error) { + + var idRule []interface{} + for _, idItem := range id { + idRule = append(idRule, idItem) + } + + logs, sub, err := _Timelock.contract.WatchLogs(opts, "Cancelled", idRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(TimelockCancelled) + if err := _Timelock.contract.UnpackLog(event, "Cancelled", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseCancelled is a log parse operation binding the contract event 0xbaa1eb22f2a492ba1a5fea61b8df4d27c6c8b5f3971e63bb58fa14ff72eedb70. +// +// Solidity: event Cancelled(bytes32 indexed id) +func (_Timelock *TimelockFilterer) ParseCancelled(log types.Log) (*TimelockCancelled, error) { + event := new(TimelockCancelled) + if err := _Timelock.contract.UnpackLog(event, "Cancelled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// TimelockFunctionSelectorBlockedIterator is returned from FilterFunctionSelectorBlocked and is used to iterate over the raw logs and unpacked data for FunctionSelectorBlocked events raised by the Timelock contract. +type TimelockFunctionSelectorBlockedIterator struct { + Event *TimelockFunctionSelectorBlocked // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *TimelockFunctionSelectorBlockedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(TimelockFunctionSelectorBlocked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(TimelockFunctionSelectorBlocked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *TimelockFunctionSelectorBlockedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *TimelockFunctionSelectorBlockedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// TimelockFunctionSelectorBlocked represents a FunctionSelectorBlocked event raised by the Timelock contract. +type TimelockFunctionSelectorBlocked struct { + Selector [4]byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterFunctionSelectorBlocked is a free log retrieval operation binding the contract event 0x15b40cf8ed4c95cd3c0e1dedfdb3987c3f9bf3d3770d13ddf6dc4daa5ffae9ef. +// +// Solidity: event FunctionSelectorBlocked(bytes4 indexed selector) +func (_Timelock *TimelockFilterer) FilterFunctionSelectorBlocked(opts *bind.FilterOpts, selector [][4]byte) (*TimelockFunctionSelectorBlockedIterator, error) { + + var selectorRule []interface{} + for _, selectorItem := range selector { + selectorRule = append(selectorRule, selectorItem) + } + + logs, sub, err := _Timelock.contract.FilterLogs(opts, "FunctionSelectorBlocked", selectorRule) + if err != nil { + return nil, err + } + return &TimelockFunctionSelectorBlockedIterator{contract: _Timelock.contract, event: "FunctionSelectorBlocked", logs: logs, sub: sub}, nil +} + +// WatchFunctionSelectorBlocked is a free log subscription operation binding the contract event 0x15b40cf8ed4c95cd3c0e1dedfdb3987c3f9bf3d3770d13ddf6dc4daa5ffae9ef. +// +// Solidity: event FunctionSelectorBlocked(bytes4 indexed selector) +func (_Timelock *TimelockFilterer) WatchFunctionSelectorBlocked(opts *bind.WatchOpts, sink chan<- *TimelockFunctionSelectorBlocked, selector [][4]byte) (event.Subscription, error) { + + var selectorRule []interface{} + for _, selectorItem := range selector { + selectorRule = append(selectorRule, selectorItem) + } + + logs, sub, err := _Timelock.contract.WatchLogs(opts, "FunctionSelectorBlocked", selectorRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(TimelockFunctionSelectorBlocked) + if err := _Timelock.contract.UnpackLog(event, "FunctionSelectorBlocked", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseFunctionSelectorBlocked is a log parse operation binding the contract event 0x15b40cf8ed4c95cd3c0e1dedfdb3987c3f9bf3d3770d13ddf6dc4daa5ffae9ef. +// +// Solidity: event FunctionSelectorBlocked(bytes4 indexed selector) +func (_Timelock *TimelockFilterer) ParseFunctionSelectorBlocked(log types.Log) (*TimelockFunctionSelectorBlocked, error) { + event := new(TimelockFunctionSelectorBlocked) + if err := _Timelock.contract.UnpackLog(event, "FunctionSelectorBlocked", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// TimelockFunctionSelectorUnblockedIterator is returned from FilterFunctionSelectorUnblocked and is used to iterate over the raw logs and unpacked data for FunctionSelectorUnblocked events raised by the Timelock contract. +type TimelockFunctionSelectorUnblockedIterator struct { + Event *TimelockFunctionSelectorUnblocked // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *TimelockFunctionSelectorUnblockedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(TimelockFunctionSelectorUnblocked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(TimelockFunctionSelectorUnblocked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *TimelockFunctionSelectorUnblockedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *TimelockFunctionSelectorUnblockedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// TimelockFunctionSelectorUnblocked represents a FunctionSelectorUnblocked event raised by the Timelock contract. +type TimelockFunctionSelectorUnblocked struct { + Selector [4]byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterFunctionSelectorUnblocked is a free log retrieval operation binding the contract event 0xd91859a8d88193a56a2983deb65a5253985141c49c70bf016880b5243bd432e1. +// +// Solidity: event FunctionSelectorUnblocked(bytes4 indexed selector) +func (_Timelock *TimelockFilterer) FilterFunctionSelectorUnblocked(opts *bind.FilterOpts, selector [][4]byte) (*TimelockFunctionSelectorUnblockedIterator, error) { + + var selectorRule []interface{} + for _, selectorItem := range selector { + selectorRule = append(selectorRule, selectorItem) + } + + logs, sub, err := _Timelock.contract.FilterLogs(opts, "FunctionSelectorUnblocked", selectorRule) + if err != nil { + return nil, err + } + return &TimelockFunctionSelectorUnblockedIterator{contract: _Timelock.contract, event: "FunctionSelectorUnblocked", logs: logs, sub: sub}, nil +} + +// WatchFunctionSelectorUnblocked is a free log subscription operation binding the contract event 0xd91859a8d88193a56a2983deb65a5253985141c49c70bf016880b5243bd432e1. +// +// Solidity: event FunctionSelectorUnblocked(bytes4 indexed selector) +func (_Timelock *TimelockFilterer) WatchFunctionSelectorUnblocked(opts *bind.WatchOpts, sink chan<- *TimelockFunctionSelectorUnblocked, selector [][4]byte) (event.Subscription, error) { + + var selectorRule []interface{} + for _, selectorItem := range selector { + selectorRule = append(selectorRule, selectorItem) + } + + logs, sub, err := _Timelock.contract.WatchLogs(opts, "FunctionSelectorUnblocked", selectorRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(TimelockFunctionSelectorUnblocked) + if err := _Timelock.contract.UnpackLog(event, "FunctionSelectorUnblocked", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseFunctionSelectorUnblocked is a log parse operation binding the contract event 0xd91859a8d88193a56a2983deb65a5253985141c49c70bf016880b5243bd432e1. +// +// Solidity: event FunctionSelectorUnblocked(bytes4 indexed selector) +func (_Timelock *TimelockFilterer) ParseFunctionSelectorUnblocked(log types.Log) (*TimelockFunctionSelectorUnblocked, error) { + event := new(TimelockFunctionSelectorUnblocked) + if err := _Timelock.contract.UnpackLog(event, "FunctionSelectorUnblocked", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// TimelockMinDelayChangeIterator is returned from FilterMinDelayChange and is used to iterate over the raw logs and unpacked data for MinDelayChange events raised by the Timelock contract. +type TimelockMinDelayChangeIterator struct { + Event *TimelockMinDelayChange // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *TimelockMinDelayChangeIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(TimelockMinDelayChange) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(TimelockMinDelayChange) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *TimelockMinDelayChangeIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *TimelockMinDelayChangeIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// TimelockMinDelayChange represents a MinDelayChange event raised by the Timelock contract. +type TimelockMinDelayChange struct { + OldDuration *big.Int + NewDuration *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterMinDelayChange is a free log retrieval operation binding the contract event 0x11c24f4ead16507c69ac467fbd5e4eed5fb5c699626d2cc6d66421df253886d5. +// +// Solidity: event MinDelayChange(uint256 oldDuration, uint256 newDuration) +func (_Timelock *TimelockFilterer) FilterMinDelayChange(opts *bind.FilterOpts) (*TimelockMinDelayChangeIterator, error) { + + logs, sub, err := _Timelock.contract.FilterLogs(opts, "MinDelayChange") + if err != nil { + return nil, err + } + return &TimelockMinDelayChangeIterator{contract: _Timelock.contract, event: "MinDelayChange", logs: logs, sub: sub}, nil +} + +// WatchMinDelayChange is a free log subscription operation binding the contract event 0x11c24f4ead16507c69ac467fbd5e4eed5fb5c699626d2cc6d66421df253886d5. +// +// Solidity: event MinDelayChange(uint256 oldDuration, uint256 newDuration) +func (_Timelock *TimelockFilterer) WatchMinDelayChange(opts *bind.WatchOpts, sink chan<- *TimelockMinDelayChange) (event.Subscription, error) { + + logs, sub, err := _Timelock.contract.WatchLogs(opts, "MinDelayChange") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(TimelockMinDelayChange) + if err := _Timelock.contract.UnpackLog(event, "MinDelayChange", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseMinDelayChange is a log parse operation binding the contract event 0x11c24f4ead16507c69ac467fbd5e4eed5fb5c699626d2cc6d66421df253886d5. +// +// Solidity: event MinDelayChange(uint256 oldDuration, uint256 newDuration) +func (_Timelock *TimelockFilterer) ParseMinDelayChange(log types.Log) (*TimelockMinDelayChange, error) { + event := new(TimelockMinDelayChange) + if err := _Timelock.contract.UnpackLog(event, "MinDelayChange", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// TimelockRoleAdminChangedIterator is returned from FilterRoleAdminChanged and is used to iterate over the raw logs and unpacked data for RoleAdminChanged events raised by the Timelock contract. +type TimelockRoleAdminChangedIterator struct { + Event *TimelockRoleAdminChanged // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *TimelockRoleAdminChangedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(TimelockRoleAdminChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(TimelockRoleAdminChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *TimelockRoleAdminChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *TimelockRoleAdminChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// TimelockRoleAdminChanged represents a RoleAdminChanged event raised by the Timelock contract. +type TimelockRoleAdminChanged struct { + Role [32]byte + PreviousAdminRole [32]byte + NewAdminRole [32]byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRoleAdminChanged is a free log retrieval operation binding the contract event 0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff. +// +// Solidity: event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +func (_Timelock *TimelockFilterer) FilterRoleAdminChanged(opts *bind.FilterOpts, role [][32]byte, previousAdminRole [][32]byte, newAdminRole [][32]byte) (*TimelockRoleAdminChangedIterator, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var previousAdminRoleRule []interface{} + for _, previousAdminRoleItem := range previousAdminRole { + previousAdminRoleRule = append(previousAdminRoleRule, previousAdminRoleItem) + } + var newAdminRoleRule []interface{} + for _, newAdminRoleItem := range newAdminRole { + newAdminRoleRule = append(newAdminRoleRule, newAdminRoleItem) + } + + logs, sub, err := _Timelock.contract.FilterLogs(opts, "RoleAdminChanged", roleRule, previousAdminRoleRule, newAdminRoleRule) + if err != nil { + return nil, err + } + return &TimelockRoleAdminChangedIterator{contract: _Timelock.contract, event: "RoleAdminChanged", logs: logs, sub: sub}, nil +} + +// WatchRoleAdminChanged is a free log subscription operation binding the contract event 0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff. +// +// Solidity: event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +func (_Timelock *TimelockFilterer) WatchRoleAdminChanged(opts *bind.WatchOpts, sink chan<- *TimelockRoleAdminChanged, role [][32]byte, previousAdminRole [][32]byte, newAdminRole [][32]byte) (event.Subscription, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var previousAdminRoleRule []interface{} + for _, previousAdminRoleItem := range previousAdminRole { + previousAdminRoleRule = append(previousAdminRoleRule, previousAdminRoleItem) + } + var newAdminRoleRule []interface{} + for _, newAdminRoleItem := range newAdminRole { + newAdminRoleRule = append(newAdminRoleRule, newAdminRoleItem) + } + + logs, sub, err := _Timelock.contract.WatchLogs(opts, "RoleAdminChanged", roleRule, previousAdminRoleRule, newAdminRoleRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(TimelockRoleAdminChanged) + if err := _Timelock.contract.UnpackLog(event, "RoleAdminChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRoleAdminChanged is a log parse operation binding the contract event 0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff. +// +// Solidity: event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +func (_Timelock *TimelockFilterer) ParseRoleAdminChanged(log types.Log) (*TimelockRoleAdminChanged, error) { + event := new(TimelockRoleAdminChanged) + if err := _Timelock.contract.UnpackLog(event, "RoleAdminChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// TimelockRoleGrantedIterator is returned from FilterRoleGranted and is used to iterate over the raw logs and unpacked data for RoleGranted events raised by the Timelock contract. +type TimelockRoleGrantedIterator struct { + Event *TimelockRoleGranted // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *TimelockRoleGrantedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(TimelockRoleGranted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(TimelockRoleGranted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *TimelockRoleGrantedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *TimelockRoleGrantedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// TimelockRoleGranted represents a RoleGranted event raised by the Timelock contract. +type TimelockRoleGranted struct { + Role [32]byte + Account common.Address + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRoleGranted is a free log retrieval operation binding the contract event 0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d. +// +// Solidity: event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +func (_Timelock *TimelockFilterer) FilterRoleGranted(opts *bind.FilterOpts, role [][32]byte, account []common.Address, sender []common.Address) (*TimelockRoleGrantedIterator, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _Timelock.contract.FilterLogs(opts, "RoleGranted", roleRule, accountRule, senderRule) + if err != nil { + return nil, err + } + return &TimelockRoleGrantedIterator{contract: _Timelock.contract, event: "RoleGranted", logs: logs, sub: sub}, nil +} + +// WatchRoleGranted is a free log subscription operation binding the contract event 0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d. +// +// Solidity: event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +func (_Timelock *TimelockFilterer) WatchRoleGranted(opts *bind.WatchOpts, sink chan<- *TimelockRoleGranted, role [][32]byte, account []common.Address, sender []common.Address) (event.Subscription, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _Timelock.contract.WatchLogs(opts, "RoleGranted", roleRule, accountRule, senderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(TimelockRoleGranted) + if err := _Timelock.contract.UnpackLog(event, "RoleGranted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRoleGranted is a log parse operation binding the contract event 0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d. +// +// Solidity: event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +func (_Timelock *TimelockFilterer) ParseRoleGranted(log types.Log) (*TimelockRoleGranted, error) { + event := new(TimelockRoleGranted) + if err := _Timelock.contract.UnpackLog(event, "RoleGranted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// TimelockRoleRevokedIterator is returned from FilterRoleRevoked and is used to iterate over the raw logs and unpacked data for RoleRevoked events raised by the Timelock contract. +type TimelockRoleRevokedIterator struct { + Event *TimelockRoleRevoked // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *TimelockRoleRevokedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(TimelockRoleRevoked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(TimelockRoleRevoked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *TimelockRoleRevokedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *TimelockRoleRevokedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// TimelockRoleRevoked represents a RoleRevoked event raised by the Timelock contract. +type TimelockRoleRevoked struct { + Role [32]byte + Account common.Address + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRoleRevoked is a free log retrieval operation binding the contract event 0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b. +// +// Solidity: event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +func (_Timelock *TimelockFilterer) FilterRoleRevoked(opts *bind.FilterOpts, role [][32]byte, account []common.Address, sender []common.Address) (*TimelockRoleRevokedIterator, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _Timelock.contract.FilterLogs(opts, "RoleRevoked", roleRule, accountRule, senderRule) + if err != nil { + return nil, err + } + return &TimelockRoleRevokedIterator{contract: _Timelock.contract, event: "RoleRevoked", logs: logs, sub: sub}, nil +} + +// WatchRoleRevoked is a free log subscription operation binding the contract event 0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b. +// +// Solidity: event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +func (_Timelock *TimelockFilterer) WatchRoleRevoked(opts *bind.WatchOpts, sink chan<- *TimelockRoleRevoked, role [][32]byte, account []common.Address, sender []common.Address) (event.Subscription, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _Timelock.contract.WatchLogs(opts, "RoleRevoked", roleRule, accountRule, senderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(TimelockRoleRevoked) + if err := _Timelock.contract.UnpackLog(event, "RoleRevoked", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRoleRevoked is a log parse operation binding the contract event 0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b. +// +// Solidity: event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +func (_Timelock *TimelockFilterer) ParseRoleRevoked(log types.Log) (*TimelockRoleRevoked, error) { + event := new(TimelockRoleRevoked) + if err := _Timelock.contract.UnpackLog(event, "RoleRevoked", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/pkg/timelock/contract/Timelock.json b/pkg/timelock/contract/Timelock.json new file mode 100644 index 0000000..97561ab --- /dev/null +++ b/pkg/timelock/contract/Timelock.json @@ -0,0 +1,611 @@ +[ + { + "inputs": [ + { "internalType": "uint256", "name": "minDelay", "type": "uint256" }, + { "internalType": "address", "name": "admin", "type": "address" }, + { "internalType": "address[]", "name": "proposers", "type": "address[]" }, + { "internalType": "address[]", "name": "executors", "type": "address[]" }, + { + "internalType": "address[]", + "name": "cancellers", + "type": "address[]" + }, + { "internalType": "address[]", "name": "bypassers", "type": "address[]" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "BypasserCallExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "CallExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "predecessor", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "delay", + "type": "uint256" + } + ], + "name": "CallScheduled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "Cancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes4", + "name": "selector", + "type": "bytes4" + } + ], + "name": "FunctionSelectorBlocked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes4", + "name": "selector", + "type": "bytes4" + } + ], + "name": "FunctionSelectorUnblocked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldDuration", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newDuration", + "type": "uint256" + } + ], + "name": "MinDelayChange", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "inputs": [], + "name": "ADMIN_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "BYPASSER_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CANCELLER_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EXECUTOR_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PROPOSER_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "selector", "type": "bytes4" } + ], + "name": "blockFunctionSelector", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "bytes", "name": "data", "type": "bytes" } + ], + "internalType": "struct RBACTimelock.Call[]", + "name": "calls", + "type": "tuple[]" + } + ], + "name": "bypasserExecuteBatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "id", "type": "bytes32" }], + "name": "cancel", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "bytes", "name": "data", "type": "bytes" } + ], + "internalType": "struct RBACTimelock.Call[]", + "name": "calls", + "type": "tuple[]" + }, + { "internalType": "bytes32", "name": "predecessor", "type": "bytes32" }, + { "internalType": "bytes32", "name": "salt", "type": "bytes32" } + ], + "name": "executeBatch", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "index", "type": "uint256" } + ], + "name": "getBlockedFunctionSelectorAt", + "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBlockedFunctionSelectorCount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMinDelay", + "outputs": [ + { "internalType": "uint256", "name": "duration", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" } + ], + "name": "getRoleAdmin", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "uint256", "name": "index", "type": "uint256" } + ], + "name": "getRoleMember", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" } + ], + "name": "getRoleMemberCount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "id", "type": "bytes32" }], + "name": "getTimestamp", + "outputs": [ + { "internalType": "uint256", "name": "timestamp", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "hasRole", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "bytes", "name": "data", "type": "bytes" } + ], + "internalType": "struct RBACTimelock.Call[]", + "name": "calls", + "type": "tuple[]" + }, + { "internalType": "bytes32", "name": "predecessor", "type": "bytes32" }, + { "internalType": "bytes32", "name": "salt", "type": "bytes32" } + ], + "name": "hashOperationBatch", + "outputs": [ + { "internalType": "bytes32", "name": "hash", "type": "bytes32" } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "id", "type": "bytes32" }], + "name": "isOperation", + "outputs": [ + { "internalType": "bool", "name": "registered", "type": "bool" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "id", "type": "bytes32" }], + "name": "isOperationDone", + "outputs": [{ "internalType": "bool", "name": "done", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "id", "type": "bytes32" }], + "name": "isOperationPending", + "outputs": [{ "internalType": "bool", "name": "pending", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "id", "type": "bytes32" }], + "name": "isOperationReady", + "outputs": [{ "internalType": "bool", "name": "ready", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "uint256[]", "name": "", "type": "uint256[]" }, + { "internalType": "uint256[]", "name": "", "type": "uint256[]" }, + { "internalType": "bytes", "name": "", "type": "bytes" } + ], + "name": "onERC1155BatchReceived", + "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "bytes", "name": "", "type": "bytes" } + ], + "name": "onERC1155Received", + "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "bytes", "name": "", "type": "bytes" } + ], + "name": "onERC721Received", + "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "address", "name": "target", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "bytes", "name": "data", "type": "bytes" } + ], + "internalType": "struct RBACTimelock.Call[]", + "name": "calls", + "type": "tuple[]" + }, + { "internalType": "bytes32", "name": "predecessor", "type": "bytes32" }, + { "internalType": "bytes32", "name": "salt", "type": "bytes32" }, + { "internalType": "uint256", "name": "delay", "type": "uint256" } + ], + "name": "scheduleBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "selector", "type": "bytes4" } + ], + "name": "unblockFunctionSelector", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "newDelay", "type": "uint256" } + ], + "name": "updateDelay", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/pkg/timelock/contract/Timelock.sol b/pkg/timelock/contract/Timelock.sol new file mode 100644 index 0000000..12752ce --- /dev/null +++ b/pkg/timelock/contract/Timelock.sol @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "openzeppelin-contracts/access/AccessControlEnumerable.sol"; +import "openzeppelin-contracts/token/ERC721/IERC721Receiver.sol"; +import "openzeppelin-contracts/token/ERC1155/IERC1155Receiver.sol"; +import "openzeppelin-contracts/utils/Address.sol"; +import "openzeppelin-contracts/utils/structs/EnumerableSet.sol"; + +/** + * @notice Contract module which acts as a timelocked controller with role-based + * access control. When set as the owner of an `Ownable` smart contract, it + * can enforce a timelock on `onlyOwner` maintenance operations and prevent + * a list of blocked functions from being called. The timelock can be bypassed + * by a bypasser or an admin in emergency situations that require quick action. + * + * Non-emergency actions are expected to follow the timelock. + * + * The contract has five roles. Each role can be inhabited by multiple + * (potentially overlapping) addresses. + * + * 1) Admin: The admin manages membership for all roles (including the admin + * role itself). The admin automatically inhabits all other roles. The admin + * can call the bypasserExecuteBatch function to bypass any restrictions like + * the delay imposed by the timelock and the list of blocked functions. The + * admin can manage the list of blocked functions. In practice, the admin + * role is expected to (1) be inhabited by a contract requiring a secure + * quorum of votes before taking any action and (2) to be used rarely, namely + * only for emergency actions or configuration of the RBACTimelock. + * + * 2) Proposer: The proposer can schedule delayed operations that don't use any + * blocked function selector. + * + * 3) Executor: The executor can execute previously scheduled operations once + * their delay has expired. + * + * 4) Canceller: The canceller can cancel operations that have been scheduled + * but not yet executed. + * + * 5) Bypasser: The bypasser can bypass any restrictions like the delay imposed + * by the timelock and the list of blocked functions to immediately execute + * operations, e.g. in case of emergencies. + * + * + * @dev This contract is a modified version of OpenZeppelin's TimelockController + * contract from v4.7.0, accessed in commit + * 561d1061fc568f04c7a65853538e834a889751e8 of + * github.com/OpenZeppelin/openzeppelin-contracts + */ +contract RBACTimelock is AccessControlEnumerable, IERC721Receiver, IERC1155Receiver { + using EnumerableSet for EnumerableSet.Bytes32Set; + + struct Call { + address target; + uint256 value; + bytes data; + } + + bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); + bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); + bytes32 public constant CANCELLER_ROLE = keccak256("CANCELLER_ROLE"); + bytes32 public constant BYPASSER_ROLE = keccak256("BYPASSER_ROLE"); + uint256 internal constant _DONE_TIMESTAMP = uint256(1); + + mapping(bytes32 => uint256) private _timestamps; + uint256 private _minDelay; + EnumerableSet.Bytes32Set private _blockedFunctionSelectors; + + /** + * @dev Emitted when a call is scheduled as part of operation `id`. + */ + event CallScheduled( + bytes32 indexed id, + uint256 indexed index, + address target, + uint256 value, + bytes data, + bytes32 predecessor, + bytes32 salt, + uint256 delay + ); + + /** + * @dev Emitted when a call is performed as part of operation `id`. + */ + event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data); + + /** + * @dev Emitted when a call is performed via bypasser. + */ + event BypasserCallExecuted(uint256 indexed index, address target, uint256 value, bytes data); + + /** + * @dev Emitted when operation `id` is cancelled. + */ + event Cancelled(bytes32 indexed id); + + /** + * @dev Emitted when the minimum delay for future operations is modified. + */ + event MinDelayChange(uint256 oldDuration, uint256 newDuration); + + /** + * @dev Emitted when a function selector is blocked. + */ + event FunctionSelectorBlocked(bytes4 indexed selector); + + /** + * @dev Emitted when a function selector is unblocked. + */ + event FunctionSelectorUnblocked(bytes4 indexed selector); + + + /** + * @dev Initializes the contract with the following parameters: + * + * - `minDelay`: initial minimum delay for operations + * - `admin`: account to be granted admin role + * - `proposers`: accounts to be granted proposer role + * - `executors`: accounts to be granted executor role + * - `cancellers`: accounts to be granted canceller role + * + * The admin is the most powerful role. Only an admin can manage membership + * of all roles. Only an admin can bypass restrictions like the timelock + * minDelay and blocked function selectors. + */ + constructor( + uint256 minDelay, + address admin, + address[] memory proposers, + address[] memory executors, + address[] memory cancellers, + address[] memory bypassers + ) { + _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); + _setRoleAdmin(PROPOSER_ROLE, ADMIN_ROLE); + _setRoleAdmin(EXECUTOR_ROLE, ADMIN_ROLE); + _setRoleAdmin(CANCELLER_ROLE, ADMIN_ROLE); + _setRoleAdmin(BYPASSER_ROLE, ADMIN_ROLE); + + _setupRole(ADMIN_ROLE, admin); + + // register proposers + for (uint256 i = 0; i < proposers.length; ++i) { + _setupRole(PROPOSER_ROLE, proposers[i]); + } + + // register executors + for (uint256 i = 0; i < executors.length; ++i) { + _setupRole(EXECUTOR_ROLE, executors[i]); + } + + // register cancellers + for (uint256 i = 0; i < cancellers.length; ++i) { + _setupRole(CANCELLER_ROLE, cancellers[i]); + } + + // register bypassers + for (uint256 i = 0; i < bypassers.length; ++i) { + _setupRole(BYPASSER_ROLE, bypassers[i]); + } + + _minDelay = minDelay; + emit MinDelayChange(0, minDelay); + } + + /** + * @dev Modifier to make a function callable only by a certain role or the + * admin role. + */ + modifier onlyRoleOrAdminRole(bytes32 role) { + address sender = _msgSender(); + if (!hasRole(ADMIN_ROLE, sender)) { + _checkRole(role, sender); + } + _; + } + + /** + * @dev Contract might receive/hold ETH as part of the maintenance process. + */ + receive() external payable {} + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, AccessControlEnumerable) returns (bool) { + return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Returns whether an id correspond to a registered operation. This + * includes both Pending, Ready and Done operations. + */ + function isOperation(bytes32 id) public view virtual returns (bool registered) { + return getTimestamp(id) > 0; + } + + /** + * @dev Returns whether an operation is pending or not. + */ + function isOperationPending(bytes32 id) public view virtual returns (bool pending) { + return getTimestamp(id) > _DONE_TIMESTAMP; + } + + /** + * @dev Returns whether an operation is ready or not. + */ + function isOperationReady(bytes32 id) public view virtual returns (bool ready) { + uint256 timestamp = getTimestamp(id); + return timestamp > _DONE_TIMESTAMP && timestamp <= block.timestamp; + } + + /** + * @dev Returns whether an operation is done or not. + */ + function isOperationDone(bytes32 id) public view virtual returns (bool done) { + return getTimestamp(id) == _DONE_TIMESTAMP; + } + + /** + * @dev Returns the timestamp at with an operation becomes ready (0 for + * unset operations, 1 for done operations). + */ + function getTimestamp(bytes32 id) public view virtual returns (uint256 timestamp) { + return _timestamps[id]; + } + + /** + * @dev Returns the minimum delay for an operation to become valid. + * + * This value can be changed by executing an operation that calls `updateDelay`. + */ + function getMinDelay() public view virtual returns (uint256 duration) { + return _minDelay; + } + + /** + * @dev Returns the identifier of an operation containing a batch of + * transactions. + */ + function hashOperationBatch( + Call[] calldata calls, + bytes32 predecessor, + bytes32 salt + ) public pure virtual returns (bytes32 hash) { + return keccak256(abi.encode(calls, predecessor, salt)); + } + + /** + * @dev Schedule an operation containing a batch of transactions. + * + * Emits one {CallScheduled} event per transaction in the batch. + * + * Requirements: + * + * - the caller must have the 'proposer' or 'admin' role. + * - all payloads must not start with a blocked function selector. + */ + function scheduleBatch( + Call[] calldata calls, + bytes32 predecessor, + bytes32 salt, + uint256 delay + ) public virtual onlyRoleOrAdminRole(PROPOSER_ROLE) { + bytes32 id = hashOperationBatch(calls, predecessor, salt); + _schedule(id, delay); + for (uint256 i = 0; i < calls.length; ++i) { + _checkFunctionSelectorNotBlocked(calls[i].data); + emit CallScheduled(id, i, calls[i].target, calls[i].value, calls[i].data, predecessor, salt, delay); + } + } + + /** + * @dev Schedule an operation that is to becomes valid after a given delay. + */ + function _schedule(bytes32 id, uint256 delay) private { + require(!isOperation(id), "RBACTimelock: operation already scheduled"); + require(delay >= getMinDelay(), "RBACTimelock: insufficient delay"); + _timestamps[id] = block.timestamp + delay; + } + + /** + * @dev Cancel an operation. + * + * Requirements: + * + * - the caller must have the 'canceller' or 'admin' role. + */ + function cancel(bytes32 id) public virtual onlyRoleOrAdminRole(CANCELLER_ROLE) { + require(isOperationPending(id), "RBACTimelock: operation cannot be cancelled"); + delete _timestamps[id]; + + emit Cancelled(id); + } + + /** + * @dev Execute an (ready) operation containing a batch of transactions. + * Note that we perform a raw call to each target. Raw calls to targets that + * don't have associated contract code will always succeed regardless of + * payload. + * + * Emits one {CallExecuted} event per transaction in the batch. + * + * Requirements: + * + * - the caller must have the 'executor' or 'admin' role. + */ + function executeBatch( + Call[] calldata calls, + bytes32 predecessor, + bytes32 salt + ) public payable virtual onlyRoleOrAdminRole(EXECUTOR_ROLE) { + bytes32 id = hashOperationBatch(calls, predecessor, salt); + + _beforeCall(id, predecessor); + for (uint256 i = 0; i < calls.length; ++i) { + _execute(calls[i]); + emit CallExecuted(id, i, calls[i].target, calls[i].value, calls[i].data); + } + _afterCall(id); + } + + /** + * @dev Execute an operation's call. + */ + function _execute( + Call calldata call + ) internal virtual { + (bool success, ) = call.target.call{value: call.value}(call.data); + require(success, "RBACTimelock: underlying transaction reverted"); + } + + /** + * @dev Checks before execution of an operation's calls. + */ + function _beforeCall(bytes32 id, bytes32 predecessor) private view { + require(isOperationReady(id), "RBACTimelock: operation is not ready"); + require(predecessor == bytes32(0) || isOperationDone(predecessor), "RBACTimelock: missing dependency"); + } + + /** + * @dev Checks after execution of an operation's calls. + */ + function _afterCall(bytes32 id) private { + require(isOperationReady(id), "RBACTimelock: operation is not ready"); + _timestamps[id] = _DONE_TIMESTAMP; + } + + /** + * @dev Changes the minimum timelock duration for future operations. + * + * Emits a {MinDelayChange} event. + * + * Requirements: + * + * - the caller must have the 'admin' role. + */ + function updateDelay(uint256 newDelay) external virtual onlyRole(ADMIN_ROLE) { + emit MinDelayChange(_minDelay, newDelay); + _minDelay = newDelay; + } + + /** + * @dev See {IERC721Receiver-onERC721Received}. + */ + function onERC721Received( + address, + address, + uint256, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC721Received.selector; + } + + /** + * @dev See {IERC1155Receiver-onERC1155Received}. + */ + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155Received.selector; + } + + /** + * @dev See {IERC1155Receiver-onERC1155BatchReceived}. + */ + function onERC1155BatchReceived( + address, + address, + uint256[] memory, + uint256[] memory, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155BatchReceived.selector; + } + + /* + * New functions not present in orginal OpenZeppelin TimelockController + */ + + /** + * @dev Blocks a function selector from being used, i.e. schedule + * operations with this function selector will revert. + * + * Requirements: + * + * - the caller must have the 'admin' role. + */ + function blockFunctionSelector(bytes4 selector) external onlyRole(ADMIN_ROLE) { + if (_blockedFunctionSelectors.add(selector)) { + emit FunctionSelectorBlocked(selector); + } + } + + /** + * @dev Unblocks a previously blocked function selector so it can be used again. + * Requirements: + * + * - the caller must have the 'admin' role. + */ + function unblockFunctionSelector(bytes4 selector) external onlyRole(ADMIN_ROLE) { + if (_blockedFunctionSelectors.remove(selector)) { + emit FunctionSelectorUnblocked(selector); + } + } + + /** + * @dev Returns the number of blocked function selectors. + */ + function getBlockedFunctionSelectorCount() external view returns (uint256) { + return _blockedFunctionSelectors.length(); + } + + /** + * @dev Returns the blocked function selector with the given index. Function + * selectors are not sorted in any particular way, and their ordering may + * change at any point. + * + * WARNING: When using {getBlockedFunctionSelectorCount} and + * {getBlockedFunctionSelectorAt} via RPC, make sure you perform all queries + * on the same block. When using these functions within an onchain + * transaction, make sure that the state of this contract hasn't changed in + * between invocations to avoid time-of-check time-of-use bugs. + * See the following + * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum + * post] for more information. + */ + function getBlockedFunctionSelectorAt(uint256 index) external view returns (bytes4) { + return bytes4(_blockedFunctionSelectors.at(index)); + } + + /** + * @dev Directly execute a batch of transactions, bypassing any other + * checks. + * Note that we perform a raw call to each target. Raw calls to targets that + * don't have associated contract code will always succeed regardless of + * payload. + * + * Emits one {BypasserCallExecuted} event per transaction in the batch. + * + * Requirements: + * + * - the caller must have the 'bypasser' or 'admin' role. + */ + function bypasserExecuteBatch( + Call[] calldata calls + ) public payable virtual onlyRoleOrAdminRole(BYPASSER_ROLE) { + for (uint256 i = 0; i < calls.length; ++i) { + _execute(calls[i]); + emit BypasserCallExecuted(i, calls[i].target, calls[i].value, calls[i].data); + } + } + + /** + * @dev Checks to see if the function being scheduled is blocked. This + * is used when trying to schedule or batch schedule an operation. + */ + function _checkFunctionSelectorNotBlocked(bytes calldata data) private view { + if (data.length < 4) { + return; + } + bytes4 selector = bytes4(data[:4]); + require(!_blockedFunctionSelectors.contains(bytes32(selector)), "RBACTimelock: selector is blocked"); + } +} diff --git a/pkg/timelock/operations.go b/pkg/timelock/operations.go new file mode 100644 index 0000000..163d245 --- /dev/null +++ b/pkg/timelock/operations.go @@ -0,0 +1,144 @@ +package timelock + +import ( + "context" + "crypto/ecdsa" + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/smartcontractkit/timelock-worker/pkg/timelock/contract" +) + +// execute runs the CallScheduled operation if: +// - The predecessor operation is finished +// - The operation is ready to be executed +// Otherwise the operation will throw an info log and wait for a future tick. +func (tw *Worker) execute(ctx context.Context, op []*contract.TimelockCallScheduled) { + if isReady(ctx, tw.contract, op[0].Id) { + tw.logger.Debug().Msgf("execute operation %x", op[0].Id) + + tx, err := executeCallSchedule(ctx, &tw.executeContract.TimelockTransactor, op, tw.privateKey) + if err != nil || tx == nil { + tw.logger.Error().Msgf("execute operation %x error: %s", op[0].Id, err.Error()) + } else { + tw.logger.Info().Msgf("execute operation %x success: %s", op[0].Id, tx.Hash()) + + if _, err = bind.WaitMined(ctx, tw.ethClient, tx); err != nil { + tw.logger.Error().Msgf("execute operation %x error: %s", op[0].Id, err.Error()) + } + } + } else { + tw.logger.Info().Msgf("skipping operation %x: not ready", op[0].Id) + } +} + +// executeCallScheduleOperation is the handler to execute a CallScheduled operation. +func executeCallSchedule(ctx context.Context, c *contract.TimelockTransactor, cs []*contract.TimelockCallScheduled, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) { + fromAddress, err := privateKeyToAddress(privateKey) + if err != nil { + return nil, err + } + + // Compute all the different calls from each specific CallSchedule. + var calls []contract.RBACTimelockCall + for _, op := range cs { + calls = append(calls, contract.RBACTimelockCall{ + Target: op.Target, + Value: op.Value, + Data: op.Data, + }) + } + + // Execute the tx's with all the computed calls. + // Predecessor and salt are the same for all the tx's. + tx, err := c.ExecuteBatch( + &bind.TransactOpts{ + From: fromAddress, + Signer: signTx, + Context: ctx}, + calls, + cs[0].Predecessor, + cs[0].Salt) + if err != nil { + return nil, err + } + + return tx, nil +} + +// isOperation returns a boolean determining if this is a valid operation. +// It's mostly to be used for sanity checks. +func isOperation(ctx context.Context, c *contract.Timelock, id [32]byte) bool { + isOp, err := c.IsOperation(&bind.CallOpts{Context: ctx}, id) + if err != nil { + return false + } + + return isOp +} + +// isReady returns if the schedule operation is ready. +// Not applicable to other operation types. +func isReady(ctx context.Context, c *contract.Timelock, id [32]byte) bool { + isReady, err := c.IsOperationReady(&bind.CallOpts{Context: ctx}, id) + if err != nil { + return false + } + + return isReady +} + +// isDone returns true when the operation has been completed. +func isDone(ctx context.Context, c *contract.Timelock, id [32]byte) bool { + isDone, err := c.IsOperationDone(&bind.CallOpts{Context: ctx}, id) + if err != nil { + return false + } + + return isDone +} + +// isReady returns if the schedule operation is pending. +// Not applicable to other operation types. +func isPending(ctx context.Context, c *contract.Timelock, id [32]byte) bool { + isPending, err := c.IsOperationPending(&bind.CallOpts{Context: ctx}, id) + if err != nil { + return false + } + + return isPending +} + +// signTx is a function that implements the type SignerFn, so can be passed as a Signer method. +func signTx(address common.Address, tx *types.Transaction) (*types.Transaction, error) { + if tWorker == nil { + return nil, fmt.Errorf("timelockWorker can't be instantiated") + } + + chainID, err := tWorker.ethClient.NetworkID(context.Background()) + if err != nil { + return nil, err + } + + signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainID), tWorker.privateKey) + if err != nil { + return nil, err + } + + return signedTx, nil +} + +// privateKeyToAddress is an util function to calculate the address of a given private key. +// From a private key the public key can be deducted, and with the pubkey is +// trivial to calculate the address. +func privateKeyToAddress(privateKey *ecdsa.PrivateKey) (common.Address, error) { + publicKeyECDSA, ok := privateKey.Public().(*ecdsa.PublicKey) + if !ok { + return common.Address{}, fmt.Errorf("publicKey is not of type *ecdsa.PublicKey") + } + + return crypto.PubkeyToAddress(*publicKeyECDSA), nil +} diff --git a/pkg/timelock/operations_test.go b/pkg/timelock/operations_test.go new file mode 100644 index 0000000..9edcb6b --- /dev/null +++ b/pkg/timelock/operations_test.go @@ -0,0 +1,205 @@ +package timelock + +import ( + "context" + "crypto/ecdsa" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/timelock-worker/pkg/timelock/contract" +) + +func Test_isOperation(t *testing.T) { + testWorker, _ := NewTimelockWorker(testNodeURL, testTimelockAddress, testCallProxyAddress, testPrivateKey, testFromBlock, int64(testPollPeriod), testLogger) + var ctx context.Context + + type args struct { + ctx context.Context + c *contract.Timelock + id [32]byte + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "isOperation: empty, should fail", + args: args{ + ctx: ctx, + c: testWorker.contract, + id: [32]byte{}, + }, + want: false, + }, + { + name: "isOperation: real operation, should succeed", + args: args{ + ctx: ctx, + c: testWorker.contract, + id: [32]byte{79, 143, 96, 15, 56, 41, 187, 178, 167, 120, 61, 145, 140, 241, 3, 220, 155, 151, 111, 184, 96, 128, 73, 10, 146, 173, 93, 211, 80, 225, 69, 183}, + }, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isOperation(tt.args.ctx, tt.args.c, tt.args.id); got != tt.want { + t.Errorf("isOperation() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_isReady(t *testing.T) { + testWorker, _ := NewTimelockWorker(testNodeURL, testTimelockAddress, testCallProxyAddress, testPrivateKey, testFromBlock, int64(testPollPeriod), testLogger) + var ctx context.Context + + type args struct { + ctx context.Context + c *contract.Timelock + id [32]byte + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "isReady: empty, should fail", + args: args{ + ctx: ctx, + c: testWorker.contract, + id: [32]byte{}, + }, + want: false, + }, + { + name: "isReady: real operation not ready, should fail", + args: args{ + ctx: ctx, + c: testWorker.contract, + id: [32]byte{1, 6, 204, 130, 40, 127, 196, 24, 117, 166, 94, 233, 151, 222, 146, 45, 238, 98, 11, 85, 157, 173, 136, 226, 220, 74, 141, 29, 9, 125, 249, 119}, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isReady(tt.args.ctx, tt.args.c, tt.args.id); got != tt.want { + t.Errorf("isReady() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_isDone(t *testing.T) { + testWorker, _ := NewTimelockWorker(testNodeURL, testTimelockAddress, testCallProxyAddress, testPrivateKey, testFromBlock, int64(testPollPeriod), testLogger) + var ctx context.Context + + type args struct { + ctx context.Context + c *contract.Timelock + id [32]byte + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "isOperation: empty, should fail", + args: args{ + ctx: ctx, + c: testWorker.contract, + id: [32]byte{}, + }, + want: false, + }, + { + name: "isOperation: real operation, should succeed", + args: args{ + ctx: ctx, + c: testWorker.contract, + id: [32]byte{79, 143, 96, 15, 56, 41, 187, 178, 167, 120, 61, 145, 140, 241, 3, 220, 155, 151, 111, 184, 96, 128, 73, 10, 146, 173, 93, 211, 80, 225, 69, 183}, + }, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isDone(tt.args.ctx, tt.args.c, tt.args.id); got != tt.want { + t.Errorf("isDone() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_isPending(t *testing.T) { + testWorker, _ := NewTimelockWorker(testNodeURL, testTimelockAddress, testCallProxyAddress, testPrivateKey, testFromBlock, int64(testPollPeriod), testLogger) + var ctx context.Context + + type args struct { + ctx context.Context + c *contract.Timelock + id [32]byte + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "isOperation: empty, should fail", + args: args{ + ctx: ctx, + c: testWorker.contract, + id: [32]byte{}, + }, + want: false, + }, + { + name: "isOperation: real operation, should succeed", + args: args{ + ctx: ctx, + c: testWorker.contract, + id: [32]byte{79, 143, 96, 15, 56, 41, 187, 178, 167, 120, 61, 145, 140, 241, 3, 220, 155, 151, 111, 184, 96, 128, 73, 10, 146, 173, 93, 211, 80, 225, 69, 183}, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isPending(tt.args.ctx, tt.args.c, tt.args.id); got != tt.want { + t.Errorf("isPending() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_privateKeyToAddress(t *testing.T) { + type args struct { + privateKey *ecdsa.PrivateKey + } + tests := []struct { + name string + args args + want common.Address + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := privateKeyToAddress(tt.args.privateKey) + if (err != nil) != tt.wantErr { + t.Errorf("privateKeyToAddress() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("privateKeyToAddress() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/timelock/scheduler.go b/pkg/timelock/scheduler.go new file mode 100644 index 0000000..422b8e9 --- /dev/null +++ b/pkg/timelock/scheduler.go @@ -0,0 +1,174 @@ +package timelock + +import ( + "bufio" + "context" + "fmt" + "os" + "sort" + "sync" + "time" + + "github.com/smartcontractkit/timelock-worker/pkg/timelock/contract" +) + +type operationKey [32]byte + +// Scheduler represents a scheduler with an in memory store. +// Whenever accesing the map the mutex should be Locked, to prevent +// any race condition. +type scheduler struct { + mu sync.Mutex + ticker *time.Ticker + add chan *contract.TimelockCallScheduled + del chan operationKey + store map[operationKey][]*contract.TimelockCallScheduled + busy bool +} + +// newScheduler returns a new initialized scheduler. +func newScheduler(tick time.Duration) *scheduler { + s := &scheduler{ + ticker: time.NewTicker(tick), + add: make(chan *contract.TimelockCallScheduled), + del: make(chan operationKey), + store: make(map[operationKey][]*contract.TimelockCallScheduled), + busy: false, + } + + return s +} + +// runScheduler starts the scheduler. +// ticker.C will signal the scheduler every N seconds, where N is +// defined in the scheduler.ticker field +// add and del are the channels to manage the store, we +// call them this way so no process is allowd to add/delete from +// the store, which could cause race conditions like adding/deleting +// while the operation is being executed. +func (tw *Worker) runScheduler(ctx context.Context) { + for { + select { + case <-tw.ticker.C: + if len(tw.store) <= 0 { + tw.logger.Debug().Msgf("new scheduler tick: no operations in store") + continue + } + + if !tw.isSchedulerBusy() { + tw.logger.Debug().Msgf("new scheduler tick: operations in store") + tw.setSchedulerBusy() + for _, op := range tw.store { + tw.execute(ctx, op) + } + tw.setSchedulerFree() + } else { + tw.logger.Debug().Msgf("new scheduler tick: scheduler is busy, skipping until next tick") + } + + case op := <-tw.add: + tw.mu.Lock() + if len(tw.store[op.Id]) <= int(op.Index.Int64()) { + tw.store[op.Id] = append(tw.store[op.Id], op) + } + tw.store[op.Id][op.Index.Int64()] = op + tw.mu.Unlock() + tw.logger.Debug().Msgf("scheduled operation: %x", op.Id) + + case op := <-tw.del: + if _, ok := tw.store[op]; ok { + tw.mu.Lock() + delete(tw.store, op) + tw.mu.Unlock() + tw.logger.Debug().Msgf("de-scheduled operation: %x", op) + } + } + } +} + +// updateSchedulerDelay updates the internal ticker delay, so it can be reconfigured while running. +func (tw *Worker) updateSchedulerDelay(t time.Duration) { + if t > 0 { + tw.ticker.Reset(t) + tw.logger.Debug().Msgf("internal min delay changed to %v", t.String()) + } else { + tw.logger.Debug().Msgf("internal min delay not changed, invalid duration: %v", t.String()) + } +} + +// addToScheduler adds a new CallSchedule operation safely to the store. +func (tw *Worker) addToScheduler(op *contract.TimelockCallScheduled) { + tw.logger.Debug().Msgf("scheduling operation: %x", op.Id) + tw.add <- op + tw.logger.Debug().Msgf("operations in scheduler: %v", len(tw.store)) +} + +// delFromScheduler deletes an operation safely from the store. +func (tw *Worker) delFromScheduler(op operationKey) { + tw.logger.Debug().Msgf("de-scheduling operation: %v", op) + tw.del <- op + tw.logger.Debug().Msgf("operations in scheduler: %v", len(tw.store)) +} + +func (tw *Worker) setSchedulerBusy() { + tw.logger.Debug().Msgf("setting scheduler busy") + tw.mu.Lock() + tw.busy = true + tw.mu.Unlock() +} + +func (tw *Worker) setSchedulerFree() { + tw.logger.Debug().Msgf("setting scheduler free") + tw.mu.Lock() + tw.busy = false + tw.mu.Unlock() +} + +func (tw *Worker) isSchedulerBusy() bool { + return tw.busy +} + +// dumpOperationStore dumps to the logger and to the log file the current scheduled unexecuted operations. +// maps in go don't guarantee order, so that's why we have to find the earliest block. +func (tw *Worker) dumpOperationStore() { + if len(tw.store) > 0 { + f, err := os.Create(logPath + logFile) + if err != nil { + tw.logger.Fatal().Msgf("unable to create %s: %s", logPath+logFile, err.Error()) + } + defer f.Close() + + tw.logger.Info().Msgf("generating logs with pending operations in %s", logPath+logFile) + + w := bufio.NewWriter(f) + _, err = fmt.Fprintf(w, "Process stopped at %v\n", time.Now().In(time.UTC)) + if err != nil { + tw.logger.Fatal().Msgf("error writing to buffer: %s", err.Error()) + } + + // Get the earliest block from all the operations stored by sorting them. + blocks := make([]int, 0) + for _, op := range tw.store { + blocks = append(blocks, int(op[0].Raw.BlockNumber)) + } + sort.Ints(blocks) + + for _, op := range tw.store { + if int(op[0].Raw.BlockNumber) == blocks[0] { + tw.logger.Info().Hex(fieldTXHash, op[0].Raw.TxHash[:]).Uint64(fieldBlockNumber, op[0].Raw.BlockNumber).Msgf("earliest unexecuted CallSchedule. Use this block number when spinning up the service again, with the environment variable or in timelock.env as FROM_BLOCK=%v, or using the flag --from-block=%v", op[0].Raw.BlockNumber, op[0].Raw.BlockNumber) + _, err = fmt.Fprintf(w, "Earliest CallSchedule pending ID: %x\tBlock Number: %v\n\tUse this block number to ensure all pending operations are properly executed.\n\tSet it as environment variable or in timelock.env with FROM_BLOCK=%v, or as a flag with --from-block=%v\n", op[0].Id, op[0].Raw.BlockNumber, op[0].Raw.BlockNumber, op[0].Raw.BlockNumber) + if err != nil { + tw.logger.Fatal().Msgf("error writing to buffer: %s", err.Error()) + } + } else { + _, err = fmt.Fprintf(w, "CallSchedule pending ID: %x\tBlock Number: %v\n", op[0].Id, op[0].Raw.BlockNumber) + tw.logger.Info().Hex(fieldTXHash, op[0].Raw.TxHash[:]).Uint64(fieldBlockNumber, op[0].Raw.BlockNumber).Msgf("CallSchedule pending") + if err != nil { + tw.logger.Fatal().Msgf("error writing to buffer: %s", err.Error()) + } + } + } + + w.Flush() + } +} diff --git a/pkg/timelock/scheduler_test.go b/pkg/timelock/scheduler_test.go new file mode 100644 index 0000000..e5dfb2d --- /dev/null +++ b/pkg/timelock/scheduler_test.go @@ -0,0 +1,78 @@ +package timelock + +import ( + "reflect" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func Test_newScheduler(t *testing.T) { + tScheduler := newScheduler(10 * time.Second) + + type args struct { + tick time.Duration + } + tests := []struct { + name string + args args + want *scheduler + }{ + { + name: "New scheduler", + args: args{ + tick: 10 * time.Second, + }, + want: tScheduler, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := newScheduler(tt.args.tick) + if reflect.TypeOf(got) != reflect.TypeOf(tt.want) { + t.Errorf("newScheduler() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestWorker_updateSchedulerDelay(t *testing.T) { + testWorker, _ := NewTimelockWorker(testNodeURL, testTimelockAddress, testCallProxyAddress, testPrivateKey, testFromBlock, int64(testPollPeriod), testLogger) + + // Should never fail + testWorker.updateSchedulerDelay(1 * time.Second) + testWorker.updateSchedulerDelay(-1 * time.Second) + testWorker.updateSchedulerDelay(0 * time.Second) +} + +func TestWorker_isSchedulerBusy(t *testing.T) { + testWorker, _ := NewTimelockWorker(testNodeURL, testTimelockAddress, testCallProxyAddress, testPrivateKey, testFromBlock, int64(testPollPeriod), testLogger) + + isBusy := testWorker.isSchedulerBusy() + assert.Equal(t, false, isBusy, "scheduler should be busy by default") + + testWorker.setSchedulerBusy() + isBusy = testWorker.isSchedulerBusy() + assert.Equal(t, true, isBusy, "scheduler should be busy after setSchedulerBusy()") + + testWorker.setSchedulerFree() + isBusy = testWorker.isSchedulerBusy() + assert.Equal(t, false, isBusy, "scheduler shouldn't be busy after setSchedulerFree()") +} + +func TestWorker_setSchedulerBusy(t *testing.T) { + testWorker, _ := NewTimelockWorker(testNodeURL, testTimelockAddress, testCallProxyAddress, testPrivateKey, testFromBlock, int64(testPollPeriod), testLogger) + + testWorker.setSchedulerBusy() + isBusy := testWorker.isSchedulerBusy() + assert.Equal(t, true, isBusy, "scheduler should be busy after setSchedulerBusy()") +} + +func TestWorker_setSchedulerFree(t *testing.T) { + testWorker, _ := NewTimelockWorker(testNodeURL, testTimelockAddress, testCallProxyAddress, testPrivateKey, testFromBlock, int64(testPollPeriod), testLogger) + + testWorker.setSchedulerFree() + isBusy := testWorker.isSchedulerBusy() + assert.Equal(t, false, isBusy, "scheduler shouldn't be busy after setSchedulerFree()") +} diff --git a/pkg/timelock/timelock.go b/pkg/timelock/timelock.go new file mode 100644 index 0000000..940f937 --- /dev/null +++ b/pkg/timelock/timelock.go @@ -0,0 +1,286 @@ +package timelock + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "net/url" + "os" + "os/signal" + "sync" + "syscall" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" + "github.com/rs/zerolog" + "github.com/smartcontractkit/timelock-worker/pkg/timelock/contract" +) + +var ( + wg sync.WaitGroup + tWorker *Worker +) + +// Worker represents a worker instance. +// address is an array of addresses as expected by ethereum.FilterQuery, +// but it's enforced only to one address in the logic. +type Worker struct { + ethClient *ethclient.Client + contract *contract.Timelock + executeContract *contract.Timelock + ABI *abi.ABI + address []common.Address + fromBlock *big.Int + pollPeriod int64 + logger *zerolog.Logger + privateKey *ecdsa.PrivateKey + scheduler +} + +// NewTimelockWorker initializes and returns a timelockWorker. +// It's a singleton, so further executions will retrieve the same timelockWorker. +func NewTimelockWorker(nodeURL, timelockAddress, callProxyAddress, privateKey string, fromBlock *big.Int, pollPeriod int64, logger *zerolog.Logger) (*Worker, error) { + // Sanity check on each provided variable before allocating more resources. + u, err := url.ParseRequestURI(nodeURL) + if err != nil { + return nil, err + } + + if u.Scheme == "http" || u.Scheme == "https" { + return nil, fmt.Errorf("only ws or wss are valid options to suscribe to events: nodeURL using %s", u.Scheme) + } + + if !common.IsHexAddress(timelockAddress) { + return nil, fmt.Errorf("timelock address provided is not valid: %s", timelockAddress) + } + + if !common.IsHexAddress(callProxyAddress) { + return nil, fmt.Errorf("call proxy address provided is not valid: %s", callProxyAddress) + } + + if pollPeriod <= 0 { + return nil, fmt.Errorf("poll-period must be a positive non-zero integer: got %d", pollPeriod) + } + + if fromBlock.Int64() < big.NewInt(0).Int64() { + return nil, fmt.Errorf("from block can't be a negative number (minimum value 0): got %d", pollPeriod) + } + + if _, err := crypto.HexToECDSA(privateKey); err != nil { + return nil, fmt.Errorf("the provided private key is not valid: got %s", privateKey) + } + + // All variables provided are correct, start allocating new structures. + client, err := rpc.Dial(nodeURL) + if err != nil { + return nil, err + } + + ethClient := ethclient.NewClient(client) + + timelockABI, err := contract.TimelockMetaData.GetAbi() + if err != nil { + return nil, err + } + + // The contract ABI give grants capabilities such as parsing events and accessing to fields. + // As NewTimelock only accepts one contract, hardcode it to address[0]. + timelockContract, err := contract.NewTimelock(common.HexToAddress(timelockAddress), ethClient) + if err != nil { + return nil, err + } + + // The execute contract is the call proxy contract, which is the one that actually executes the transaction. + // It's not the same as the timelock contract, so it has to be initialized separately. + executeContract, err := contract.NewTimelock(common.HexToAddress(callProxyAddress), ethClient) + if err != nil { + return nil, err + } + + privateKeyECDSA, err := crypto.HexToECDSA(privateKey) + if err != nil { + return nil, err + } + + tWorker = &Worker{ + ethClient: ethClient, + contract: timelockContract, + executeContract: executeContract, + ABI: timelockABI, + address: []common.Address{common.HexToAddress(timelockAddress)}, + fromBlock: fromBlock, + pollPeriod: pollPeriod, + logger: logger, + privateKey: privateKeyECDSA, + scheduler: *newScheduler(defaultSchedulerDelay), + } + + return tWorker, nil +} + +// Listen is the main function of a Timelock Worker, it subscribes to events using the ethClient +// and targeting the contract address set. +func (tw *Worker) Listen(ctx context.Context) error { + if err := ctx.Err(); err != nil { + return err + } + + stopCh := make(chan string) + logCh := make(chan types.Log) + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) + + tw.updateSchedulerDelay(time.Duration(tw.pollPeriod) * time.Second) + go tw.runScheduler(ctx) + tw.startLog() + + // Shutdown gracefully based on OS interrupts. + go func() { + for { + handleOSSignal(<-sigCh, stopCh) + } + }() + + // FilterQuery to be feed to the subscription and FilterLogs. + query := ethereum.FilterQuery{ + Addresses: tw.address, + FromBlock: tw.fromBlock, + } + + // Create the new subscription with the predefined query. + sub, err := tw.ethClient.SubscribeFilterLogs(ctx, query, logCh) + if err != nil { + return err + } + + // Read events by FilterLogs. This method calls eth_getLogs under the hood. + filter, err := tw.ethClient.FilterLogs(ctx, query) + if err != nil { + return err + } + + go func() { + for _, l := range filter { + logCh <- l + } + }() + + // This is the goroutine watching over the subscription. + // We want wg.Done() to cancel the whole execution, so don't add more than 1 to wg. + // Also, when receiving an event that creates an error, skip the event and + // continue processing the rest, as an external operator can cancel the faulty event. + loop := true + wg.Add(1) + go func() { + for loop { + select { + case log := <-logCh: + // Decode the log into an event using the ABI exposed in Timelock.go + event, err := tw.ABI.EventByID(log.Topics[0]) + if err != nil { + continue + } + + if event == nil { + continue + } + + switch event.Name { + // A CallScheduled event should be added to an scheduler only if it's not already done + // and it's a valid Operation. + case eventCallScheduled: + cs, err := tw.contract.ParseCallScheduled(log) + if err != nil { + continue + } + + if !isDone(ctx, tw.contract, cs.Id) && isOperation(ctx, tw.contract, cs.Id) { + tw.logger.Info().Hex(fieldTXHash, cs.Raw.TxHash[:]).Uint64(fieldBlockNumber, cs.Raw.BlockNumber).Msgf("%s received", eventCallScheduled) + tw.addToScheduler(cs) + } + + // A CallExecuted which is in Done status should delete the task in the scheduler store. + case eventCallExecuted: + cs, err := tw.contract.ParseCallExecuted(log) + if err != nil { + continue + } + + if isDone(ctx, tw.contract, cs.Id) { + tw.logger.Info().Hex(fieldTXHash, cs.Raw.TxHash[:]).Uint64(fieldBlockNumber, cs.Raw.BlockNumber).Msgf("%s received, skipping operation", eventCallExecuted) + tw.delFromScheduler(cs.Id) + } + + // A Cancelled which is in Done status should delete the task in the scheduler store. + case eventCancelled: + cs, err := tw.contract.ParseCancelled(log) + if err != nil { + continue + } + + if isDone(ctx, tw.contract, cs.Id) { + tw.logger.Info().Hex(fieldTXHash, cs.Raw.TxHash[:]).Uint64(fieldBlockNumber, cs.Raw.BlockNumber).Msgf("%s received, cancelling operation", eventCancelled) + tw.delFromScheduler(cs.Id) + } + } + + case err := <-sub.Err(): + // Check if the error is not nil, because sub.Unsubscribe will + // signal the channel sub.Err() to close it, leading to false nil errors. + if err != nil { + tw.logger.Info().Msgf("subscription: %s", err.Error()) + loop = false + } + + case signal := <-stopCh: + tw.logger.Info().Msgf("received OS signal %s", signal) + loop = false + } + } + wg.Done() + }() + wg.Wait() + + // Close in this specific order to avoid runtime panics, + // or memory leaks. + defer close(sigCh) + defer close(stopCh) + defer close(logCh) + defer sub.Unsubscribe() + defer ctx.Done() + + tw.dumpOperationStore() + + return nil +} + +// handleOSSignal handles SIGINT and SIGTERM OS signals, and signals the stopCh. +func handleOSSignal(signal os.Signal, stopCh chan string) { + switch signal { + case syscall.SIGINT: + stopCh <- syscall.SIGINT.String() + case syscall.SIGTERM: + stopCh <- syscall.SIGTERM.String() + } +} + +func (tw *Worker) startLog() { + tw.logger.Info().Msgf("timelock-worker started") + tw.logger.Info().Msgf("\tTimelock contract address: %v", tw.address[0]) + + wallet, err := privateKeyToAddress(tw.privateKey) + if err != nil { + tw.logger.Info().Msgf("\tEOA address: unable to determine") + } + tw.logger.Info().Msgf("\tEOA address: %v", wallet) + + tw.logger.Info().Msgf("\tStarting from block: %v", tw.fromBlock) + tw.logger.Info().Msgf("\tPoll Period: %v", time.Duration(tw.pollPeriod*int64(time.Second)).String()) +} diff --git a/pkg/timelock/timelock_test.go b/pkg/timelock/timelock_test.go new file mode 100644 index 0000000..7856c41 --- /dev/null +++ b/pkg/timelock/timelock_test.go @@ -0,0 +1,193 @@ +package timelock + +import ( + "math/big" + "os" + "reflect" + "sync" + "syscall" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" +) + +func TestNewTimelockWorker(t *testing.T) { + testWorker, _ := NewTimelockWorker(testNodeURL, testTimelockAddress, testCallProxyAddress, testPrivateKey, testFromBlock, int64(testPollPeriod), testLogger) + + type args struct { + nodeURL string + timelockAddress string + callProxyAddress string + privateKey string + fromBlock *big.Int + pollPeriod int64 + logger *zerolog.Logger + } + tests := []struct { + name string + args args + want *Worker + wantErr bool + }{ + { + name: "NewTimelockWorker new instance is created (success)", + args: args{ + nodeURL: testNodeURL, + timelockAddress: testTimelockAddress, + callProxyAddress: testCallProxyAddress, + privateKey: testPrivateKey, + fromBlock: testFromBlock, + pollPeriod: int64(testPollPeriod), + logger: testLogger, + }, + want: testWorker, + wantErr: false, + }, + { + name: "NewTimelockWorker bad rpc provided (fail)", + args: args{ + nodeURL: "wss://bad/rpc", + timelockAddress: testTimelockAddress, + callProxyAddress: testCallProxyAddress, + privateKey: testPrivateKey, + fromBlock: testFromBlock, + pollPeriod: int64(testPollPeriod), + logger: testLogger, + }, + want: testWorker, + wantErr: true, + }, + { + name: "NewTimelockWorker bad rpc protocol provided (fail)", + args: args{ + nodeURL: "https://bad/protocol", + timelockAddress: testTimelockAddress, + callProxyAddress: testCallProxyAddress, + privateKey: testPrivateKey, + fromBlock: testFromBlock, + pollPeriod: int64(testPollPeriod), + logger: testLogger, + }, + want: testWorker, + wantErr: true, + }, + { + name: "NewTimelockWorker bad timelock address provided (fail)", + args: args{ + nodeURL: testNodeURL, + timelockAddress: "0x1234", + callProxyAddress: testCallProxyAddress, + privateKey: testPrivateKey, + fromBlock: testFromBlock, + pollPeriod: int64(testPollPeriod), + logger: testLogger, + }, + want: testWorker, + wantErr: true, + }, + { + name: "NewTimelockWorker bad call proxy address provided (fail)", + args: args{ + nodeURL: testNodeURL, + timelockAddress: testTimelockAddress, + callProxyAddress: "0x1234", + privateKey: testPrivateKey, + fromBlock: testFromBlock, + pollPeriod: int64(testPollPeriod), + logger: testLogger, + }, + want: testWorker, + wantErr: true, + }, + { + name: "NewTimelockWorker bad private key provided (fail)", + args: args{ + nodeURL: testNodeURL, + timelockAddress: testTimelockAddress, + callProxyAddress: testCallProxyAddress, + privateKey: "0123456789", + fromBlock: testFromBlock, + pollPeriod: int64(testPollPeriod), + logger: testLogger, + }, + want: testWorker, + wantErr: true, + }, + { + name: "NewTimelockWorker bad negative from block provided (fail)", + args: args{ + nodeURL: testNodeURL, + timelockAddress: testTimelockAddress, + callProxyAddress: testCallProxyAddress, + privateKey: testPrivateKey, + fromBlock: big.NewInt(-1), + pollPeriod: int64(testPollPeriod), + logger: testLogger, + }, + want: testWorker, + wantErr: true, + }, + { + name: "NewTimelockWorker bad poll period provided (fail)", + args: args{ + nodeURL: testNodeURL, + timelockAddress: testTimelockAddress, + callProxyAddress: testCallProxyAddress, + privateKey: testPrivateKey, + fromBlock: testFromBlock, + pollPeriod: 0, + logger: testLogger, + }, + want: testWorker, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewTimelockWorker(tt.args.nodeURL, tt.args.timelockAddress, tt.args.callProxyAddress, tt.args.privateKey, tt.args.fromBlock, tt.args.pollPeriod, tt.args.logger) + if (err != nil) != tt.wantErr { + t.Errorf("NewTimelockWorker() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !(reflect.TypeOf(tt.want) == reflect.TypeOf(got)) { + t.Errorf("NewTimelockWorker() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_handleOSSignal(t *testing.T) { + stopCh := make(chan string) + sigCh := make(chan os.Signal, 1) + var testWg sync.WaitGroup + + testWg.Add(1) + go func() { + for { + handleOSSignal(<-sigCh, stopCh) + } + }() + + sigCh <- syscall.SIGTERM + assert.Equal(t, <-stopCh, "terminated", "send SIGTERM, receive terminated") + + sigCh <- syscall.SIGINT + assert.Equal(t, <-stopCh, "interrupt", "send SIGINT, receive interrupt") +} + +func TestWorker_startLog(t *testing.T) { + testWorker, _ := NewTimelockWorker(testNodeURL, testTimelockAddress, testCallProxyAddress, testPrivateKey, testFromBlock, int64(testPollPeriod), testLogger) + tests := []struct { + name string + }{ + { + "Show logs", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testWorker.startLog() + }) + } +} diff --git a/scripts/golangci.yml b/scripts/golangci.yml new file mode 100644 index 0000000..9edbce9 --- /dev/null +++ b/scripts/golangci.yml @@ -0,0 +1,52 @@ +run: + timeout: 10m + tests: false + modules-download-mode: readonly +output: + format: html +linters: + disable-all: true + enable: + - asasalint + - bidichk + - bodyclose + - containedctx + - contextcheck + - dogsled + - dupl + - errcheck + - errname + - errorlint + - exhaustive + - exportloopref + - gochecknoinits + - godot + - gofmt + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - makezero + - misspell + - nakedret + - nilerr + - nilnil + - noctx + - nolintlint + - predeclared + - promlinter + - staticcheck + - tenv + - testpackage + - thelper + - tparallel + - typecheck + - unconvert + - unparam + - whitespace + issues: + max-issues-per-linter: 0 + max-same-issues: 0 + severity: + default-severity: error diff --git a/scripts/golangci_html_report.sh b/scripts/golangci_html_report.sh new file mode 100755 index 0000000..8928796 --- /dev/null +++ b/scripts/golangci_html_report.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -x +TMP_FILE="scripts/report.html" +golangci-lint run -c scripts/golangci.yml > ${TMP_FILE} +open ${TMP_FILE} diff --git a/timelock.env b/timelock.env new file mode 100644 index 0000000..e0489e9 --- /dev/null +++ b/timelock.env @@ -0,0 +1,6 @@ +TIMELOCK_ADDRESS=0x1234... +CALL_PROXY_ADDRESS=0x5678... +PRIVATE_KEY=1234a5678b... +NODE_URL=wss://mywss/foo/bar +POLL_PERIOD=900 +FROM_BLOCK=0