diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..863f937c --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +**/.DS_Store +.vscode +.env +/dist/ +cmd/__debug_bin +**__debug** \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index 77bc1aa8..a0a1caef 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -16,32 +16,32 @@ linters: disable-all: true enable: - whitespace # Tool for detection of leading and trailing whitespace - - wsl # Forces you to use empty lines + # - wsl # Forces you to use empty lines - wastedassign # Finds wasted assignment statements - unconvert # Unnecessary type conversions - tparallel # Detects inappropriate usage of t.Parallel() method in your Go test codes - thelper # Detects golang test helpers without t.Helper() call and checks the consistency of test helpers - - stylecheck # Stylecheck is a replacement for golint - - prealloc # Finds slice declarations that could potentially be pre-allocated + # - stylecheck # Stylecheck is a replacement for golint + # - prealloc # Finds slice declarations that could potentially be pre-allocated - predeclared # Finds code that shadows one of Go's predeclared identifiers - - nolintlint # Ill-formed or insufficient nolint directives - - nlreturn # Checks for a new line before return and branch statements to increase code clarity + # - nolintlint # Ill-formed or insufficient nolint directives + # - nlreturn # Checks for a new line before return and branch statements to increase code clarity - misspell # Misspelled English words in comments - makezero # Finds slice declarations with non-zero initial length - - lll # Long lines + # - lll # Long lines - importas # Enforces consistent import aliases - gosec # Security problems - gofmt # Whether the code was gofmt-ed - goimports # Unused imports - goconst # Repeated strings that could be replaced by a constant - - forcetypeassert # Finds forced type assertions + # - forcetypeassert # Finds forced type assertions - dogsled # Checks assignments with too many blank identifiers (e.g. x, , , _, := f()) - - dupl # Code clone detection + # - dupl # Code clone detection - errname # Checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error - - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13 - - gocritic + # - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13 + # - gocritic - errcheck # Errcheck is a go lint rule for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases - - godox # Godox is a linter for TODOs and FIXMEs left in the code + # - godox # Godox is a linter for TODOs and FIXMEs left in the code linters-settings: gofmt: diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..19aef335 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +# CONTAINER FOR BUILDING BINARY +FROM golang:1.22.4 AS build + +# INSTALL DEPENDENCIES +RUN go install github.com/gobuffalo/packr/v2/packr2@v2.8.3 +COPY go.mod go.sum /src/ +RUN cd /src && go mod download + +# BUILD BINARY +COPY . /src +RUN cd /src/aggregator/db && packr2 +RUN cd /src && make build + +# CONTAINER FOR RUNNING BINARY +FROM alpine:3.18.4 +COPY --from=build /src/dist/cdk /app/cdk +RUN apk update && apk add postgresql15-client +EXPOSE 8123 +CMD ["/bin/sh", "-c", "/app/cdk run"] diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..89d32b1c --- /dev/null +++ b/Makefile @@ -0,0 +1,116 @@ +include version.mk + +ARCH := $(shell arch) + +ifeq ($(ARCH),x86_64) + ARCH = amd64 +else + ifeq ($(ARCH),aarch64) + ARCH = arm64 + endif +endif +GOBASE := $(shell pwd) +GOBIN := $(GOBASE)/dist +GOENVVARS := GOBIN=$(GOBIN) CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) +GOBINARY := cdk +GOCMD := $(GOBASE)/cmd + +LDFLAGS += -X 'github.com/0xPolygon/cdk.Version=$(VERSION)' +LDFLAGS += -X 'github.com/0xPolygon/cdk.GitRev=$(GITREV)' +LDFLAGS += -X 'github.com/0xPolygon/cdk.GitBranch=$(GITBRANCH)' +LDFLAGS += -X 'github.com/0xPolygon/cdk.BuildDate=$(DATE)' + +# Variables +VENV = .venv +VENV_PYTHON = $(VENV)/bin/python +SYSTEM_PYTHON = $(or $(shell which python3), $(shell which python)) +PYTHON = $(or $(wildcard $(VENV_PYTHON)), "install_first_venv") +GENERATE_SCHEMA_DOC = $(VENV)/bin/generate-schema-doc +GENERATE_DOC_PATH = "docs/config-file/" +GENERATE_DOC_TEMPLATES_PATH = "docs/config-file/templates/" + +# Check dependencies +# Check for Go +.PHONY: check-go +check-go: + @which go > /dev/null || (echo "Error: Go is not installed" && exit 1) + +# Check for Docker +.PHONY: check-docker +check-docker: + @which docker > /dev/null || (echo "Error: docker is not installed" && exit 1) + +# Check for Docker-compose +.PHONY: check-docker-compose +check-docker-compose: + @which docker-compose > /dev/null || (echo "Error: docker-compose is not installed" && exit 1) + +# Check for Protoc +.PHONY: check-protoc +check-protoc: + @which protoc > /dev/null || (echo "Error: Protoc is not installed" && exit 1) + +# Check for Curl +.PHONY: check-curl +check-curl: + @which curl > /dev/null || (echo "Error: curl is not installed" && exit 1) + +# Targets that require the checks +build: check-go +lint: check-go +build-docker: check-docker +build-docker-nc: check-docker +stop: check-docker check-docker-compose +install-linter: check-go check-curl +generate-code-from-proto: check-protoc + +.PHONY: build +build: ## Builds the binary locally into ./dist + $(GOENVVARS) go build -ldflags "all=$(LDFLAGS)" -o $(GOBIN)/$(GOBINARY) $(GOCMD) + +.PHONY: build-docker +build-docker: ## Builds a docker image with the cdk binary + docker build -t cdk -f ./Dockerfile . + +.PHONY: build-docker-nc +build-docker-nc: ## Builds a docker image with the cdk binary - but without build cache + docker build --no-cache=true -t cdk -f ./Dockerfile . + +.PHONY: stop +stop: ## Stops all services + docker-compose down + +.PHONY: test +test: + go test -count=1 -short -race -p 1 -timeout 60s ./... + +.PHONY: install-linter +install-linter: ## Installs the linter + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $$(go env GOPATH)/bin v1.54.2 + +.PHONY: lint +lint: ## Runs the linter + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/golangci-lint run + +$(VENV_PYTHON): + rm -rf $(VENV) + $(SYSTEM_PYTHON) -m venv $(VENV) + +venv: $(VENV_PYTHON) + +.PHONY: generate-code-from-proto +generate-code-from-proto: ## Generates code from proto files + cd proto/src/proto/aggregator/v1 && protoc --proto_path=. --proto_path=../../../../include --go_out=../../../../../aggregator/prover --go-grpc_out=../../../../../aggregator/prover --go-grpc_opt=paths=source_relative --go_opt=paths=source_relative aggregator.proto + cd proto/src/proto/datastream/v1 && protoc --proto_path=. --proto_path=../../../../include --go_out=../../../../../state/datastream --go-grpc_out=../../../../../state/datastream --go-grpc_opt=paths=source_relative --go_opt=paths=source_relative datastream.proto + + +## Help display. +## Pulls comments from beside commands and prints a nicely formatted +## display with the commands and their usage information. +.DEFAULT_GOAL := help + +.PHONY: help +help: ## Prints this help + @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \ + | sort \ + | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/aggregator/agglayer_client.go b/aggregator/agglayer_client.go new file mode 100644 index 00000000..4726ccc1 --- /dev/null +++ b/aggregator/agglayer_client.go @@ -0,0 +1,81 @@ +package aggregator + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "github.com/0xPolygon/cdk-rpc/rpc" + "github.com/0xPolygon/cdk-rpc/types" + "github.com/ethereum/go-ethereum/common" +) + +// AgglayerClientInterface is the interface that defines the methods that the AggLayerClient will implement +type AgglayerClientInterface interface { + SendTx(signedTx SignedTx) (common.Hash, error) + WaitTxToBeMined(hash common.Hash, ctx context.Context) error +} + +// AggLayerClient is the client that will be used to interact with the AggLayer +type AggLayerClient struct { + url string +} + +// NewAggLayerClient returns a client ready to be used +func NewAggLayerClient(url string) *AggLayerClient { + return &AggLayerClient{ + url: url, + } +} + +// SendTx sends a signed transaction to the AggLayer +func (c *AggLayerClient) SendTx(signedTx SignedTx) (common.Hash, error) { + response, err := rpc.JSONRPCCall(c.url, "interop_sendTx", signedTx) + if err != nil { + return common.Hash{}, err + } + + if response.Error != nil { + return common.Hash{}, fmt.Errorf("%v %v", response.Error.Code, response.Error.Message) + } + + var result types.ArgHash + err = json.Unmarshal(response.Result, &result) + if err != nil { + return common.Hash{}, err + } + + return result.Hash(), nil +} + +// WaitTxToBeMined waits for a transaction to be mined +func (c *AggLayerClient) WaitTxToBeMined(hash common.Hash, ctx context.Context) error { + ticker := time.NewTicker(time.Second) + for { + select { + case <-ctx.Done(): + return errors.New("context finished before tx was mined") + case <-ticker.C: + response, err := rpc.JSONRPCCall(c.url, "interop_getTxStatus", hash) + if err != nil { + return err + } + + if response.Error != nil { + return fmt.Errorf("%v %v", response.Error.Code, response.Error.Message) + } + + var result string + err = json.Unmarshal(response.Result, &result) + if err != nil { + return err + } + if strings.ToLower(result) == "done" { + return nil + } + } + } +} diff --git a/aggregator/agglayer_tx.go b/aggregator/agglayer_tx.go new file mode 100644 index 00000000..b0cd09c9 --- /dev/null +++ b/aggregator/agglayer_tx.go @@ -0,0 +1,63 @@ +package aggregator + +import ( + "crypto/ecdsa" + + "github.com/0xPolygon/cdk-rpc/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +// ZKP is the struct that contains the zero-knowledge proof +type ZKP struct { + NewStateRoot common.Hash `json:"newStateRoot"` + NewLocalExitRoot common.Hash `json:"newLocalExitRoot"` + Proof types.ArgBytes `json:"proof"` +} + +// Tx is the struct that contains the verified batch transaction +type Tx struct { + RollupID uint32 + LastVerifiedBatch types.ArgUint64 `json:"lastVerifiedBatch"` + NewVerifiedBatch types.ArgUint64 `json:"newVerifiedBatch"` + ZKP ZKP `json:"ZKP"` +} + +// Hash returns a hash that uniquely identifies the tx +func (t *Tx) Hash() common.Hash { + return common.BytesToHash(crypto.Keccak256( + []byte(t.LastVerifiedBatch.Hex()), + []byte(t.NewVerifiedBatch.Hex()), + t.ZKP.NewStateRoot[:], + t.ZKP.NewLocalExitRoot[:], + []byte(t.ZKP.Proof.Hex()), + )) +} + +// Sign returns a signed batch by the private key +func (t *Tx) Sign(privateKey *ecdsa.PrivateKey) (*SignedTx, error) { + hashToSign := t.Hash() + sig, err := crypto.Sign(hashToSign.Bytes(), privateKey) + if err != nil { + return nil, err + } + return &SignedTx{ + Tx: *t, + Signature: sig, + }, nil +} + +// SignedTx is the struct that contains the signed batch transaction +type SignedTx struct { + Tx Tx `json:"tx"` + Signature types.ArgBytes `json:"signature"` +} + +// Signer returns the address of the signer +func (s *SignedTx) Signer() (common.Address, error) { + pubKey, err := crypto.SigToPub(s.Tx.Hash().Bytes(), s.Signature) + if err != nil { + return common.Address{}, err + } + return crypto.PubkeyToAddress(*pubKey), nil +} diff --git a/aggregator/aggregator.go b/aggregator/aggregator.go new file mode 100644 index 00000000..02db11fd --- /dev/null +++ b/aggregator/aggregator.go @@ -0,0 +1,1722 @@ +package aggregator + +import ( + "bytes" + "context" + "crypto/ecdsa" + "encoding/json" + "errors" + "fmt" + "math/big" + "net" + "strings" + "sync" + "sync/atomic" + "time" + "unicode" + + "github.com/0xPolygon/cdk-rpc/rpc" + cdkTypes "github.com/0xPolygon/cdk-rpc/types" + ethmanTypes "github.com/0xPolygon/cdk/aggregator/ethmantypes" + "github.com/0xPolygon/cdk/aggregator/prover" + "github.com/0xPolygon/cdk/config/types" + "github.com/0xPolygon/cdk/l1infotree" + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/state" + "github.com/0xPolygon/cdk/state/datastream" + "github.com/0xPolygonHermez/zkevm-data-streamer/datastreamer" + streamlog "github.com/0xPolygonHermez/zkevm-data-streamer/log" + "github.com/0xPolygonHermez/zkevm-ethtx-manager/ethtxmanager" + ethtxlog "github.com/0xPolygonHermez/zkevm-ethtx-manager/log" + synclog "github.com/0xPolygonHermez/zkevm-synchronizer-l1/log" + "github.com/0xPolygonHermez/zkevm-synchronizer-l1/state/entities" + "github.com/0xPolygonHermez/zkevm-synchronizer-l1/synchronizer" + "github.com/ethereum/go-ethereum/common" + "github.com/iden3/go-iden3-crypto/keccak256" + "google.golang.org/grpc" + grpchealth "google.golang.org/grpc/health/grpc_health_v1" + "google.golang.org/grpc/peer" + "google.golang.org/protobuf/proto" +) + +const ( + dataStreamType = 1 + mockedStateRoot = "0x090bcaf734c4f06c93954a827b45a6e8c67b8e0fd1e0a35a1c5982d6961828f9" + mockedLocalExitRoot = "0x17c04c3760510b48c6012742c540a81aba4bca2f78b9d14bfd2f123e2e53ea3e" +) + +type finalProofMsg struct { + proverName string + proverID string + recursiveProof *state.Proof + finalProof *prover.FinalProof +} + +// Aggregator represents an aggregator +type Aggregator struct { + prover.UnimplementedAggregatorServiceServer + + cfg Config + + state stateInterface + etherman etherman + ethTxManager *ethtxmanager.Client + streamClient *datastreamer.StreamClient + l1Syncr synchronizer.Synchronizer + halted atomic.Bool + + profitabilityChecker aggregatorTxProfitabilityChecker + timeSendFinalProof time.Time + timeCleanupLockedProofs types.Duration + stateDBMutex *sync.Mutex + timeSendFinalProofMutex *sync.RWMutex + + // Data stream handling variables + currentBatchStreamData []byte + currentStreamBatch state.Batch + currentStreamBatchRaw state.BatchRawV2 + currentStreamL2Block state.L2BlockRaw + + finalProof chan finalProofMsg + verifyingProof bool + + srv *grpc.Server + ctx context.Context + exit context.CancelFunc + + sequencerPrivateKey *ecdsa.PrivateKey + aggLayerClient AgglayerClientInterface +} + +// New creates a new aggregator. +func New( + ctx context.Context, + cfg Config, + stateInterface stateInterface, + etherman etherman) (*Aggregator, error) { + var profitabilityChecker aggregatorTxProfitabilityChecker + + switch cfg.TxProfitabilityCheckerType { + case ProfitabilityBase: + profitabilityChecker = NewTxProfitabilityCheckerBase(stateInterface, cfg.IntervalAfterWhichBatchConsolidateAnyway.Duration, cfg.TxProfitabilityMinReward.Int) + case ProfitabilityAcceptAll: + profitabilityChecker = NewTxProfitabilityCheckerAcceptAll(stateInterface, cfg.IntervalAfterWhichBatchConsolidateAnyway.Duration) + } + + // Create ethtxmanager client + cfg.EthTxManager.Log = ethtxlog.Config{ + Environment: ethtxlog.LogEnvironment(cfg.Log.Environment), + Level: cfg.Log.Level, + Outputs: cfg.Log.Outputs, + } + ethTxManager, err := ethtxmanager.New(cfg.EthTxManager) + if err != nil { + log.Fatalf("error creating ethtxmanager client: %v", err) + } + + // Data stream client logs + streamLogConfig := streamlog.Config{ + Environment: streamlog.LogEnvironment(cfg.Log.Environment), + Level: cfg.Log.Level, + Outputs: cfg.Log.Outputs, + } + + log.Init(cfg.Log) + + log.Info("Creating data stream client....") + streamClient, err := datastreamer.NewClientWithLogsConfig(cfg.StreamClient.Server, dataStreamType, streamLogConfig) + if err != nil { + log.Fatalf("failed to create stream client, error: %v", err) + } + log.Info("Data stream client created.") + + // Synchonizer logs + syncLogConfig := synclog.Config{ + Environment: synclog.LogEnvironment(cfg.Log.Environment), + Level: cfg.Log.Level, + Outputs: cfg.Log.Outputs, + } + + cfg.Synchronizer.Log = syncLogConfig + + // Create L1 synchronizer client + cfg.Synchronizer.Etherman.L1URL = cfg.EthTxManager.Etherman.URL + log.Debugf("Creating synchronizer client with config: %+v", cfg.Synchronizer) + l1Syncr, err := synchronizer.NewSynchronizer(ctx, cfg.Synchronizer) + if err != nil { + log.Fatalf("failed to create synchronizer client, error: %v", err) + } + + var ( + aggLayerClient AgglayerClientInterface + sequencerPrivateKey *ecdsa.PrivateKey + ) + + if cfg.SettlementBackend == AggLayer { + aggLayerClient = NewAggLayerClient(cfg.AggLayerURL) + + sequencerPrivateKey, err = newKeyFromKeystore(cfg.SequencerPrivateKey) + if err != nil { + return nil, err + } + } + + a := &Aggregator{ + cfg: cfg, + state: stateInterface, + etherman: etherman, + ethTxManager: ethTxManager, + streamClient: streamClient, + l1Syncr: l1Syncr, + profitabilityChecker: profitabilityChecker, + stateDBMutex: &sync.Mutex{}, + timeSendFinalProofMutex: &sync.RWMutex{}, + timeCleanupLockedProofs: cfg.CleanupLockedProofsInterval, + finalProof: make(chan finalProofMsg), + currentBatchStreamData: []byte{}, + aggLayerClient: aggLayerClient, + sequencerPrivateKey: sequencerPrivateKey, + } + + // Set function to handle the batches from the data stream + a.streamClient.SetProcessEntryFunc(a.handleReceivedDataStream) + a.l1Syncr.SetCallbackOnReorgDone(a.handleReorg) + + return a, nil +} + +func (a *Aggregator) handleReorg(reorgData synchronizer.ReorgExecutionResult) { + log.Warnf("Reorg detected, reorgData: %+v", reorgData) + + ctx := context.Background() + + // Get new latest verified batch number + lastVBatchNumber, err := a.l1Syncr.GetLastestVirtualBatchNumber(ctx) + if err != nil { + log.Errorf("Error getting last virtual batch number: %v", err) + } else { + err = a.state.DeleteBatchesNewerThanBatchNumber(ctx, lastVBatchNumber, nil) + if err != nil { + log.Errorf("Error deleting batches newer than batch number %d: %v", lastVBatchNumber, err) + } + } + + // Halt the aggregator + a.halted.Store(true) + for { + log.Warnf("Halting the aggregator due to a L1 reorg. Reorged data has been delete so it is safe to manually restart the aggregator.") + time.Sleep(10 * time.Second) // nolint:gomnd + } +} + +func (a *Aggregator) handleReceivedDataStream(entry *datastreamer.FileEntry, client *datastreamer.StreamClient, server *datastreamer.StreamServer) error { + ctx := context.Background() + forcedBlockhashL1 := common.Hash{} + + if !a.halted.Load() { + if entry.Type != datastreamer.EntryType(datastreamer.EtBookmark) { + a.currentBatchStreamData = append(a.currentBatchStreamData, entry.Encode()...) + + switch entry.Type { + case datastreamer.EntryType(datastream.EntryType_ENTRY_TYPE_BATCH_START): + batch := &datastream.BatchStart{} + err := proto.Unmarshal(entry.Data, batch) + if err != nil { + log.Errorf("Error unmarshalling batch: %v", err) + return err + } + + a.currentStreamBatch.BatchNumber = batch.Number + a.currentStreamBatch.ChainID = batch.ChainId + a.currentStreamBatch.ForkID = batch.ForkId + a.currentStreamBatch.Type = batch.Type + case datastreamer.EntryType(datastream.EntryType_ENTRY_TYPE_BATCH_END): + batch := &datastream.BatchEnd{} + err := proto.Unmarshal(entry.Data, batch) + if err != nil { + log.Errorf("Error unmarshalling batch: %v", err) + return err + } + + a.currentStreamBatch.LocalExitRoot = common.BytesToHash(batch.LocalExitRoot) + a.currentStreamBatch.StateRoot = common.BytesToHash(batch.StateRoot) + + // Add last block (if any) to the current batch + if a.currentStreamL2Block.BlockNumber != 0 { + a.currentStreamBatchRaw.Blocks = append(a.currentStreamBatchRaw.Blocks, a.currentStreamL2Block) + } + + // Save Current Batch + if a.currentStreamBatch.BatchNumber != 0 { + var batchl2Data []byte + + // Get batchl2Data from L1 + virtualBatch, err := a.l1Syncr.GetVirtualBatchByBatchNumber(ctx, a.currentStreamBatch.BatchNumber) + if err != nil && !errors.Is(err, entities.ErrNotFound) { + log.Errorf("Error getting virtual batch: %v", err) + return err + } + + for errors.Is(err, entities.ErrNotFound) { + log.Debug("Waiting for virtual batch to be available") + time.Sleep(a.cfg.RetryTime.Duration) + virtualBatch, err = a.l1Syncr.GetVirtualBatchByBatchNumber(ctx, a.currentStreamBatch.BatchNumber) + + if err != nil && !errors.Is(err, entities.ErrNotFound) { + log.Errorf("Error getting virtual batch: %v", err) + return err + } + } + + // Encode batch + if a.currentStreamBatch.Type != datastream.BatchType_BATCH_TYPE_INVALID { + batchl2Data, err = state.EncodeBatchV2(&a.currentStreamBatchRaw) + if err != nil { + log.Errorf("Error encoding batch: %v", err) + return err + } + } + + // If the batch is marked as Invalid in the DS we enforce retrieve the data from L1 + if a.cfg.UseL1BatchData || a.currentStreamBatch.Type == datastream.BatchType_BATCH_TYPE_INVALID { + a.currentStreamBatch.BatchL2Data = virtualBatch.BatchL2Data + } else { + a.currentStreamBatch.BatchL2Data = batchl2Data + } + + // Compare BatchL2Data from L1 and DataStream + if common.Bytes2Hex(batchl2Data) != common.Bytes2Hex(virtualBatch.BatchL2Data) { + log.Warnf("BatchL2Data from L1 and data stream are different for batch %d", a.currentStreamBatch.BatchNumber) + + if a.currentStreamBatch.Type == datastream.BatchType_BATCH_TYPE_INVALID { + log.Warnf("Batch is marked as invalid in data stream") + } else { + log.Warnf("DataStream BatchL2Data:%v", common.Bytes2Hex(batchl2Data)) + } + log.Warnf("L1 BatchL2Data:%v", common.Bytes2Hex(virtualBatch.BatchL2Data)) + } + + // Ger L1InfoRoot + sequence, err := a.l1Syncr.GetSequenceByBatchNumber(ctx, a.currentStreamBatch.BatchNumber) + if err != nil { + log.Errorf("Error getting sequence: %v", err) + return err + } + + for sequence == nil { + log.Debug("Waiting for sequence to be available") + time.Sleep(a.cfg.RetryTime.Duration) + sequence, err = a.l1Syncr.GetSequenceByBatchNumber(ctx, a.currentStreamBatch.BatchNumber) + if err != nil { + log.Errorf("Error getting sequence: %v", err) + return err + } + } + + a.currentStreamBatch.L1InfoRoot = sequence.L1InfoRoot + a.currentStreamBatch.Timestamp = sequence.Timestamp + + // Calculate Acc Input Hash + oldBatch, _, err := a.state.GetBatch(ctx, a.currentStreamBatch.BatchNumber-1, nil) + if err != nil { + log.Errorf("Error getting batch %d: %v", a.currentStreamBatch.BatchNumber-1, err) + return err + } + + // Injected Batch + if a.currentStreamBatch.BatchNumber == 1 { + l1Block, err := a.l1Syncr.GetL1BlockByNumber(ctx, virtualBatch.BlockNumber) + if err != nil { + log.Errorf("Error getting L1 block: %v", err) + return err + } + + forcedBlockhashL1 = l1Block.ParentHash + a.currentStreamBatch.L1InfoRoot = a.currentStreamBatch.GlobalExitRoot + } + + accInputHash, err := calculateAccInputHash(oldBatch.AccInputHash, a.currentStreamBatch.BatchL2Data, a.currentStreamBatch.L1InfoRoot, uint64(a.currentStreamBatch.Timestamp.Unix()), a.currentStreamBatch.Coinbase, forcedBlockhashL1) + if err != nil { + log.Errorf("Error calculating acc input hash: %v", err) + return err + } + + a.currentStreamBatch.AccInputHash = accInputHash + + err = a.state.AddBatch(ctx, &a.currentStreamBatch, a.currentBatchStreamData, nil) + if err != nil { + log.Errorf("Error adding batch: %v", err) + return err + } + } + + // Reset current batch data + a.currentBatchStreamData = []byte{} + a.currentStreamBatchRaw = state.BatchRawV2{ + Blocks: make([]state.L2BlockRaw, 0), + } + a.currentStreamL2Block = state.L2BlockRaw{} + + case datastreamer.EntryType(datastream.EntryType_ENTRY_TYPE_L2_BLOCK): + // Add previous block (if any) to the current batch + if a.currentStreamL2Block.BlockNumber != 0 { + a.currentStreamBatchRaw.Blocks = append(a.currentStreamBatchRaw.Blocks, a.currentStreamL2Block) + } + // "Open" the new block + l2Block := &datastream.L2Block{} + err := proto.Unmarshal(entry.Data, l2Block) + if err != nil { + log.Errorf("Error unmarshalling L2Block: %v", err) + return err + } + + header := state.ChangeL2BlockHeader{ + DeltaTimestamp: l2Block.DeltaTimestamp, + IndexL1InfoTree: l2Block.L1InfotreeIndex, + } + + a.currentStreamL2Block.ChangeL2BlockHeader = header + a.currentStreamL2Block.Transactions = make([]state.L2TxRaw, 0) + a.currentStreamL2Block.BlockNumber = l2Block.Number + a.currentStreamBatch.L1InfoTreeIndex = l2Block.L1InfotreeIndex + a.currentStreamBatch.Coinbase = common.BytesToAddress(l2Block.Coinbase) + a.currentStreamBatch.GlobalExitRoot = common.BytesToHash(l2Block.GlobalExitRoot) + + case datastreamer.EntryType(datastream.EntryType_ENTRY_TYPE_TRANSACTION): + l2Tx := &datastream.Transaction{} + err := proto.Unmarshal(entry.Data, l2Tx) + if err != nil { + log.Errorf("Error unmarshalling L2Tx: %v", err) + return err + } + // New Tx raw + tx, err := state.DecodeTx(common.Bytes2Hex(l2Tx.Encoded)) + if err != nil { + log.Errorf("Error decoding tx: %v", err) + return err + } + + l2TxRaw := state.L2TxRaw{ + EfficiencyPercentage: uint8(l2Tx.EffectiveGasPricePercentage), + TxAlreadyEncoded: false, + Tx: tx, + } + a.currentStreamL2Block.Transactions = append(a.currentStreamL2Block.Transactions, l2TxRaw) + } + } + } + return nil +} + +// Start starts the aggregator +func (a *Aggregator) Start(ctx context.Context) error { + var cancel context.CancelFunc + if ctx == nil { + ctx = context.Background() + } + ctx, cancel = context.WithCancel(ctx) + a.ctx = ctx + a.exit = cancel + + address := fmt.Sprintf("%s:%d", a.cfg.Host, a.cfg.Port) + lis, err := net.Listen("tcp", address) + if err != nil { + log.Fatalf("Failed to listen: %v", err) + } + + a.srv = grpc.NewServer() + prover.RegisterAggregatorServiceServer(a.srv, a) + + healthService := newHealthChecker() + grpchealth.RegisterHealthServer(a.srv, healthService) + + // Initial L1 Sync blocking + err = a.l1Syncr.Sync(true) + if err != nil { + log.Fatalf("Failed to synchronize from L1: %v", err) + return err + } + + // Get last verified batch number to set the starting point for verifications + lastVerifiedBatchNumber, err := a.etherman.GetLatestVerifiedBatchNum() + if err != nil { + return err + } + + // Cleanup data base + err = a.state.DeleteBatchesOlderThanBatchNumber(ctx, lastVerifiedBatchNumber, nil) + if err != nil { + return err + } + + // Delete ungenerated recursive proofs + err = a.state.DeleteUngeneratedProofs(ctx, nil) + if err != nil { + return fmt.Errorf("failed to initialize proofs cache %w", err) + } + + accInputHash, err := a.getVerifiedBatchAccInputHash(ctx, lastVerifiedBatchNumber) + if err != nil { + return err + } + + log.Infof("Last Verified Batch Number:%v", lastVerifiedBatchNumber) + log.Infof("Starting AccInputHash:%v", accInputHash.String()) + + // Store Acc Input Hash of the latest verified batch + dummyBatch := state.Batch{BatchNumber: lastVerifiedBatchNumber, AccInputHash: *accInputHash} + err = a.state.AddBatch(ctx, &dummyBatch, []byte{0}, nil) + if err != nil { + return err + } + + a.resetVerifyProofTime() + + go a.cleanupLockedProofs() + go a.sendFinalProof() + go a.ethTxManager.Start() + + // Keep syncing L1 + go func() { + err := a.l1Syncr.Sync(false) + if err != nil { + log.Fatalf("Failed to synchronize from L1: %v", err) + } + }() + + // Start stream client + err = a.streamClient.Start() + if err != nil { + log.Fatalf("failed to start stream client, error: %v", err) + } + + bookMark := &datastream.BookMark{ + Type: datastream.BookmarkType_BOOKMARK_TYPE_BATCH, + Value: lastVerifiedBatchNumber + 1, + } + + marshalledBookMark, err := proto.Marshal(bookMark) + if err != nil { + log.Fatalf("failed to marshal bookmark: %v", err) + } + + err = a.streamClient.ExecCommandStartBookmark(marshalledBookMark) + if err != nil { + log.Fatalf("failed to connect to data stream: %v", err) + } + + // A this point everything is ready, so start serving + go func() { + log.Infof("Server listening on port %d", a.cfg.Port) + if err := a.srv.Serve(lis); err != nil { + a.exit() + log.Fatalf("Failed to serve: %v", err) + } + }() + + <-ctx.Done() + return ctx.Err() +} + +// Stop stops the Aggregator server. +func (a *Aggregator) Stop() { + a.exit() + a.srv.Stop() +} + +// Channel implements the bi-directional communication channel between the +// Prover client and the Aggregator server. +func (a *Aggregator) Channel(stream prover.AggregatorService_ChannelServer) error { + ctx := stream.Context() + var proverAddr net.Addr + p, ok := peer.FromContext(ctx) + if ok { + proverAddr = p.Addr + } + prover, err := prover.New(stream, proverAddr, a.cfg.ProofStatePollingInterval) + if err != nil { + return err + } + + log := log.WithFields( + "prover", prover.Name(), + "proverId", prover.ID(), + "proverAddr", prover.Addr(), + ) + log.Info("Establishing stream connection with prover") + + // Check if prover supports the required Fork ID + if !prover.SupportsForkID(a.cfg.ForkId) { + err := errors.New("prover does not support required fork ID") + log.Warn(FirstToUpper(err.Error())) + return err + } + + for { + select { + case <-a.ctx.Done(): + // server disconnected + return a.ctx.Err() + case <-ctx.Done(): + // client disconnected + return ctx.Err() + + default: + if !a.halted.Load() { + isIdle, err := prover.IsIdle() + if err != nil { + log.Errorf("Failed to check if prover is idle: %v", err) + time.Sleep(a.cfg.RetryTime.Duration) + continue + } + if !isIdle { + log.Debug("Prover is not idle") + time.Sleep(a.cfg.RetryTime.Duration) + continue + } + + _, err = a.tryBuildFinalProof(ctx, prover, nil) + if err != nil { + log.Errorf("Error checking proofs to verify: %v", err) + } + + proofGenerated, err := a.tryAggregateProofs(ctx, prover) + if err != nil { + log.Errorf("Error trying to aggregate proofs: %v", err) + } + + if !proofGenerated { + proofGenerated, err = a.tryGenerateBatchProof(ctx, prover) + if err != nil { + log.Errorf("Error trying to generate proof: %v", err) + } + } + if !proofGenerated { + // if no proof was generated (aggregated or batch) wait some time before retry + time.Sleep(a.cfg.RetryTime.Duration) + } // if proof was generated we retry immediately as probably we have more proofs to process + } + } + } +} + +// This function waits to receive a final proof from a prover. Once it receives +// the proof, it performs these steps in order: +// - send the final proof to L1 +// - wait for the synchronizer to catch up +// - clean up the cache of recursive proofs +func (a *Aggregator) sendFinalProof() { + for { + select { + case <-a.ctx.Done(): + return + case msg := <-a.finalProof: + ctx := a.ctx + proof := msg.recursiveProof + + log.WithFields("proofId", proof.ProofID, "batches", fmt.Sprintf("%d-%d", proof.BatchNumber, proof.BatchNumberFinal)) + log.Info("Verifying final proof with ethereum smart contract") + + a.startProofVerification() + + finalBatch, _, err := a.state.GetBatch(ctx, proof.BatchNumberFinal, nil) + if err != nil { + log.Errorf("Failed to retrieve batch with number [%d]: %v", proof.BatchNumberFinal, err) + a.endProofVerification() + continue + } + + inputs := ethmanTypes.FinalProofInputs{ + FinalProof: msg.finalProof, + NewLocalExitRoot: finalBatch.LocalExitRoot.Bytes(), + NewStateRoot: finalBatch.StateRoot.Bytes(), + } + + switch a.cfg.SettlementBackend { + case AggLayer: + if success := a.settleWithAggLayer(ctx, proof, inputs); !success { + continue + } + default: + if success := a.settleDirect(ctx, proof, inputs); !success { + continue + } + } + + a.resetVerifyProofTime() + a.endProofVerification() + } + } +} + +func (a *Aggregator) settleWithAggLayer( + ctx context.Context, + proof *state.Proof, + inputs ethmanTypes.FinalProofInputs) bool { + proofStrNo0x := strings.TrimPrefix(inputs.FinalProof.Proof, "0x") + proofBytes := common.Hex2Bytes(proofStrNo0x) + tx := Tx{ + LastVerifiedBatch: cdkTypes.ArgUint64(proof.BatchNumber - 1), + NewVerifiedBatch: cdkTypes.ArgUint64(proof.BatchNumberFinal), + ZKP: ZKP{ + NewStateRoot: common.BytesToHash(inputs.NewStateRoot), + NewLocalExitRoot: common.BytesToHash(inputs.NewLocalExitRoot), + Proof: cdkTypes.ArgBytes(proofBytes), + }, + RollupID: a.etherman.GetRollupId(), + } + signedTx, err := tx.Sign(a.sequencerPrivateKey) + if err != nil { + log.Errorf("failed to sign tx: %v", err) + a.handleFailureToAddVerifyBatchToBeMonitored(ctx, proof) + + return false + } + + log.Debug("final proof signedTx: ", signedTx.Tx.ZKP.Proof.Hex()) + txHash, err := a.aggLayerClient.SendTx(*signedTx) + if err != nil { + log.Errorf("failed to send tx to the agglayer: %v", err) + a.handleFailureToAddVerifyBatchToBeMonitored(ctx, proof) + + return false + } + + log.Infof("tx %s sent to agglayer, waiting to be mined", txHash.Hex()) + log.Debugf("Timeout set to %f seconds", a.cfg.AggLayerTxTimeout.Duration.Seconds()) + waitCtx, cancelFunc := context.WithDeadline(ctx, time.Now().Add(a.cfg.AggLayerTxTimeout.Duration)) + defer cancelFunc() + if err := a.aggLayerClient.WaitTxToBeMined(txHash, waitCtx); err != nil { + log.Errorf("agglayer didn't mine the tx: %v", err) + a.handleFailureToAddVerifyBatchToBeMonitored(ctx, proof) + + return false + } + + return true +} + +// settleDirect sends the final proof to the L1 smart contract directly. +func (a *Aggregator) settleDirect( + ctx context.Context, + proof *state.Proof, + inputs ethmanTypes.FinalProofInputs) bool { + // add batch verification to be monitored + sender := common.HexToAddress(a.cfg.SenderAddress) + to, data, err := a.etherman.BuildTrustedVerifyBatchesTxData(proof.BatchNumber-1, proof.BatchNumberFinal, &inputs, sender) + if err != nil { + log.Errorf("Error estimating batch verification to add to eth tx manager: %v", err) + a.handleFailureToAddVerifyBatchToBeMonitored(ctx, proof) + return false + } + + monitoredTxID, err := a.ethTxManager.Add(ctx, to, nil, big.NewInt(0), data, a.cfg.GasOffset, nil) + if err != nil { + log.Errorf("Error Adding TX to ethTxManager: %v", err) + mTxLogger := ethtxmanager.CreateLogger(monitoredTxID, sender, to) + mTxLogger.Errorf("Error to add batch verification tx to eth tx manager: %v", err) + a.handleFailureToAddVerifyBatchToBeMonitored(ctx, proof) + return false + } + + // process monitored batch verifications before starting a next cycle + a.ethTxManager.ProcessPendingMonitoredTxs(ctx, func(result ethtxmanager.MonitoredTxResult) { + a.handleMonitoredTxResult(result) + }) + + return true +} + +func (a *Aggregator) handleFailureToAddVerifyBatchToBeMonitored(ctx context.Context, proof *state.Proof) { + log := log.WithFields("proofId", proof.ProofID, "batches", fmt.Sprintf("%d-%d", proof.BatchNumber, proof.BatchNumberFinal)) + proof.GeneratingSince = nil + err := a.state.UpdateGeneratedProof(ctx, proof, nil) + if err != nil { + log.Errorf("Failed updating proof state (false): %v", err) + } + a.endProofVerification() +} + +// buildFinalProof builds and return the final proof for an aggregated/batch proof. +func (a *Aggregator) buildFinalProof(ctx context.Context, prover proverInterface, proof *state.Proof) (*prover.FinalProof, error) { + log := log.WithFields( + "prover", prover.Name(), + "proverId", prover.ID(), + "proverAddr", prover.Addr(), + "recursiveProofId", *proof.ProofID, + "batches", fmt.Sprintf("%d-%d", proof.BatchNumber, proof.BatchNumberFinal), + ) + + finalProofID, err := prover.FinalProof(proof.Proof, a.cfg.SenderAddress) + if err != nil { + return nil, fmt.Errorf("failed to get final proof id: %w", err) + } + proof.ProofID = finalProofID + + log.Infof("Final proof ID for batches [%d-%d]: %s", proof.BatchNumber, proof.BatchNumberFinal, *proof.ProofID) + log = log.WithFields("finalProofId", finalProofID) + + finalProof, err := prover.WaitFinalProof(ctx, *proof.ProofID) + if err != nil { + return nil, fmt.Errorf("failed to get final proof from prover: %w", err) + } + + // mock prover sanity check + if string(finalProof.Public.NewStateRoot) == mockedStateRoot && string(finalProof.Public.NewLocalExitRoot) == mockedLocalExitRoot { + // This local exit root and state root come from the mock + // prover, use the one captured by the executor instead + finalBatch, _, err := a.state.GetBatch(ctx, proof.BatchNumberFinal, nil) + if err != nil { + return nil, fmt.Errorf("failed to retrieve batch with number [%d]", proof.BatchNumberFinal) + } + log.Warnf("NewLocalExitRoot and NewStateRoot look like a mock values, using values from executor instead: LER: %v, SR: %v", + finalBatch.LocalExitRoot.TerminalString(), finalBatch.StateRoot.TerminalString()) + finalProof.Public.NewStateRoot = finalBatch.StateRoot.Bytes() + finalProof.Public.NewLocalExitRoot = finalBatch.LocalExitRoot.Bytes() + } + + // Sanity Check: state root from the proof must match the one from the final batch + finalBatch, _, err := a.state.GetBatch(ctx, proof.BatchNumberFinal, nil) + if err != nil { + return nil, fmt.Errorf("failed to retrieve batch with number [%d]", proof.BatchNumberFinal) + } + + if !bytes.Equal(finalProof.Public.NewStateRoot, finalBatch.StateRoot.Bytes()) { + for { + log.Errorf("State root from the proof [%#x] does not match the one from the batch [%#x]. HALTED", finalProof.Public.NewStateRoot, finalBatch.StateRoot.Bytes()) + time.Sleep(a.cfg.RetryTime.Duration) + } + } + + return finalProof, nil +} + +// tryBuildFinalProof checks if the provided proof is eligible to be used to +// build the final proof. If no proof is provided it looks for a previously +// generated proof. If the proof is eligible, then the final proof generation +// is triggered. +func (a *Aggregator) tryBuildFinalProof(ctx context.Context, prover proverInterface, proof *state.Proof) (bool, error) { + proverName := prover.Name() + proverID := prover.ID() + + log := log.WithFields( + "prover", proverName, + "proverId", proverID, + "proverAddr", prover.Addr(), + ) + log.Debug("tryBuildFinalProof start") + + var err error + if !a.canVerifyProof() { + log.Debug("Time to verify proof not reached or proof verification in progress") + return false, nil + } + log.Debug("Send final proof time reached") + + lastVerifiedBatchNumber, err := a.etherman.GetLatestVerifiedBatchNum() + if err != nil { + return false, err + } + + if proof == nil { + // we don't have a proof generating at the moment, check if we + // have a proof ready to verify + + proof, err = a.getAndLockProofReadyToVerify(ctx, prover, lastVerifiedBatchNumber) + if errors.Is(err, state.ErrNotFound) { + // nothing to verify, swallow the error + log.Debug("No proof ready to verify") + return false, nil + } + if err != nil { + return false, err + } + + defer func() { + if err != nil { + // Set the generating state to false for the proof ("unlock" it) + proof.GeneratingSince = nil + err2 := a.state.UpdateGeneratedProof(a.ctx, proof, nil) + if err2 != nil { + log.Errorf("Failed to unlock proof: %v", err2) + } + } + }() + } else { + // we do have a proof generating at the moment, check if it is + // eligible to be verified + eligible, err := a.validateEligibleFinalProof(ctx, proof, lastVerifiedBatchNumber) + if err != nil { + return false, fmt.Errorf("failed to validate eligible final proof, %w", err) + } + if !eligible { + return false, nil + } + } + + log = log.WithFields( + "proofId", *proof.ProofID, + "batches", fmt.Sprintf("%d-%d", proof.BatchNumber, proof.BatchNumberFinal), + ) + + // at this point we have an eligible proof, build the final one using it + finalProof, err := a.buildFinalProof(ctx, prover, proof) + if err != nil { + err = fmt.Errorf("failed to build final proof, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + + msg := finalProofMsg{ + proverName: proverName, + proverID: proverID, + recursiveProof: proof, + finalProof: finalProof, + } + + select { + case <-a.ctx.Done(): + return false, a.ctx.Err() + case a.finalProof <- msg: + } + + log.Debug("tryBuildFinalProof end") + return true, nil +} + +func (a *Aggregator) validateEligibleFinalProof(ctx context.Context, proof *state.Proof, lastVerifiedBatchNum uint64) (bool, error) { + batchNumberToVerify := lastVerifiedBatchNum + 1 + + if proof.BatchNumber != batchNumberToVerify { + if proof.BatchNumber < batchNumberToVerify && proof.BatchNumberFinal >= batchNumberToVerify { + // We have a proof that contains some batches below the last batch verified, anyway can be eligible as final proof + log.Warnf("Proof %d-%d contains some batches lower than last batch verified %d. Check anyway if it is eligible", proof.BatchNumber, proof.BatchNumberFinal, lastVerifiedBatchNum) + } else if proof.BatchNumberFinal < batchNumberToVerify { + // We have a proof that contains batches below that the last batch verified, we need to delete this proof + log.Warnf("Proof %d-%d lower than next batch to verify %d. Deleting it", proof.BatchNumber, proof.BatchNumberFinal, batchNumberToVerify) + err := a.state.DeleteGeneratedProofs(ctx, proof.BatchNumber, proof.BatchNumberFinal, nil) + if err != nil { + return false, fmt.Errorf("failed to delete discarded proof, err: %w", err) + } + return false, nil + } else { + log.Debugf("Proof batch number %d is not the following to last verfied batch number %d", proof.BatchNumber, lastVerifiedBatchNum) + return false, nil + } + } + + bComplete, err := a.state.CheckProofContainsCompleteSequences(ctx, proof, nil) + if err != nil { + return false, fmt.Errorf("failed to check if proof contains complete sequences, %w", err) + } + if !bComplete { + log.Infof("Recursive proof %d-%d not eligible to be verified: not containing complete sequences", proof.BatchNumber, proof.BatchNumberFinal) + return false, nil + } + return true, nil +} + +func (a *Aggregator) getAndLockProofReadyToVerify(ctx context.Context, prover proverInterface, lastVerifiedBatchNum uint64) (*state.Proof, error) { + a.stateDBMutex.Lock() + defer a.stateDBMutex.Unlock() + + // Get proof ready to be verified + proofToVerify, err := a.state.GetProofReadyToVerify(ctx, lastVerifiedBatchNum, nil) + if err != nil { + return nil, err + } + + now := time.Now().Round(time.Microsecond) + proofToVerify.GeneratingSince = &now + + err = a.state.UpdateGeneratedProof(ctx, proofToVerify, nil) + if err != nil { + return nil, err + } + + return proofToVerify, nil +} + +func (a *Aggregator) unlockProofsToAggregate(ctx context.Context, proof1 *state.Proof, proof2 *state.Proof) error { + // Release proofs from generating state in a single transaction + dbTx, err := a.state.BeginStateTransaction(ctx) + if err != nil { + log.Warnf("Failed to begin transaction to release proof aggregation state, err: %v", err) + return err + } + + proof1.GeneratingSince = nil + err = a.state.UpdateGeneratedProof(ctx, proof1, dbTx) + if err == nil { + proof2.GeneratingSince = nil + err = a.state.UpdateGeneratedProof(ctx, proof2, dbTx) + } + + if err != nil { + if err := dbTx.Rollback(ctx); err != nil { + err := fmt.Errorf("failed to rollback proof aggregation state: %w", err) + log.Error(FirstToUpper(err.Error())) + return err + } + return fmt.Errorf("failed to release proof aggregation state: %w", err) + } + + err = dbTx.Commit(ctx) + if err != nil { + return fmt.Errorf("failed to release proof aggregation state %w", err) + } + + return nil +} + +func (a *Aggregator) getAndLockProofsToAggregate(ctx context.Context, prover proverInterface) (*state.Proof, *state.Proof, error) { + log := log.WithFields( + "prover", prover.Name(), + "proverId", prover.ID(), + "proverAddr", prover.Addr(), + ) + + a.stateDBMutex.Lock() + defer a.stateDBMutex.Unlock() + + proof1, proof2, err := a.state.GetProofsToAggregate(ctx, nil) + if err != nil { + return nil, nil, err + } + + // Set proofs in generating state in a single transaction + dbTx, err := a.state.BeginStateTransaction(ctx) + if err != nil { + log.Errorf("Failed to begin transaction to set proof aggregation state, err: %v", err) + return nil, nil, err + } + + now := time.Now().Round(time.Microsecond) + proof1.GeneratingSince = &now + err = a.state.UpdateGeneratedProof(ctx, proof1, dbTx) + if err == nil { + proof2.GeneratingSince = &now + err = a.state.UpdateGeneratedProof(ctx, proof2, dbTx) + } + + if err != nil { + if err := dbTx.Rollback(ctx); err != nil { + err := fmt.Errorf("failed to rollback proof aggregation state %w", err) + log.Error(FirstToUpper(err.Error())) + return nil, nil, err + } + return nil, nil, fmt.Errorf("failed to set proof aggregation state %w", err) + } + + err = dbTx.Commit(ctx) + if err != nil { + return nil, nil, fmt.Errorf("failed to set proof aggregation state %w", err) + } + + return proof1, proof2, nil +} + +func (a *Aggregator) tryAggregateProofs(ctx context.Context, prover proverInterface) (bool, error) { + proverName := prover.Name() + proverID := prover.ID() + + log := log.WithFields( + "prover", proverName, + "proverId", proverID, + "proverAddr", prover.Addr(), + ) + log.Debug("tryAggregateProofs start") + + proof1, proof2, err0 := a.getAndLockProofsToAggregate(ctx, prover) + if errors.Is(err0, state.ErrNotFound) { + // nothing to aggregate, swallow the error + log.Debug("Nothing to aggregate") + return false, nil + } + if err0 != nil { + return false, err0 + } + + var ( + aggrProofID *string + err error + ) + + defer func() { + if err != nil { + err2 := a.unlockProofsToAggregate(a.ctx, proof1, proof2) + if err2 != nil { + log.Errorf("Failed to release aggregated proofs, err: %v", err2) + } + } + log.Debug("tryAggregateProofs end") + }() + + log.Infof("Aggregating proofs: %d-%d and %d-%d", proof1.BatchNumber, proof1.BatchNumberFinal, proof2.BatchNumber, proof2.BatchNumberFinal) + + batches := fmt.Sprintf("%d-%d", proof1.BatchNumber, proof2.BatchNumberFinal) + log = log.WithFields("batches", batches) + + inputProver := map[string]interface{}{ + "recursive_proof_1": proof1.Proof, + "recursive_proof_2": proof2.Proof, + } + b, err := json.Marshal(inputProver) + if err != nil { + err = fmt.Errorf("failed to serialize input prover, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + + proof := &state.Proof{ + BatchNumber: proof1.BatchNumber, + BatchNumberFinal: proof2.BatchNumberFinal, + Prover: &proverName, + ProverID: &proverID, + InputProver: string(b), + } + + aggrProofID, err = prover.AggregatedProof(proof1.Proof, proof2.Proof) + if err != nil { + err = fmt.Errorf("failed to get aggregated proof id, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + + proof.ProofID = aggrProofID + + log.Infof("Proof ID for aggregated proof: %v", *proof.ProofID) + log = log.WithFields("proofId", *proof.ProofID) + + recursiveProof, err := prover.WaitRecursiveProof(ctx, *proof.ProofID) + if err != nil { + err = fmt.Errorf("failed to get aggregated proof from prover, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + + log.Info("Aggregated proof generated") + + proof.Proof = recursiveProof + + // update the state by removing the 2 aggregated proofs and storing the + // newly generated recursive proof + dbTx, err := a.state.BeginStateTransaction(ctx) + if err != nil { + err = fmt.Errorf("failed to begin transaction to update proof aggregation state, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + + err = a.state.DeleteGeneratedProofs(ctx, proof1.BatchNumber, proof2.BatchNumberFinal, dbTx) + if err != nil { + if err := dbTx.Rollback(ctx); err != nil { + err := fmt.Errorf("failed to rollback proof aggregation state, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + err = fmt.Errorf("failed to delete previously aggregated proofs, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + + now := time.Now().Round(time.Microsecond) + proof.GeneratingSince = &now + + err = a.state.AddGeneratedProof(ctx, proof, dbTx) + if err != nil { + if err := dbTx.Rollback(ctx); err != nil { + err := fmt.Errorf("failed to rollback proof aggregation state, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + err = fmt.Errorf("failed to store the recursive proof, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + + err = dbTx.Commit(ctx) + if err != nil { + err = fmt.Errorf("failed to store the recursive proof, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + + // NOTE(pg): the defer func is useless from now on, use a different variable + // name for errors (or shadow err in inner scopes) to not trigger it. + + // state is up to date, check if we can send the final proof using the + // one just crafted. + finalProofBuilt, finalProofErr := a.tryBuildFinalProof(ctx, prover, proof) + if finalProofErr != nil { + // just log the error and continue to handle the aggregated proof + log.Errorf("Failed trying to check if recursive proof can be verified: %v", finalProofErr) + } + + // NOTE(pg): prover is done, use a.ctx from now on + + if !finalProofBuilt { + proof.GeneratingSince = nil + + // final proof has not been generated, update the recursive proof + err := a.state.UpdateGeneratedProof(a.ctx, proof, nil) + if err != nil { + err = fmt.Errorf("failed to store batch proof result, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + } + + return true, nil +} + +func (a *Aggregator) getVerifiedBatchAccInputHash(ctx context.Context, batchNumber uint64) (*common.Hash, error) { + accInputHash, err := a.etherman.GetBatchAccInputHash(ctx, batchNumber) + if err != nil { + return nil, err + } + + return &accInputHash, nil +} + +func (a *Aggregator) getAndLockBatchToProve(ctx context.Context, prover proverInterface) (*state.Batch, *state.Proof, error) { + proverID := prover.ID() + proverName := prover.Name() + + log := log.WithFields( + "prover", proverName, + "proverId", proverID, + "proverAddr", prover.Addr(), + ) + + a.stateDBMutex.Lock() + defer a.stateDBMutex.Unlock() + + // Get last virtual batch number from L1 + lastVerifiedBatchNumber, err := a.etherman.GetLatestVerifiedBatchNum() + if err != nil { + return nil, nil, err + } + + proofExists := true + batchNumberToVerify := lastVerifiedBatchNumber + + // Look for the batch number to verify + for proofExists { + batchNumberToVerify++ + proofExists, err = a.state.CheckProofExistsForBatch(ctx, batchNumberToVerify, nil) + if err != nil { + log.Infof("Error checking proof exists for batch %d", batchNumberToVerify) + return nil, nil, err + } + } + + // Check if the batch has been sequenced + sequence, err := a.l1Syncr.GetSequenceByBatchNumber(ctx, batchNumberToVerify) + if err != nil && !errors.Is(err, entities.ErrNotFound) { + return nil, nil, err + } + + // Not found, so it it not possible to verify the batch yet + if sequence == nil || errors.Is(err, entities.ErrNotFound) { + log.Infof("No sequence found for batch %d", batchNumberToVerify) + return nil, nil, state.ErrNotFound + } + + stateSequence := state.Sequence{ + FromBatchNumber: sequence.FromBatchNumber, + ToBatchNumber: sequence.ToBatchNumber, + } + + err = a.state.AddSequence(ctx, stateSequence, nil) + if err != nil { + log.Infof("Error storing sequence for batch %d", batchNumberToVerify) + return nil, nil, err + } + + batch, _, err := a.state.GetBatch(ctx, batchNumberToVerify, nil) + if err != nil { + return batch, nil, err + } + + // All the data required to generate a proof is ready + log.Infof("Found virtual batch %d pending to generate proof", batch.BatchNumber) + log = log.WithFields("batch", batch.BatchNumber) + + log.Info("Checking profitability to aggregate batch") + + // pass pol collateral as zero here, bcs in smart contract fee for aggregator is not defined yet + isProfitable, err := a.profitabilityChecker.IsProfitable(ctx, big.NewInt(0)) + if err != nil { + log.Errorf("Failed to check aggregator profitability, err: %v", err) + return nil, nil, err + } + + if !isProfitable { + log.Infof("Batch is not profitable, pol collateral %d", big.NewInt(0)) + return nil, nil, err + } + + now := time.Now().Round(time.Microsecond) + proof := &state.Proof{ + BatchNumber: batch.BatchNumber, + BatchNumberFinal: batch.BatchNumber, + Prover: &proverName, + ProverID: &proverID, + GeneratingSince: &now, + } + + // Avoid other prover to process the same batch + err = a.state.AddGeneratedProof(ctx, proof, nil) + if err != nil { + log.Errorf("Failed to add batch proof, err: %v", err) + return nil, nil, err + } + + return batch, proof, nil +} + +func (a *Aggregator) tryGenerateBatchProof(ctx context.Context, prover proverInterface) (bool, error) { + log := log.WithFields( + "prover", prover.Name(), + "proverId", prover.ID(), + "proverAddr", prover.Addr(), + ) + log.Debug("tryGenerateBatchProof start") + + batchToProve, proof, err0 := a.getAndLockBatchToProve(ctx, prover) + if errors.Is(err0, state.ErrNotFound) { + // nothing to proof, swallow the error + log.Debug("Nothing to generate proof") + return false, nil + } + if err0 != nil { + return false, err0 + } + + log = log.WithFields("batch", batchToProve.BatchNumber) + + var ( + genProofID *string + err error + ) + + defer func() { + if err != nil { + log.Debug("Deleting proof in progress") + err2 := a.state.DeleteGeneratedProofs(a.ctx, proof.BatchNumber, proof.BatchNumberFinal, nil) + if err2 != nil { + log.Errorf("Failed to delete proof in progress, err: %v", err2) + } + } + log.Debug("tryGenerateBatchProof end") + }() + + log.Infof("Sending zki + batch to the prover, batchNumber [%d]", batchToProve.BatchNumber) + inputProver, err := a.buildInputProver(ctx, batchToProve) + if err != nil { + err = fmt.Errorf("failed to build input prover, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + + log.Infof("Sending a batch to the prover. OldAccInputHash [%#x], L1InfoRoot [%#x]", + inputProver.PublicInputs.OldAccInputHash, inputProver.PublicInputs.L1InfoRoot) + + genProofID, err = prover.BatchProof(inputProver) + if err != nil { + err = fmt.Errorf("failed to get batch proof id, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + + proof.ProofID = genProofID + + log = log.WithFields("proofId", *proof.ProofID) + + resGetProof, err := prover.WaitRecursiveProof(ctx, *proof.ProofID) + if err != nil { + err = fmt.Errorf("failed to get proof from prover, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + + log.Info("Batch proof generated") + + proof.Proof = resGetProof + + // NOTE(pg): the defer func is useless from now on, use a different variable + // name for errors (or shadow err in inner scopes) to not trigger it. + + finalProofBuilt, finalProofErr := a.tryBuildFinalProof(ctx, prover, proof) + if finalProofErr != nil { + // just log the error and continue to handle the generated proof + log.Errorf("Error trying to build final proof: %v", finalProofErr) + } + + // NOTE(pg): prover is done, use a.ctx from now on + + if !finalProofBuilt { + proof.GeneratingSince = nil + + // final proof has not been generated, update the batch proof + err := a.state.UpdateGeneratedProof(a.ctx, proof, nil) + if err != nil { + err = fmt.Errorf("failed to store batch proof result, %w", err) + log.Error(FirstToUpper(err.Error())) + return false, err + } + } + + return true, nil +} + +// canVerifyProof returns true if we have reached the timeout to verify a proof +// and no other prover is verifying a proof (verifyingProof = false). +func (a *Aggregator) canVerifyProof() bool { + a.timeSendFinalProofMutex.RLock() + defer a.timeSendFinalProofMutex.RUnlock() + return a.timeSendFinalProof.Before(time.Now()) && !a.verifyingProof +} + +// startProofVerification sets to true the verifyingProof variable to indicate that there is a proof verification in progress +func (a *Aggregator) startProofVerification() { + a.timeSendFinalProofMutex.Lock() + defer a.timeSendFinalProofMutex.Unlock() + a.verifyingProof = true +} + +// endProofVerification set verifyingProof to false to indicate that there is not proof verification in progress +func (a *Aggregator) endProofVerification() { + a.timeSendFinalProofMutex.Lock() + defer a.timeSendFinalProofMutex.Unlock() + a.verifyingProof = false +} + +// resetVerifyProofTime updates the timeout to verify a proof. +func (a *Aggregator) resetVerifyProofTime() { + a.timeSendFinalProofMutex.Lock() + defer a.timeSendFinalProofMutex.Unlock() + a.timeSendFinalProof = time.Now().Add(a.cfg.VerifyProofInterval.Duration) +} + +func (a *Aggregator) buildInputProver(ctx context.Context, batchToVerify *state.Batch) (*prover.StatelessInputProver, error) { + isForcedBatch := false + batchRawData := &state.BatchRawV2{} + var err error + + if batchToVerify.BatchNumber == 1 || batchToVerify.ForcedBatchNum != nil { + isForcedBatch = true + } else { + batchRawData, err = state.DecodeBatchV2(batchToVerify.BatchL2Data) + if err != nil { + log.Errorf("Failed to decode batch data, err: %v", err) + return nil, err + } + } + + l1InfoTreeData := map[uint32]*prover.L1Data{} + forcedBlockhashL1 := common.Hash{} + l1InfoRoot := batchToVerify.L1InfoRoot.Bytes() + if !isForcedBatch { + tree, err := l1infotree.NewL1InfoTree(32, [][32]byte{}) // nolint:gomnd + if err != nil { + return nil, err + } + + leaves, err := a.l1Syncr.GetLeafsByL1InfoRoot(ctx, batchToVerify.L1InfoRoot) + if err != nil && !errors.Is(err, entities.ErrNotFound) { + return nil, err + } + + aLeaves := make([][32]byte, len(leaves)) + for i, leaf := range leaves { + aLeaves[i] = l1infotree.HashLeafData(leaf.GlobalExitRoot, leaf.PreviousBlockHash, uint64(leaf.Timestamp.Unix())) + } + + for _, l2blockRaw := range batchRawData.Blocks { + _, contained := l1InfoTreeData[l2blockRaw.IndexL1InfoTree] + if !contained && l2blockRaw.IndexL1InfoTree != 0 { + leaves, err := a.l1Syncr.GetL1InfoTreeLeaves(ctx, []uint32{l2blockRaw.IndexL1InfoTree}) + if err != nil { + log.Errorf("Error getting l1InfoTreeLeaf: %v", err) + return nil, err + } + + l1InfoTreeLeaf := leaves[l2blockRaw.IndexL1InfoTree] + + // Calculate smt proof + log.Infof("Calling tree.ComputeMerkleProof") + smtProof, calculatedL1InfoRoot, err := tree.ComputeMerkleProof(l2blockRaw.IndexL1InfoTree, aLeaves) + if err != nil { + log.Errorf("Error computing merkle proof: %v", err) + return nil, err + } + + if batchToVerify.L1InfoRoot != calculatedL1InfoRoot { + return nil, fmt.Errorf("error: l1InfoRoot mismatch. L1InfoRoot: %s, calculatedL1InfoRoot: %s. l1InfoTreeIndex: %d", batchToVerify.L1InfoRoot.String(), calculatedL1InfoRoot.String(), l2blockRaw.IndexL1InfoTree) + } + + protoProof := make([][]byte, len(smtProof)) + for i, proof := range smtProof { + tmpProof := proof + protoProof[i] = tmpProof[:] + } + + l1InfoTreeData[l2blockRaw.IndexL1InfoTree] = &prover.L1Data{ + GlobalExitRoot: l1InfoTreeLeaf.GlobalExitRoot.Bytes(), + BlockhashL1: l1InfoTreeLeaf.PreviousBlockHash.Bytes(), + MinTimestamp: uint32(l1InfoTreeLeaf.Timestamp.Unix()), + SmtProof: protoProof, + } + } + } + } else { + // Initial batch must be handled differently + if batchToVerify.BatchNumber == 1 { + virtualBatch, err := a.l1Syncr.GetVirtualBatchByBatchNumber(ctx, batchToVerify.BatchNumber) + if err != nil { + log.Errorf("Error getting virtual batch: %v", err) + return nil, err + } + l1Block, err := a.l1Syncr.GetL1BlockByNumber(ctx, virtualBatch.BlockNumber) + if err != nil { + log.Errorf("Error getting l1 block: %v", err) + return nil, err + } + + forcedBlockhashL1 = l1Block.ParentHash + l1InfoRoot = batchToVerify.GlobalExitRoot.Bytes() + } /*else { + forcedBlockhashL1, err = a.state.GetForcedBatchParentHash(ctx, *batchToVerify.ForcedBatchNum, nil) + if err != nil { + return nil, err + } + }*/ + } + + // Get Witness + witness, err := getWitness(batchToVerify.BatchNumber, a.cfg.WitnessURL, a.cfg.UseFullWitness) + if err != nil { + log.Errorf("Failed to get witness, err: %v", err) + return nil, err + } + + // Get Old Acc Input Hash + oldBatch, _, err := a.state.GetBatch(ctx, batchToVerify.BatchNumber-1, nil) + if err != nil { + return nil, err + } + + inputProver := &prover.StatelessInputProver{ + PublicInputs: &prover.StatelessPublicInputs{ + Witness: witness, + OldAccInputHash: oldBatch.AccInputHash.Bytes(), + OldBatchNum: batchToVerify.BatchNumber - 1, + ChainId: batchToVerify.ChainID, + ForkId: batchToVerify.ForkID, + BatchL2Data: batchToVerify.BatchL2Data, + L1InfoRoot: l1InfoRoot, + TimestampLimit: uint64(batchToVerify.Timestamp.Unix()), + SequencerAddr: batchToVerify.Coinbase.String(), + AggregatorAddr: a.cfg.SenderAddress, + L1InfoTreeData: l1InfoTreeData, + ForcedBlockhashL1: forcedBlockhashL1.Bytes(), + }, + } + + printInputProver(inputProver) + return inputProver, nil +} + +func calculateAccInputHash(oldAccInputHash common.Hash, batchData []byte, l1InfoRoot common.Hash, timestampLimit uint64, sequencerAddr common.Address, forcedBlockhashL1 common.Hash) (common.Hash, error) { + v1 := oldAccInputHash.Bytes() + v2 := batchData + v3 := l1InfoRoot.Bytes() + v4 := big.NewInt(0).SetUint64(timestampLimit).Bytes() + v5 := sequencerAddr.Bytes() + v6 := forcedBlockhashL1.Bytes() + + // Add 0s to make values 32 bytes long + for len(v1) < 32 { + v1 = append([]byte{0}, v1...) + } + for len(v3) < 32 { + v3 = append([]byte{0}, v3...) + } + for len(v4) < 8 { + v4 = append([]byte{0}, v4...) + } + for len(v5) < 20 { + v5 = append([]byte{0}, v5...) + } + for len(v6) < 32 { + v6 = append([]byte{0}, v6...) + } + + v2 = keccak256.Hash(v2) + + log.Debugf("OldAccInputHash: %v", oldAccInputHash) + log.Debugf("BatchHashData: %v", common.Bytes2Hex(v2)) + log.Debugf("L1InfoRoot: %v", l1InfoRoot) + log.Debugf("TimeStampLimit: %v", timestampLimit) + log.Debugf("Sequencer Address: %v", sequencerAddr) + log.Debugf("Forced BlockHashL1: %v", forcedBlockhashL1) + + return common.BytesToHash(keccak256.Hash(v1, v2, v3, v4, v5, v6)), nil +} + +func getWitness(batchNumber uint64, URL string, fullWitness bool) ([]byte, error) { + var witness string + var response rpc.Response + var err error + + witnessType := "trimmed" + if fullWitness { + witnessType = "full" + } + + response, err = rpc.JSONRPCCall(URL, "zkevm_getBatchWitness", batchNumber, witnessType) + if err != nil { + return nil, err + } + + // Check if the response is an error + if response.Error != nil { + return nil, fmt.Errorf("error from witness for batch %d: %v", batchNumber, response.Error) + } + + err = json.Unmarshal(response.Result, &witness) + if err != nil { + return nil, err + } + + witnessString := strings.TrimLeft(witness, "0x") + if len(witnessString)%2 != 0 { + witnessString = "0" + witnessString + } + bytes := common.Hex2Bytes(witnessString) + + return bytes, nil +} + +func printInputProver(inputProver *prover.StatelessInputProver) { + log.Debugf("Witness length: %v", len(inputProver.PublicInputs.Witness)) + log.Debugf("BatchL2Data length: %v", len(inputProver.PublicInputs.BatchL2Data)) + // log.Debugf("Full DataStream: %v", common.Bytes2Hex(inputProver.PublicInputs.DataStream)) + log.Debugf("OldAccInputHash: %v", common.BytesToHash(inputProver.PublicInputs.OldAccInputHash)) + log.Debugf("L1InfoRoot: %v", common.BytesToHash(inputProver.PublicInputs.L1InfoRoot)) + log.Debugf("TimestampLimit: %v", inputProver.PublicInputs.TimestampLimit) + log.Debugf("SequencerAddr: %v", inputProver.PublicInputs.SequencerAddr) + log.Debugf("AggregatorAddr: %v", inputProver.PublicInputs.AggregatorAddr) + log.Debugf("L1InfoTreeData: %+v", inputProver.PublicInputs.L1InfoTreeData) + log.Debugf("ForcedBlockhashL1: %v", common.BytesToHash(inputProver.PublicInputs.ForcedBlockhashL1)) +} + +// healthChecker will provide an implementation of the HealthCheck interface. +type healthChecker struct{} + +// newHealthChecker returns a health checker according to standard package +// grpc.health.v1. +func newHealthChecker() *healthChecker { + return &healthChecker{} +} + +// HealthCheck interface implementation. + +// Check returns the current status of the server for unary gRPC health requests, +// for now if the server is up and able to respond we will always return SERVING. +func (hc *healthChecker) Check(ctx context.Context, req *grpchealth.HealthCheckRequest) (*grpchealth.HealthCheckResponse, error) { + log.Info("Serving the Check request for health check") + return &grpchealth.HealthCheckResponse{ + Status: grpchealth.HealthCheckResponse_SERVING, + }, nil +} + +// Watch returns the current status of the server for stream gRPC health requests, +// for now if the server is up and able to respond we will always return SERVING. +func (hc *healthChecker) Watch(req *grpchealth.HealthCheckRequest, server grpchealth.Health_WatchServer) error { + log.Info("Serving the Watch request for health check") + return server.Send(&grpchealth.HealthCheckResponse{ + Status: grpchealth.HealthCheckResponse_SERVING, + }) +} + +func (a *Aggregator) handleMonitoredTxResult(result ethtxmanager.MonitoredTxResult) { + mTxResultLogger := ethtxmanager.CreateMonitoredTxResultLogger(result) + if result.Status == ethtxmanager.MonitoredTxStatusFailed { + mTxResultLogger.Fatal("failed to send batch verification, TODO: review this fatal and define what to do in this case") + } + + // TODO: REVIEW THIS + + /* + // monitoredIDFormat: "proof-from-%v-to-%v" + idSlice := strings.Split(result.ID, "-") + proofBatchNumberStr := idSlice[2] + proofBatchNumber, err := strconv.ParseUint(proofBatchNumberStr, encoding.Base10, 0) + + if err != nil { + mTxResultLogger.Errorf("failed to read final proof batch number from monitored tx: %v", err) + } + + proofBatchNumberFinalStr := idSlice[4] + proofBatchNumberFinal, err := strconv.ParseUint(proofBatchNumberFinalStr, encoding.Base10, 0) + + if err != nil { + mTxResultLogger.Errorf("failed to read final proof batch number final from monitored tx: %v", err) + } + + log := log.WithFields("txId", result.ID, "batches", fmt.Sprintf("%d-%d", proofBatchNumber, proofBatchNumberFinal)) + log.Info("Final proof verified") + + // wait for the synchronizer to catch up the verified batches + log.Debug("A final proof has been sent, waiting for the network to be synced") + + for !a.isSynced(a.ctx, &proofBatchNumberFinal) { + log.Info("Waiting for synchronizer to sync...") + time.Sleep(a.cfg.RetryTime.Duration) + } + + // network is synced with the final proof, we can safely delete all recursive + // proofs up to the last synced batch + err = a.State.CleanupGeneratedProofs(a.ctx, proofBatchNumberFinal, nil) + + if err != nil { + log.Errorf("Failed to store proof aggregation result: %v", err) + } + */ +} + +/* +func buildMonitoredTxID(batchNumber, batchNumberFinal uint64) string { + return fmt.Sprintf(monitoredIDFormat, batchNumber, batchNumberFinal) +} +*/ + +func (a *Aggregator) cleanupLockedProofs() { + for { + select { + case <-a.ctx.Done(): + return + case <-time.After(a.timeCleanupLockedProofs.Duration): + n, err := a.state.CleanupLockedProofs(a.ctx, a.cfg.GeneratingProofCleanupThreshold, nil) + if err != nil { + log.Errorf("Failed to cleanup locked proofs: %v", err) + } + if n == 1 { + log.Warn("Found a stale proof and removed from cache") + } else if n > 1 { + log.Warnf("Found %d stale proofs and removed from cache", n) + } + } + } +} + +// FirstToUpper returns the string passed as argument with the first letter in +// uppercase. +func FirstToUpper(s string) string { + runes := []rune(s) + runes[0] = unicode.ToUpper(runes[0]) + return string(runes) +} diff --git a/aggregator/config.go b/aggregator/config.go new file mode 100644 index 00000000..3d53e0f4 --- /dev/null +++ b/aggregator/config.go @@ -0,0 +1,169 @@ +package aggregator + +import ( + "crypto/ecdsa" + "fmt" + "math/big" + "os" + "path/filepath" + + "github.com/0xPolygon/cdk/aggregator/db" + "github.com/0xPolygon/cdk/config/types" + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygonHermez/zkevm-ethtx-manager/ethtxmanager" + syncronizerConfig "github.com/0xPolygonHermez/zkevm-synchronizer-l1/config" + "github.com/ethereum/go-ethereum/accounts/keystore" +) + +// SettlementBackend is the type of the settlement backend +type SettlementBackend string + +const ( + // AggLayer settlement backend + AggLayer SettlementBackend = "agglayer" + + // L1 settlement backend + L1 SettlementBackend = "l1" + + // TenToThePowerOf18 represents 1000000000000000000 + TenToThePowerOf18 = 1000000000000000000 +) + +// TokenAmountWithDecimals is a wrapper type that parses token amount with decimals to big int +type TokenAmountWithDecimals struct { + *big.Int `validate:"required"` +} + +// UnmarshalText unmarshal token amount from float string to big int +func (t *TokenAmountWithDecimals) UnmarshalText(data []byte) error { + amount, ok := new(big.Float).SetString(string(data)) + if !ok { + return fmt.Errorf("failed to unmarshal string to float") + } + coin := new(big.Float).SetInt(big.NewInt(TenToThePowerOf18)) + bigval := new(big.Float).Mul(amount, coin) + result := new(big.Int) + bigval.Int(result) + t.Int = result + + return nil +} + +// Config represents the configuration of the aggregator +type Config struct { + // Host for the grpc server + Host string `mapstructure:"Host"` + // Port for the grpc server + Port int `mapstructure:"Port"` + + // RetryTime is the time the aggregator main loop sleeps if there are no proofs to aggregate + // or batches to generate proofs. It is also used in the isSynced loop + RetryTime types.Duration `mapstructure:"RetryTime"` + + // VerifyProofInterval is the interval of time to verify/send an proof in L1 + VerifyProofInterval types.Duration `mapstructure:"VerifyProofInterval"` + + // ProofStatePollingInterval is the interval time to polling the prover about the generation state of a proof + ProofStatePollingInterval types.Duration `mapstructure:"ProofStatePollingInterval"` + + // TxProfitabilityCheckerType type for checking is it profitable for aggregator to validate batch + // possible values: base/acceptall + TxProfitabilityCheckerType TxProfitabilityCheckerType `mapstructure:"TxProfitabilityCheckerType"` + + // TxProfitabilityMinReward min reward for base tx profitability checker when aggregator will validate batch + // this parameter is used for the base tx profitability checker + TxProfitabilityMinReward TokenAmountWithDecimals `mapstructure:"TxProfitabilityMinReward"` + + // IntervalAfterWhichBatchConsolidateAnyway this is interval for the main sequencer, that will check if there is no transactions + IntervalAfterWhichBatchConsolidateAnyway types.Duration `mapstructure:"IntervalAfterWhichBatchConsolidateAnyway"` + + // ChainID is the L2 ChainID provided by the Network Config + ChainID uint64 + + // ForkID is the L2 ForkID provided by the Network Config + ForkId uint64 `mapstructure:"ForkId"` + + // SenderAddress defines which private key the eth tx manager needs to use + // to sign the L1 txs + SenderAddress string `mapstructure:"SenderAddress"` + + // CleanupLockedProofsInterval is the interval of time to clean up locked proofs. + CleanupLockedProofsInterval types.Duration `mapstructure:"CleanupLockedProofsInterval"` + + // GeneratingProofCleanupThreshold represents the time interval after + // which a proof in generating state is considered to be stuck and + // allowed to be cleared. + GeneratingProofCleanupThreshold string `mapstructure:"GeneratingProofCleanupThreshold"` + + // GasOffset is the amount of gas to be added to the gas estimation in order + // to provide an amount that is higher than the estimated one. This is used + // to avoid the TX getting reverted in case something has changed in the network + // state after the estimation which can cause the TX to require more gas to be + // executed. + // + // ex: + // gas estimation: 1000 + // gas offset: 100 + // final gas: 1100 + GasOffset uint64 `mapstructure:"GasOffset"` + + // WitnessURL is the URL of the witness server + WitnessURL string `mapstructure:"WitnessURL"` + + // UseL1BatchData is a flag to enable the use of L1 batch data in the aggregator + UseL1BatchData bool `mapstructure:"UseL1BatchData"` + + // UseFullWitness is a flag to enable the use of full witness in the aggregator + UseFullWitness bool `mapstructure:"UseFullWitness"` + + // DB is the database configuration + DB db.Config `mapstructure:"DB"` + + // StreamClient is the config for the stream client + StreamClient StreamClientCfg `mapstructure:"StreamClient"` + + // EthTxManager is the config for the ethtxmanager + EthTxManager ethtxmanager.Config `mapstructure:"EthTxManager"` + + // Log is the log configuration + Log log.Config `mapstructure:"Log"` + + // Synchornizer config + Synchronizer syncronizerConfig.Config `mapstructure:"Synchronizer"` + + // SettlementBackend configuration defines how a final ZKP should be settled. Directly to L1 or over the Beethoven service. + SettlementBackend SettlementBackend `mapstructure:"SettlementBackend" jsonschema:"enum=agglayer,enum=l1"` + + // SequencerPrivateKey Private key of the trusted sequencer + SequencerPrivateKey types.KeystoreFileConfig `mapstructure:"SequencerPrivateKey"` + + // AggLayerTxTimeout is the interval time to wait for a tx to be mined from the agglayer + AggLayerTxTimeout types.Duration `mapstructure:"AggLayerTxTimeout"` + + // AggLayerURL url of the agglayer service + AggLayerURL string `mapstructure:"AggLayerURL"` +} + +// StreamClientCfg contains the data streamer's configuration properties +type StreamClientCfg struct { + // Datastream server to connect + Server string `mapstructure:"Server"` + // Log is the log configuration + Log log.Config `mapstructure:"Log"` +} + +// newKeyFromKeystore creates a private key from a keystore file +func newKeyFromKeystore(cfg types.KeystoreFileConfig) (*ecdsa.PrivateKey, error) { + if cfg.Path == "" && cfg.Password == "" { + return nil, nil + } + keystoreEncrypted, err := os.ReadFile(filepath.Clean(cfg.Path)) + if err != nil { + return nil, err + } + key, err := keystore.DecryptKey(keystoreEncrypted, cfg.Password) + if err != nil { + return nil, err + } + return key.PrivateKey, nil +} diff --git a/aggregator/db/config.go b/aggregator/db/config.go new file mode 100644 index 00000000..ad56155f --- /dev/null +++ b/aggregator/db/config.go @@ -0,0 +1,25 @@ +package db + +// Config provide fields to configure the pool +type Config struct { + // Database name + Name string `mapstructure:"Name"` + + // Database User name + User string `mapstructure:"User"` + + // Database Password of the user + Password string `mapstructure:"Password"` + + // Host address of database + Host string `mapstructure:"Host"` + + // Port Number of database + Port string `mapstructure:"Port"` + + // EnableLog + EnableLog bool `mapstructure:"EnableLog"` + + // MaxConns is the maximum number of connections in the pool. + MaxConns int `mapstructure:"MaxConns"` +} diff --git a/aggregator/db/db.go b/aggregator/db/db.go new file mode 100644 index 00000000..21b31938 --- /dev/null +++ b/aggregator/db/db.go @@ -0,0 +1,123 @@ +package db + +import ( + "context" + "fmt" + + "github.com/0xPolygon/cdk/log" + "github.com/gobuffalo/packr/v2" + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" + "github.com/jackc/pgx/v4/stdlib" + migrate "github.com/rubenv/sql-migrate" +) + +const ( + // AggregatorMigrationName is the name of the migration used by packr to pack the migration file + AggregatorMigrationName = "zkevm-aggregator-db" +) + +var packrMigrations = map[string]*packr.Box{ + AggregatorMigrationName: packr.New(AggregatorMigrationName, "./migrations/aggregator"), +} + +// NewSQLDB creates a new SQL DB +func NewSQLDB(cfg Config) (*pgxpool.Pool, error) { + config, err := pgxpool.ParseConfig(fmt.Sprintf("postgres://%s:%s@%s:%s/%s?pool_max_conns=%d", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Name, cfg.MaxConns)) + if err != nil { + log.Errorf("Unable to parse DB config: %v\n", err) + return nil, err + } + if cfg.EnableLog { + config.ConnConfig.Logger = logger{} + } + conn, err := pgxpool.ConnectConfig(context.Background(), config) + if err != nil { + log.Errorf("Unable to connect to database: %v\n", err) + return nil, err + } + return conn, nil +} + +// RunMigrationsUp runs migrate-up for the given config. +func RunMigrationsUp(cfg Config, name string) error { + log.Info("running migrations up") + return runMigrations(cfg, name, migrate.Up) +} + +// CheckMigrations runs migrate-up for the given config. +func CheckMigrations(cfg Config, name string) error { + return checkMigrations(cfg, name, migrate.Up) +} + +// RunMigrationsDown runs migrate-down for the given config. +func RunMigrationsDown(cfg Config, name string) error { + log.Info("running migrations down") + return runMigrations(cfg, name, migrate.Down) +} + +// runMigrations will execute pending migrations if needed to keep +// the database updated with the latest changes in either direction, +// up or down. +func runMigrations(cfg Config, packrName string, direction migrate.MigrationDirection) error { + c, err := pgx.ParseConfig(fmt.Sprintf("postgres://%s:%s@%s:%s/%s", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Name)) + if err != nil { + return err + } + db := stdlib.OpenDB(*c) + + box, ok := packrMigrations[packrName] + if !ok { + return fmt.Errorf("packr box not found with name: %v", packrName) + } + + var migrations = &migrate.PackrMigrationSource{Box: box} + nMigrations, err := migrate.Exec(db, "postgres", migrations, direction) + if err != nil { + return err + } + + log.Info("successfully ran ", nMigrations, " migrations") + return nil +} + +func checkMigrations(cfg Config, packrName string, direction migrate.MigrationDirection) error { + c, err := pgx.ParseConfig(fmt.Sprintf("postgres://%s:%s@%s:%s/%s", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Name)) + if err != nil { + return err + } + db := stdlib.OpenDB(*c) + + box, ok := packrMigrations[packrName] + if !ok { + return fmt.Errorf("packr box not found with name: %v", packrName) + } + + migrationSource := &migrate.PackrMigrationSource{Box: box} + migrations, err := migrationSource.FindMigrations() + if err != nil { + log.Errorf("error getting migrations from source: %v", err) + return err + } + + var expected int + for _, migration := range migrations { + if len(migration.Up) != 0 { + expected++ + } + } + + var actual int + query := `SELECT COUNT(1) FROM public.gorp_migrations` + err = db.QueryRow(query).Scan(&actual) + if err != nil { + log.Error("error getting migrations count: ", err) + return err + } + if expected == actual { + log.Infof("Found %d migrations as expected", actual) + } else { + return fmt.Errorf("error the component needs to run %d migrations before starting. DB only contains %d migrations", expected, actual) + } + return nil +} diff --git a/aggregator/db/logger.go b/aggregator/db/logger.go new file mode 100644 index 00000000..3b425b13 --- /dev/null +++ b/aggregator/db/logger.go @@ -0,0 +1,27 @@ +package db + +import ( + "context" + "fmt" + + "github.com/0xPolygon/cdk/log" + "github.com/jackc/pgx/v4" +) + +type logger struct{} + +func (l logger) Log(ctx context.Context, level pgx.LogLevel, msg string, data map[string]interface{}) { + m := fmt.Sprintf("%s %v", msg, data) + + switch level { + case pgx.LogLevelInfo: + log.Info(m) + case pgx.LogLevelWarn: + log.Warn(m) + case pgx.LogLevelError: + log.Error(m) + default: + m = fmt.Sprintf("%s %s %v", level.String(), msg, data) + log.Debug(m) + } +} diff --git a/aggregator/db/migrations/aggregator/0001.sql b/aggregator/db/migrations/aggregator/0001.sql new file mode 100644 index 00000000..963dbea7 --- /dev/null +++ b/aggregator/db/migrations/aggregator/0001.sql @@ -0,0 +1,32 @@ +-- +migrate Down +DROP SCHEMA IF EXISTS aggregator CASCADE; + +-- +migrate Up +CREATE SCHEMA aggregator; + +CREATE TABLE IF NOT EXISTS aggregator.batch ( + batch_num BIGINT NOT NULL, + batch jsonb NOT NULL, + datastream varchar NOT NULL, + PRIMARY KEY (batch_num) +); + +CREATE TABLE IF NOT EXISTS aggregator.proof ( + batch_num BIGINT NOT NULL REFERENCES aggregator.batch (batch_num) ON DELETE CASCADE, + batch_num_final BIGINT NOT NULL, + proof varchar NULL, + proof_id varchar NULL, + input_prover varchar NULL, + prover varchar NULL, + prover_id varchar NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + generating_since timestamptz NULL, + PRIMARY KEY (batch_num, batch_num_final) +); + +CREATE TABLE IF NOT EXISTS aggregator.sequence ( + from_batch_num BIGINT NOT NULL REFERENCES aggregator.batch (batch_num) ON DELETE CASCADE, + to_batch_num BIGINT NOT NULL, + PRIMARY KEY (from_batch_num) +); diff --git a/aggregator/ethmantypes/finalproofinputs.go b/aggregator/ethmantypes/finalproofinputs.go new file mode 100644 index 00000000..3d0b0e36 --- /dev/null +++ b/aggregator/ethmantypes/finalproofinputs.go @@ -0,0 +1,10 @@ +package ethmantypes + +import "github.com/0xPolygon/cdk/aggregator/prover" + +// FinalProofInputs struct +type FinalProofInputs struct { + FinalProof *prover.FinalProof + NewLocalExitRoot []byte + NewStateRoot []byte +} diff --git a/aggregator/ethmantypes/sequence.go b/aggregator/ethmantypes/sequence.go new file mode 100644 index 00000000..cb68e3a0 --- /dev/null +++ b/aggregator/ethmantypes/sequence.go @@ -0,0 +1,26 @@ +package ethmantypes + +import ( + "reflect" + + "github.com/ethereum/go-ethereum/common" +) + +// Sequence represents an operation sent to the PoE smart contract to be +// processed. +type Sequence struct { + GlobalExitRoot, StateRoot, LocalExitRoot common.Hash + AccInputHash common.Hash + LastL2BLockTimestamp uint64 + BatchL2Data []byte + IsSequenceTooBig bool + BatchNumber uint64 + ForcedBatchTimestamp int64 + PrevBlockHash common.Hash + LastCoinbase common.Address +} + +// IsEmpty checks is sequence struct is empty +func (s Sequence) IsEmpty() bool { + return reflect.DeepEqual(s, Sequence{}) +} diff --git a/aggregator/interfaces.go b/aggregator/interfaces.go new file mode 100644 index 00000000..ba21ca86 --- /dev/null +++ b/aggregator/interfaces.go @@ -0,0 +1,62 @@ +package aggregator + +import ( + "context" + "math/big" + + ethmanTypes "github.com/0xPolygon/cdk/aggregator/ethmantypes" + "github.com/0xPolygon/cdk/aggregator/prover" + "github.com/0xPolygon/cdk/state" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/jackc/pgx/v4" +) + +// Consumer interfaces required by the package. + +type proverInterface interface { + Name() string + ID() string + Addr() string + IsIdle() (bool, error) + BatchProof(input *prover.StatelessInputProver) (*string, error) + AggregatedProof(inputProof1, inputProof2 string) (*string, error) + FinalProof(inputProof string, aggregatorAddr string) (*string, error) + WaitRecursiveProof(ctx context.Context, proofID string) (string, error) + WaitFinalProof(ctx context.Context, proofID string) (*prover.FinalProof, error) +} + +// etherman contains the methods required to interact with ethereum +type etherman interface { + GetRollupId() uint32 + GetLatestVerifiedBatchNum() (uint64, error) + BuildTrustedVerifyBatchesTxData(lastVerifiedBatch, newVerifiedBatch uint64, inputs *ethmanTypes.FinalProofInputs, beneficiary common.Address) (to *common.Address, data []byte, err error) + GetLatestBlockHeader(ctx context.Context) (*types.Header, error) + GetBatchAccInputHash(ctx context.Context, batchNumber uint64) (common.Hash, error) +} + +// aggregatorTxProfitabilityChecker interface for different profitability +// checking algorithms. +type aggregatorTxProfitabilityChecker interface { + IsProfitable(context.Context, *big.Int) (bool, error) +} + +// stateInterface gathers the methods to interact with the state. +type stateInterface interface { + BeginStateTransaction(ctx context.Context) (pgx.Tx, error) + CheckProofContainsCompleteSequences(ctx context.Context, proof *state.Proof, dbTx pgx.Tx) (bool, error) + GetProofReadyToVerify(ctx context.Context, lastVerfiedBatchNumber uint64, dbTx pgx.Tx) (*state.Proof, error) + GetProofsToAggregate(ctx context.Context, dbTx pgx.Tx) (*state.Proof, *state.Proof, error) + AddGeneratedProof(ctx context.Context, proof *state.Proof, dbTx pgx.Tx) error + UpdateGeneratedProof(ctx context.Context, proof *state.Proof, dbTx pgx.Tx) error + DeleteGeneratedProofs(ctx context.Context, batchNumber uint64, batchNumberFinal uint64, dbTx pgx.Tx) error + DeleteUngeneratedProofs(ctx context.Context, dbTx pgx.Tx) error + CleanupGeneratedProofs(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error + CleanupLockedProofs(ctx context.Context, duration string, dbTx pgx.Tx) (int64, error) + CheckProofExistsForBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (bool, error) + AddSequence(ctx context.Context, sequence state.Sequence, dbTx pgx.Tx) error + AddBatch(ctx context.Context, batch *state.Batch, datastream []byte, dbTx pgx.Tx) error + GetBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.Batch, []byte, error) + DeleteBatchesOlderThanBatchNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error + DeleteBatchesNewerThanBatchNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error +} diff --git a/aggregator/profitabilitychecker.go b/aggregator/profitabilitychecker.go new file mode 100644 index 00000000..27e3f705 --- /dev/null +++ b/aggregator/profitabilitychecker.go @@ -0,0 +1,91 @@ +package aggregator + +import ( + "context" + "math/big" + "time" +) + +// TxProfitabilityCheckerType checks profitability of batch validation +type TxProfitabilityCheckerType string + +const ( + // ProfitabilityBase checks pol collateral with min reward + ProfitabilityBase = "base" + // ProfitabilityAcceptAll validate batch anyway and don't check anything + ProfitabilityAcceptAll = "acceptall" +) + +// TxProfitabilityCheckerBase checks pol collateral with min reward +type TxProfitabilityCheckerBase struct { + State stateInterface + IntervalAfterWhichBatchSentAnyway time.Duration + MinReward *big.Int +} + +// NewTxProfitabilityCheckerBase init base tx profitability checker +func NewTxProfitabilityCheckerBase(state stateInterface, interval time.Duration, minReward *big.Int) *TxProfitabilityCheckerBase { + return &TxProfitabilityCheckerBase{ + State: state, + IntervalAfterWhichBatchSentAnyway: interval, + MinReward: minReward, + } +} + +// IsProfitable checks pol collateral with min reward +func (pc *TxProfitabilityCheckerBase) IsProfitable(ctx context.Context, polCollateral *big.Int) (bool, error) { + //if pc.IntervalAfterWhichBatchSentAnyway != 0 { + // ok, err := isConsolidatedBatchAppeared(ctx, pc.State, pc.IntervalAfterWhichBatchSentAnyway) + // if err != nil { + // return false, err + // } + // if ok { + // return true, nil + // } + //} + + return polCollateral.Cmp(pc.MinReward) >= 0, nil +} + +// TxProfitabilityCheckerAcceptAll validate batch anyway and don't check anything +type TxProfitabilityCheckerAcceptAll struct { + State stateInterface + IntervalAfterWhichBatchSentAnyway time.Duration +} + +// NewTxProfitabilityCheckerAcceptAll init tx profitability checker that accept all txs +func NewTxProfitabilityCheckerAcceptAll(state stateInterface, interval time.Duration) *TxProfitabilityCheckerAcceptAll { + return &TxProfitabilityCheckerAcceptAll{ + State: state, + IntervalAfterWhichBatchSentAnyway: interval, + } +} + +// IsProfitable validate batch anyway and don't check anything +func (pc *TxProfitabilityCheckerAcceptAll) IsProfitable(ctx context.Context, polCollateral *big.Int) (bool, error) { + //if pc.IntervalAfterWhichBatchSentAnyway != 0 { + // ok, err := isConsolidatedBatchAppeared(ctx, pc.State, pc.IntervalAfterWhichBatchSentAnyway) + // if err != nil { + // return false, err + // } + // if ok { + // return true, nil + // } + //} + + return true, nil +} + +// TODO: now it's impossible to check, when batch got consolidated, bcs it's not saved +//func isConsolidatedBatchAppeared(ctx context.Context, state stateInterface, intervalAfterWhichBatchConsolidatedAnyway time.Duration) (bool, error) { +// batch, err := state.GetLastVerifiedBatch(ctx, nil) +// if err != nil { +// return false, fmt.Errorf("failed to get last verified batch, err: %v", err) +// } +// interval := intervalAfterWhichBatchConsolidatedAnyway * time.Minute +// if batch..Before(time.Now().Add(-interval)) { +// return true, nil +// } +// +// return false, err +//} diff --git a/aggregator/prover/aggregator.pb.go b/aggregator/prover/aggregator.pb.go new file mode 100644 index 00000000..b79d134b --- /dev/null +++ b/aggregator/prover/aggregator.pb.go @@ -0,0 +1,2819 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.27.0 +// source: aggregator.proto + +package prover + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// * +// @dev Result +// - OK: succesfully completed +// - ERROR: request is not correct, i.e. input data is wrong +// - INTERNAL_ERROR: internal server error when delivering the response +type Result int32 + +const ( + Result_RESULT_UNSPECIFIED Result = 0 + Result_RESULT_OK Result = 1 + Result_RESULT_ERROR Result = 2 + Result_RESULT_INTERNAL_ERROR Result = 3 +) + +// Enum value maps for Result. +var ( + Result_name = map[int32]string{ + 0: "RESULT_UNSPECIFIED", + 1: "RESULT_OK", + 2: "RESULT_ERROR", + 3: "RESULT_INTERNAL_ERROR", + } + Result_value = map[string]int32{ + "RESULT_UNSPECIFIED": 0, + "RESULT_OK": 1, + "RESULT_ERROR": 2, + "RESULT_INTERNAL_ERROR": 3, + } +) + +func (x Result) Enum() *Result { + p := new(Result) + *p = x + return p +} + +func (x Result) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Result) Descriptor() protoreflect.EnumDescriptor { + return file_aggregator_proto_enumTypes[0].Descriptor() +} + +func (Result) Type() protoreflect.EnumType { + return &file_aggregator_proto_enumTypes[0] +} + +func (x Result) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Result.Descriptor instead. +func (Result) EnumDescriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{0} +} + +type GetStatusResponse_Status int32 + +const ( + GetStatusResponse_STATUS_UNSPECIFIED GetStatusResponse_Status = 0 + GetStatusResponse_STATUS_BOOTING GetStatusResponse_Status = 1 + GetStatusResponse_STATUS_COMPUTING GetStatusResponse_Status = 2 + GetStatusResponse_STATUS_IDLE GetStatusResponse_Status = 3 + GetStatusResponse_STATUS_HALT GetStatusResponse_Status = 4 +) + +// Enum value maps for GetStatusResponse_Status. +var ( + GetStatusResponse_Status_name = map[int32]string{ + 0: "STATUS_UNSPECIFIED", + 1: "STATUS_BOOTING", + 2: "STATUS_COMPUTING", + 3: "STATUS_IDLE", + 4: "STATUS_HALT", + } + GetStatusResponse_Status_value = map[string]int32{ + "STATUS_UNSPECIFIED": 0, + "STATUS_BOOTING": 1, + "STATUS_COMPUTING": 2, + "STATUS_IDLE": 3, + "STATUS_HALT": 4, + } +) + +func (x GetStatusResponse_Status) Enum() *GetStatusResponse_Status { + p := new(GetStatusResponse_Status) + *p = x + return p +} + +func (x GetStatusResponse_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GetStatusResponse_Status) Descriptor() protoreflect.EnumDescriptor { + return file_aggregator_proto_enumTypes[1].Descriptor() +} + +func (GetStatusResponse_Status) Type() protoreflect.EnumType { + return &file_aggregator_proto_enumTypes[1] +} + +func (x GetStatusResponse_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GetStatusResponse_Status.Descriptor instead. +func (GetStatusResponse_Status) EnumDescriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{10, 0} +} + +type GetProofResponse_Result int32 + +const ( + GetProofResponse_RESULT_UNSPECIFIED GetProofResponse_Result = 0 + GetProofResponse_RESULT_COMPLETED_OK GetProofResponse_Result = 1 + GetProofResponse_RESULT_ERROR GetProofResponse_Result = 2 + GetProofResponse_RESULT_COMPLETED_ERROR GetProofResponse_Result = 3 + GetProofResponse_RESULT_PENDING GetProofResponse_Result = 4 + GetProofResponse_RESULT_INTERNAL_ERROR GetProofResponse_Result = 5 + GetProofResponse_RESULT_CANCEL GetProofResponse_Result = 6 +) + +// Enum value maps for GetProofResponse_Result. +var ( + GetProofResponse_Result_name = map[int32]string{ + 0: "RESULT_UNSPECIFIED", + 1: "RESULT_COMPLETED_OK", + 2: "RESULT_ERROR", + 3: "RESULT_COMPLETED_ERROR", + 4: "RESULT_PENDING", + 5: "RESULT_INTERNAL_ERROR", + 6: "RESULT_CANCEL", + } + GetProofResponse_Result_value = map[string]int32{ + "RESULT_UNSPECIFIED": 0, + "RESULT_COMPLETED_OK": 1, + "RESULT_ERROR": 2, + "RESULT_COMPLETED_ERROR": 3, + "RESULT_PENDING": 4, + "RESULT_INTERNAL_ERROR": 5, + "RESULT_CANCEL": 6, + } +) + +func (x GetProofResponse_Result) Enum() *GetProofResponse_Result { + p := new(GetProofResponse_Result) + *p = x + return p +} + +func (x GetProofResponse_Result) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GetProofResponse_Result) Descriptor() protoreflect.EnumDescriptor { + return file_aggregator_proto_enumTypes[2].Descriptor() +} + +func (GetProofResponse_Result) Type() protoreflect.EnumType { + return &file_aggregator_proto_enumTypes[2] +} + +func (x GetProofResponse_Result) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GetProofResponse_Result.Descriptor instead. +func (GetProofResponse_Result) EnumDescriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{15, 0} +} + +type Version struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + V0_0_1 string `protobuf:"bytes,1,opt,name=v0_0_1,json=v001,proto3" json:"v0_0_1,omitempty"` +} + +func (x *Version) Reset() { + *x = Version{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Version) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Version) ProtoMessage() {} + +func (x *Version) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Version.ProtoReflect.Descriptor instead. +func (*Version) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{0} +} + +func (x *Version) GetV0_0_1() string { + if x != nil { + return x.V0_0_1 + } + return "" +} + +type AggregatorMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // Types that are assignable to Request: + // + // *AggregatorMessage_GetStatusRequest + // *AggregatorMessage_GenBatchProofRequest + // *AggregatorMessage_GenAggregatedProofRequest + // *AggregatorMessage_GenFinalProofRequest + // *AggregatorMessage_CancelRequest + // *AggregatorMessage_GetProofRequest + // *AggregatorMessage_GenStatelessBatchProofRequest + Request isAggregatorMessage_Request `protobuf_oneof:"request"` +} + +func (x *AggregatorMessage) Reset() { + *x = AggregatorMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AggregatorMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AggregatorMessage) ProtoMessage() {} + +func (x *AggregatorMessage) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AggregatorMessage.ProtoReflect.Descriptor instead. +func (*AggregatorMessage) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{1} +} + +func (x *AggregatorMessage) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (m *AggregatorMessage) GetRequest() isAggregatorMessage_Request { + if m != nil { + return m.Request + } + return nil +} + +func (x *AggregatorMessage) GetGetStatusRequest() *GetStatusRequest { + if x, ok := x.GetRequest().(*AggregatorMessage_GetStatusRequest); ok { + return x.GetStatusRequest + } + return nil +} + +func (x *AggregatorMessage) GetGenBatchProofRequest() *GenBatchProofRequest { + if x, ok := x.GetRequest().(*AggregatorMessage_GenBatchProofRequest); ok { + return x.GenBatchProofRequest + } + return nil +} + +func (x *AggregatorMessage) GetGenAggregatedProofRequest() *GenAggregatedProofRequest { + if x, ok := x.GetRequest().(*AggregatorMessage_GenAggregatedProofRequest); ok { + return x.GenAggregatedProofRequest + } + return nil +} + +func (x *AggregatorMessage) GetGenFinalProofRequest() *GenFinalProofRequest { + if x, ok := x.GetRequest().(*AggregatorMessage_GenFinalProofRequest); ok { + return x.GenFinalProofRequest + } + return nil +} + +func (x *AggregatorMessage) GetCancelRequest() *CancelRequest { + if x, ok := x.GetRequest().(*AggregatorMessage_CancelRequest); ok { + return x.CancelRequest + } + return nil +} + +func (x *AggregatorMessage) GetGetProofRequest() *GetProofRequest { + if x, ok := x.GetRequest().(*AggregatorMessage_GetProofRequest); ok { + return x.GetProofRequest + } + return nil +} + +func (x *AggregatorMessage) GetGenStatelessBatchProofRequest() *GenStatelessBatchProofRequest { + if x, ok := x.GetRequest().(*AggregatorMessage_GenStatelessBatchProofRequest); ok { + return x.GenStatelessBatchProofRequest + } + return nil +} + +type isAggregatorMessage_Request interface { + isAggregatorMessage_Request() +} + +type AggregatorMessage_GetStatusRequest struct { + GetStatusRequest *GetStatusRequest `protobuf:"bytes,2,opt,name=get_status_request,json=getStatusRequest,proto3,oneof"` +} + +type AggregatorMessage_GenBatchProofRequest struct { + GenBatchProofRequest *GenBatchProofRequest `protobuf:"bytes,3,opt,name=gen_batch_proof_request,json=genBatchProofRequest,proto3,oneof"` +} + +type AggregatorMessage_GenAggregatedProofRequest struct { + GenAggregatedProofRequest *GenAggregatedProofRequest `protobuf:"bytes,4,opt,name=gen_aggregated_proof_request,json=genAggregatedProofRequest,proto3,oneof"` +} + +type AggregatorMessage_GenFinalProofRequest struct { + GenFinalProofRequest *GenFinalProofRequest `protobuf:"bytes,5,opt,name=gen_final_proof_request,json=genFinalProofRequest,proto3,oneof"` +} + +type AggregatorMessage_CancelRequest struct { + CancelRequest *CancelRequest `protobuf:"bytes,6,opt,name=cancel_request,json=cancelRequest,proto3,oneof"` +} + +type AggregatorMessage_GetProofRequest struct { + GetProofRequest *GetProofRequest `protobuf:"bytes,7,opt,name=get_proof_request,json=getProofRequest,proto3,oneof"` +} + +type AggregatorMessage_GenStatelessBatchProofRequest struct { + GenStatelessBatchProofRequest *GenStatelessBatchProofRequest `protobuf:"bytes,8,opt,name=gen_stateless_batch_proof_request,json=genStatelessBatchProofRequest,proto3,oneof"` +} + +func (*AggregatorMessage_GetStatusRequest) isAggregatorMessage_Request() {} + +func (*AggregatorMessage_GenBatchProofRequest) isAggregatorMessage_Request() {} + +func (*AggregatorMessage_GenAggregatedProofRequest) isAggregatorMessage_Request() {} + +func (*AggregatorMessage_GenFinalProofRequest) isAggregatorMessage_Request() {} + +func (*AggregatorMessage_CancelRequest) isAggregatorMessage_Request() {} + +func (*AggregatorMessage_GetProofRequest) isAggregatorMessage_Request() {} + +func (*AggregatorMessage_GenStatelessBatchProofRequest) isAggregatorMessage_Request() {} + +type ProverMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // Types that are assignable to Response: + // + // *ProverMessage_GetStatusResponse + // *ProverMessage_GenBatchProofResponse + // *ProverMessage_GenAggregatedProofResponse + // *ProverMessage_GenFinalProofResponse + // *ProverMessage_CancelResponse + // *ProverMessage_GetProofResponse + Response isProverMessage_Response `protobuf_oneof:"response"` +} + +func (x *ProverMessage) Reset() { + *x = ProverMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProverMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProverMessage) ProtoMessage() {} + +func (x *ProverMessage) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProverMessage.ProtoReflect.Descriptor instead. +func (*ProverMessage) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{2} +} + +func (x *ProverMessage) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (m *ProverMessage) GetResponse() isProverMessage_Response { + if m != nil { + return m.Response + } + return nil +} + +func (x *ProverMessage) GetGetStatusResponse() *GetStatusResponse { + if x, ok := x.GetResponse().(*ProverMessage_GetStatusResponse); ok { + return x.GetStatusResponse + } + return nil +} + +func (x *ProverMessage) GetGenBatchProofResponse() *GenBatchProofResponse { + if x, ok := x.GetResponse().(*ProverMessage_GenBatchProofResponse); ok { + return x.GenBatchProofResponse + } + return nil +} + +func (x *ProverMessage) GetGenAggregatedProofResponse() *GenAggregatedProofResponse { + if x, ok := x.GetResponse().(*ProverMessage_GenAggregatedProofResponse); ok { + return x.GenAggregatedProofResponse + } + return nil +} + +func (x *ProverMessage) GetGenFinalProofResponse() *GenFinalProofResponse { + if x, ok := x.GetResponse().(*ProverMessage_GenFinalProofResponse); ok { + return x.GenFinalProofResponse + } + return nil +} + +func (x *ProverMessage) GetCancelResponse() *CancelResponse { + if x, ok := x.GetResponse().(*ProverMessage_CancelResponse); ok { + return x.CancelResponse + } + return nil +} + +func (x *ProverMessage) GetGetProofResponse() *GetProofResponse { + if x, ok := x.GetResponse().(*ProverMessage_GetProofResponse); ok { + return x.GetProofResponse + } + return nil +} + +type isProverMessage_Response interface { + isProverMessage_Response() +} + +type ProverMessage_GetStatusResponse struct { + GetStatusResponse *GetStatusResponse `protobuf:"bytes,2,opt,name=get_status_response,json=getStatusResponse,proto3,oneof"` +} + +type ProverMessage_GenBatchProofResponse struct { + GenBatchProofResponse *GenBatchProofResponse `protobuf:"bytes,3,opt,name=gen_batch_proof_response,json=genBatchProofResponse,proto3,oneof"` +} + +type ProverMessage_GenAggregatedProofResponse struct { + GenAggregatedProofResponse *GenAggregatedProofResponse `protobuf:"bytes,4,opt,name=gen_aggregated_proof_response,json=genAggregatedProofResponse,proto3,oneof"` +} + +type ProverMessage_GenFinalProofResponse struct { + GenFinalProofResponse *GenFinalProofResponse `protobuf:"bytes,5,opt,name=gen_final_proof_response,json=genFinalProofResponse,proto3,oneof"` +} + +type ProverMessage_CancelResponse struct { + CancelResponse *CancelResponse `protobuf:"bytes,6,opt,name=cancel_response,json=cancelResponse,proto3,oneof"` +} + +type ProverMessage_GetProofResponse struct { + GetProofResponse *GetProofResponse `protobuf:"bytes,7,opt,name=get_proof_response,json=getProofResponse,proto3,oneof"` +} + +func (*ProverMessage_GetStatusResponse) isProverMessage_Response() {} + +func (*ProverMessage_GenBatchProofResponse) isProverMessage_Response() {} + +func (*ProverMessage_GenAggregatedProofResponse) isProverMessage_Response() {} + +func (*ProverMessage_GenFinalProofResponse) isProverMessage_Response() {} + +func (*ProverMessage_CancelResponse) isProverMessage_Response() {} + +func (*ProverMessage_GetProofResponse) isProverMessage_Response() {} + +// * +// @dev GetStatusRequest +type GetStatusRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetStatusRequest) Reset() { + *x = GetStatusRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetStatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetStatusRequest) ProtoMessage() {} + +func (x *GetStatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetStatusRequest.ProtoReflect.Descriptor instead. +func (*GetStatusRequest) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{3} +} + +// * +// @dev GenBatchProofRequest +// @param {input} - input prover +type GenBatchProofRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Input *InputProver `protobuf:"bytes,1,opt,name=input,proto3" json:"input,omitempty"` +} + +func (x *GenBatchProofRequest) Reset() { + *x = GenBatchProofRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenBatchProofRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenBatchProofRequest) ProtoMessage() {} + +func (x *GenBatchProofRequest) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenBatchProofRequest.ProtoReflect.Descriptor instead. +func (*GenBatchProofRequest) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{4} +} + +func (x *GenBatchProofRequest) GetInput() *InputProver { + if x != nil { + return x.Input + } + return nil +} + +type GenStatelessBatchProofRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Input *StatelessInputProver `protobuf:"bytes,1,opt,name=input,proto3" json:"input,omitempty"` +} + +func (x *GenStatelessBatchProofRequest) Reset() { + *x = GenStatelessBatchProofRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenStatelessBatchProofRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenStatelessBatchProofRequest) ProtoMessage() {} + +func (x *GenStatelessBatchProofRequest) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenStatelessBatchProofRequest.ProtoReflect.Descriptor instead. +func (*GenStatelessBatchProofRequest) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{5} +} + +func (x *GenStatelessBatchProofRequest) GetInput() *StatelessInputProver { + if x != nil { + return x.Input + } + return nil +} + +// * +// @dev GenAggregatedProofRequest +// @param {recursive_proof_1} - proof json of the first batch to aggregate +// @param {recursive_proof_2} - proof json of the second batch to aggregate +type GenAggregatedProofRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RecursiveProof_1 string `protobuf:"bytes,1,opt,name=recursive_proof_1,json=recursiveProof1,proto3" json:"recursive_proof_1,omitempty"` + RecursiveProof_2 string `protobuf:"bytes,2,opt,name=recursive_proof_2,json=recursiveProof2,proto3" json:"recursive_proof_2,omitempty"` +} + +func (x *GenAggregatedProofRequest) Reset() { + *x = GenAggregatedProofRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenAggregatedProofRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenAggregatedProofRequest) ProtoMessage() {} + +func (x *GenAggregatedProofRequest) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenAggregatedProofRequest.ProtoReflect.Descriptor instead. +func (*GenAggregatedProofRequest) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{6} +} + +func (x *GenAggregatedProofRequest) GetRecursiveProof_1() string { + if x != nil { + return x.RecursiveProof_1 + } + return "" +} + +func (x *GenAggregatedProofRequest) GetRecursiveProof_2() string { + if x != nil { + return x.RecursiveProof_2 + } + return "" +} + +// * +// @dev GenFinalProofRequest +// @param {recursive_proof} - proof json of the batch or aggregated proof to finalise +// @param {aggregator_addr} - address of the aggregator +type GenFinalProofRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RecursiveProof string `protobuf:"bytes,1,opt,name=recursive_proof,json=recursiveProof,proto3" json:"recursive_proof,omitempty"` + AggregatorAddr string `protobuf:"bytes,2,opt,name=aggregator_addr,json=aggregatorAddr,proto3" json:"aggregator_addr,omitempty"` +} + +func (x *GenFinalProofRequest) Reset() { + *x = GenFinalProofRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenFinalProofRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenFinalProofRequest) ProtoMessage() {} + +func (x *GenFinalProofRequest) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenFinalProofRequest.ProtoReflect.Descriptor instead. +func (*GenFinalProofRequest) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{7} +} + +func (x *GenFinalProofRequest) GetRecursiveProof() string { + if x != nil { + return x.RecursiveProof + } + return "" +} + +func (x *GenFinalProofRequest) GetAggregatorAddr() string { + if x != nil { + return x.AggregatorAddr + } + return "" +} + +// * +// @dev CancelRequest +// @param {id} - identifier of the proof request to cancel +type CancelRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *CancelRequest) Reset() { + *x = CancelRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CancelRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CancelRequest) ProtoMessage() {} + +func (x *CancelRequest) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CancelRequest.ProtoReflect.Descriptor instead. +func (*CancelRequest) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{8} +} + +func (x *CancelRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +// * +// @dev Request GetProof +// @param {id} - proof identifier of the proof request +// @param {timeout} - time to wait until the service responds +type GetProofRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Timeout uint64 `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"` +} + +func (x *GetProofRequest) Reset() { + *x = GetProofRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetProofRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProofRequest) ProtoMessage() {} + +func (x *GetProofRequest) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProofRequest.ProtoReflect.Descriptor instead. +func (*GetProofRequest) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{9} +} + +func (x *GetProofRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *GetProofRequest) GetTimeout() uint64 { + if x != nil { + return x.Timeout + } + return 0 +} + +// * +// @dev Response GetStatus +// @param {status} - server status +// - BOOTING: being ready to compute proofs +// - COMPUTING: busy computing a proof +// - IDLE: waiting for a proof to compute +// - HALT: stop +// @param {last_computed_request_id} - last proof identifier that has been computed +// @param {last_computed_end_time} - last proof timestamp when it was finished +// @param {current_computing_request_id} - id of the proof that is being computed +// @param {current_computing_start_time} - timestamp when the proof that is being computed started +// @param {version_proto} - .proto verion +// @param {version_server} - server version +// @param {pending_request_queue_ids} - list of identifierss of proof requests that are in the pending queue +// @param {prover_name} - id of this prover server, normally specified via config.json, or UNSPECIFIED otherwise; it does not change if prover reboots +// @param {prover_id} - id of this prover instance or reboot; it changes if prover reboots; it is a UUID, automatically generated during the initialization +// @param {number_of_cores} - number of cores in the system where the prover is running +// @param {total_memory} - total memory in the system where the prover is running +// @param {free_memory} - free memory in the system where the prover is running +type GetStatusResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status GetStatusResponse_Status `protobuf:"varint,1,opt,name=status,proto3,enum=aggregator.v1.GetStatusResponse_Status" json:"status,omitempty"` + LastComputedRequestId string `protobuf:"bytes,2,opt,name=last_computed_request_id,json=lastComputedRequestId,proto3" json:"last_computed_request_id,omitempty"` + LastComputedEndTime uint64 `protobuf:"varint,3,opt,name=last_computed_end_time,json=lastComputedEndTime,proto3" json:"last_computed_end_time,omitempty"` + CurrentComputingRequestId string `protobuf:"bytes,4,opt,name=current_computing_request_id,json=currentComputingRequestId,proto3" json:"current_computing_request_id,omitempty"` + CurrentComputingStartTime uint64 `protobuf:"varint,5,opt,name=current_computing_start_time,json=currentComputingStartTime,proto3" json:"current_computing_start_time,omitempty"` + VersionProto string `protobuf:"bytes,6,opt,name=version_proto,json=versionProto,proto3" json:"version_proto,omitempty"` + VersionServer string `protobuf:"bytes,7,opt,name=version_server,json=versionServer,proto3" json:"version_server,omitempty"` + PendingRequestQueueIds []string `protobuf:"bytes,8,rep,name=pending_request_queue_ids,json=pendingRequestQueueIds,proto3" json:"pending_request_queue_ids,omitempty"` + ProverName string `protobuf:"bytes,9,opt,name=prover_name,json=proverName,proto3" json:"prover_name,omitempty"` + ProverId string `protobuf:"bytes,10,opt,name=prover_id,json=proverId,proto3" json:"prover_id,omitempty"` + NumberOfCores uint64 `protobuf:"varint,11,opt,name=number_of_cores,json=numberOfCores,proto3" json:"number_of_cores,omitempty"` + TotalMemory uint64 `protobuf:"varint,12,opt,name=total_memory,json=totalMemory,proto3" json:"total_memory,omitempty"` + FreeMemory uint64 `protobuf:"varint,13,opt,name=free_memory,json=freeMemory,proto3" json:"free_memory,omitempty"` + ForkId uint64 `protobuf:"varint,14,opt,name=fork_id,json=forkId,proto3" json:"fork_id,omitempty"` +} + +func (x *GetStatusResponse) Reset() { + *x = GetStatusResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetStatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetStatusResponse) ProtoMessage() {} + +func (x *GetStatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetStatusResponse.ProtoReflect.Descriptor instead. +func (*GetStatusResponse) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{10} +} + +func (x *GetStatusResponse) GetStatus() GetStatusResponse_Status { + if x != nil { + return x.Status + } + return GetStatusResponse_STATUS_UNSPECIFIED +} + +func (x *GetStatusResponse) GetLastComputedRequestId() string { + if x != nil { + return x.LastComputedRequestId + } + return "" +} + +func (x *GetStatusResponse) GetLastComputedEndTime() uint64 { + if x != nil { + return x.LastComputedEndTime + } + return 0 +} + +func (x *GetStatusResponse) GetCurrentComputingRequestId() string { + if x != nil { + return x.CurrentComputingRequestId + } + return "" +} + +func (x *GetStatusResponse) GetCurrentComputingStartTime() uint64 { + if x != nil { + return x.CurrentComputingStartTime + } + return 0 +} + +func (x *GetStatusResponse) GetVersionProto() string { + if x != nil { + return x.VersionProto + } + return "" +} + +func (x *GetStatusResponse) GetVersionServer() string { + if x != nil { + return x.VersionServer + } + return "" +} + +func (x *GetStatusResponse) GetPendingRequestQueueIds() []string { + if x != nil { + return x.PendingRequestQueueIds + } + return nil +} + +func (x *GetStatusResponse) GetProverName() string { + if x != nil { + return x.ProverName + } + return "" +} + +func (x *GetStatusResponse) GetProverId() string { + if x != nil { + return x.ProverId + } + return "" +} + +func (x *GetStatusResponse) GetNumberOfCores() uint64 { + if x != nil { + return x.NumberOfCores + } + return 0 +} + +func (x *GetStatusResponse) GetTotalMemory() uint64 { + if x != nil { + return x.TotalMemory + } + return 0 +} + +func (x *GetStatusResponse) GetFreeMemory() uint64 { + if x != nil { + return x.FreeMemory + } + return 0 +} + +func (x *GetStatusResponse) GetForkId() uint64 { + if x != nil { + return x.ForkId + } + return 0 +} + +// * +// @dev GenBatchProofResponse +// @param {id} - proof identifier, to be used in GetProofRequest() +// @param {result} - request result +type GenBatchProofResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Result Result `protobuf:"varint,2,opt,name=result,proto3,enum=aggregator.v1.Result" json:"result,omitempty"` +} + +func (x *GenBatchProofResponse) Reset() { + *x = GenBatchProofResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenBatchProofResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenBatchProofResponse) ProtoMessage() {} + +func (x *GenBatchProofResponse) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenBatchProofResponse.ProtoReflect.Descriptor instead. +func (*GenBatchProofResponse) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{11} +} + +func (x *GenBatchProofResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *GenBatchProofResponse) GetResult() Result { + if x != nil { + return x.Result + } + return Result_RESULT_UNSPECIFIED +} + +// * +// @dev GenAggregatedProofResponse +// @param {id} - proof identifier, to be used in GetProofRequest() +// @param {result} - request result +type GenAggregatedProofResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Result Result `protobuf:"varint,2,opt,name=result,proto3,enum=aggregator.v1.Result" json:"result,omitempty"` +} + +func (x *GenAggregatedProofResponse) Reset() { + *x = GenAggregatedProofResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenAggregatedProofResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenAggregatedProofResponse) ProtoMessage() {} + +func (x *GenAggregatedProofResponse) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenAggregatedProofResponse.ProtoReflect.Descriptor instead. +func (*GenAggregatedProofResponse) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{12} +} + +func (x *GenAggregatedProofResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *GenAggregatedProofResponse) GetResult() Result { + if x != nil { + return x.Result + } + return Result_RESULT_UNSPECIFIED +} + +// * +// @dev Response GenFinalProof +// @param {id} - proof identifier, to be used in GetProofRequest() +// @param {result} - request result +type GenFinalProofResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Result Result `protobuf:"varint,2,opt,name=result,proto3,enum=aggregator.v1.Result" json:"result,omitempty"` +} + +func (x *GenFinalProofResponse) Reset() { + *x = GenFinalProofResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenFinalProofResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenFinalProofResponse) ProtoMessage() {} + +func (x *GenFinalProofResponse) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenFinalProofResponse.ProtoReflect.Descriptor instead. +func (*GenFinalProofResponse) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{13} +} + +func (x *GenFinalProofResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *GenFinalProofResponse) GetResult() Result { + if x != nil { + return x.Result + } + return Result_RESULT_UNSPECIFIED +} + +// * +// @dev CancelResponse +// @param {result} - request result +type CancelResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Result Result `protobuf:"varint,1,opt,name=result,proto3,enum=aggregator.v1.Result" json:"result,omitempty"` +} + +func (x *CancelResponse) Reset() { + *x = CancelResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CancelResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CancelResponse) ProtoMessage() {} + +func (x *CancelResponse) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CancelResponse.ProtoReflect.Descriptor instead. +func (*CancelResponse) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{14} +} + +func (x *CancelResponse) GetResult() Result { + if x != nil { + return x.Result + } + return Result_RESULT_UNSPECIFIED +} + +// * +// @dev GetProofResponse +// @param {id} - proof identifier +// @param {final_proof} - groth16 proof + public circuit inputs +// @param {recursive_proof} - recursive proof json +// @param {result} - proof result +// - COMPLETED_OK: proof has been computed successfully and it is valid +// - ERROR: request error +// - COMPLETED_ERROR: proof has been computed successfully and it is not valid +// - PENDING: proof is being computed +// - INTERNAL_ERROR: server error during proof computation +// - CANCEL: proof has been cancelled +// +// @param {result_string} - extends result information +type GetProofResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // Types that are assignable to Proof: + // + // *GetProofResponse_FinalProof + // *GetProofResponse_RecursiveProof + Proof isGetProofResponse_Proof `protobuf_oneof:"proof"` + Result GetProofResponse_Result `protobuf:"varint,4,opt,name=result,proto3,enum=aggregator.v1.GetProofResponse_Result" json:"result,omitempty"` + ResultString string `protobuf:"bytes,5,opt,name=result_string,json=resultString,proto3" json:"result_string,omitempty"` +} + +func (x *GetProofResponse) Reset() { + *x = GetProofResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetProofResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProofResponse) ProtoMessage() {} + +func (x *GetProofResponse) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProofResponse.ProtoReflect.Descriptor instead. +func (*GetProofResponse) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{15} +} + +func (x *GetProofResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (m *GetProofResponse) GetProof() isGetProofResponse_Proof { + if m != nil { + return m.Proof + } + return nil +} + +func (x *GetProofResponse) GetFinalProof() *FinalProof { + if x, ok := x.GetProof().(*GetProofResponse_FinalProof); ok { + return x.FinalProof + } + return nil +} + +func (x *GetProofResponse) GetRecursiveProof() string { + if x, ok := x.GetProof().(*GetProofResponse_RecursiveProof); ok { + return x.RecursiveProof + } + return "" +} + +func (x *GetProofResponse) GetResult() GetProofResponse_Result { + if x != nil { + return x.Result + } + return GetProofResponse_RESULT_UNSPECIFIED +} + +func (x *GetProofResponse) GetResultString() string { + if x != nil { + return x.ResultString + } + return "" +} + +type isGetProofResponse_Proof interface { + isGetProofResponse_Proof() +} + +type GetProofResponse_FinalProof struct { + FinalProof *FinalProof `protobuf:"bytes,2,opt,name=final_proof,json=finalProof,proto3,oneof"` +} + +type GetProofResponse_RecursiveProof struct { + RecursiveProof string `protobuf:"bytes,3,opt,name=recursive_proof,json=recursiveProof,proto3,oneof"` +} + +func (*GetProofResponse_FinalProof) isGetProofResponse_Proof() {} + +func (*GetProofResponse_RecursiveProof) isGetProofResponse_Proof() {} + +// @dev FinalProof +// @param {proof} - groth16 proof +// @param {public} - public circuit inputs +type FinalProof struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Proof string `protobuf:"bytes,1,opt,name=proof,proto3" json:"proof,omitempty"` + Public *PublicInputsExtended `protobuf:"bytes,2,opt,name=public,proto3" json:"public,omitempty"` +} + +func (x *FinalProof) Reset() { + *x = FinalProof{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FinalProof) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FinalProof) ProtoMessage() {} + +func (x *FinalProof) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FinalProof.ProtoReflect.Descriptor instead. +func (*FinalProof) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{16} +} + +func (x *FinalProof) GetProof() string { + if x != nil { + return x.Proof + } + return "" +} + +func (x *FinalProof) GetPublic() *PublicInputsExtended { + if x != nil { + return x.Public + } + return nil +} + +// @dev PublicInputs +// @param {old_state_root} +// @param {old_acc_input_hash} +// @param {old_batch_num} +// @param {chain_id} +// @param {batch_l2_data} +// @param {global_exit_root} +// @param {sequencer_addr} +// @param {aggregator_addr} +type PublicInputs struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OldStateRoot []byte `protobuf:"bytes,1,opt,name=old_state_root,json=oldStateRoot,proto3" json:"old_state_root,omitempty"` + OldAccInputHash []byte `protobuf:"bytes,2,opt,name=old_acc_input_hash,json=oldAccInputHash,proto3" json:"old_acc_input_hash,omitempty"` + OldBatchNum uint64 `protobuf:"varint,3,opt,name=old_batch_num,json=oldBatchNum,proto3" json:"old_batch_num,omitempty"` + ChainId uint64 `protobuf:"varint,4,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + ForkId uint64 `protobuf:"varint,5,opt,name=fork_id,json=forkId,proto3" json:"fork_id,omitempty"` + BatchL2Data []byte `protobuf:"bytes,6,opt,name=batch_l2_data,json=batchL2Data,proto3" json:"batch_l2_data,omitempty"` + L1InfoRoot []byte `protobuf:"bytes,7,opt,name=l1_info_root,json=l1InfoRoot,proto3" json:"l1_info_root,omitempty"` + TimestampLimit uint64 `protobuf:"varint,8,opt,name=timestamp_limit,json=timestampLimit,proto3" json:"timestamp_limit,omitempty"` + SequencerAddr string `protobuf:"bytes,9,opt,name=sequencer_addr,json=sequencerAddr,proto3" json:"sequencer_addr,omitempty"` + ForcedBlockhashL1 []byte `protobuf:"bytes,10,opt,name=forced_blockhash_l1,json=forcedBlockhashL1,proto3" json:"forced_blockhash_l1,omitempty"` + AggregatorAddr string `protobuf:"bytes,12,opt,name=aggregator_addr,json=aggregatorAddr,proto3" json:"aggregator_addr,omitempty"` + L1InfoTreeData map[uint32]*L1Data `protobuf:"bytes,16,rep,name=l1_info_tree_data,json=l1InfoTreeData,proto3" json:"l1_info_tree_data,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *PublicInputs) Reset() { + *x = PublicInputs{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PublicInputs) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PublicInputs) ProtoMessage() {} + +func (x *PublicInputs) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PublicInputs.ProtoReflect.Descriptor instead. +func (*PublicInputs) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{17} +} + +func (x *PublicInputs) GetOldStateRoot() []byte { + if x != nil { + return x.OldStateRoot + } + return nil +} + +func (x *PublicInputs) GetOldAccInputHash() []byte { + if x != nil { + return x.OldAccInputHash + } + return nil +} + +func (x *PublicInputs) GetOldBatchNum() uint64 { + if x != nil { + return x.OldBatchNum + } + return 0 +} + +func (x *PublicInputs) GetChainId() uint64 { + if x != nil { + return x.ChainId + } + return 0 +} + +func (x *PublicInputs) GetForkId() uint64 { + if x != nil { + return x.ForkId + } + return 0 +} + +func (x *PublicInputs) GetBatchL2Data() []byte { + if x != nil { + return x.BatchL2Data + } + return nil +} + +func (x *PublicInputs) GetL1InfoRoot() []byte { + if x != nil { + return x.L1InfoRoot + } + return nil +} + +func (x *PublicInputs) GetTimestampLimit() uint64 { + if x != nil { + return x.TimestampLimit + } + return 0 +} + +func (x *PublicInputs) GetSequencerAddr() string { + if x != nil { + return x.SequencerAddr + } + return "" +} + +func (x *PublicInputs) GetForcedBlockhashL1() []byte { + if x != nil { + return x.ForcedBlockhashL1 + } + return nil +} + +func (x *PublicInputs) GetAggregatorAddr() string { + if x != nil { + return x.AggregatorAddr + } + return "" +} + +func (x *PublicInputs) GetL1InfoTreeData() map[uint32]*L1Data { + if x != nil { + return x.L1InfoTreeData + } + return nil +} + +type StatelessPublicInputs struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Witness []byte `protobuf:"bytes,1,opt,name=witness,proto3" json:"witness,omitempty"` + OldAccInputHash []byte `protobuf:"bytes,2,opt,name=old_acc_input_hash,json=oldAccInputHash,proto3" json:"old_acc_input_hash,omitempty"` + OldBatchNum uint64 `protobuf:"varint,3,opt,name=old_batch_num,json=oldBatchNum,proto3" json:"old_batch_num,omitempty"` + ChainId uint64 `protobuf:"varint,4,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + ForkId uint64 `protobuf:"varint,5,opt,name=fork_id,json=forkId,proto3" json:"fork_id,omitempty"` + BatchL2Data []byte `protobuf:"bytes,6,opt,name=batch_l2_data,json=batchL2Data,proto3" json:"batch_l2_data,omitempty"` + L1InfoRoot []byte `protobuf:"bytes,7,opt,name=l1_info_root,json=l1InfoRoot,proto3" json:"l1_info_root,omitempty"` + TimestampLimit uint64 `protobuf:"varint,8,opt,name=timestamp_limit,json=timestampLimit,proto3" json:"timestamp_limit,omitempty"` + SequencerAddr string `protobuf:"bytes,9,opt,name=sequencer_addr,json=sequencerAddr,proto3" json:"sequencer_addr,omitempty"` + ForcedBlockhashL1 []byte `protobuf:"bytes,10,opt,name=forced_blockhash_l1,json=forcedBlockhashL1,proto3" json:"forced_blockhash_l1,omitempty"` + AggregatorAddr string `protobuf:"bytes,11,opt,name=aggregator_addr,json=aggregatorAddr,proto3" json:"aggregator_addr,omitempty"` + L1InfoTreeData map[uint32]*L1Data `protobuf:"bytes,12,rep,name=l1_info_tree_data,json=l1InfoTreeData,proto3" json:"l1_info_tree_data,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *StatelessPublicInputs) Reset() { + *x = StatelessPublicInputs{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatelessPublicInputs) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatelessPublicInputs) ProtoMessage() {} + +func (x *StatelessPublicInputs) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatelessPublicInputs.ProtoReflect.Descriptor instead. +func (*StatelessPublicInputs) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{18} +} + +func (x *StatelessPublicInputs) GetWitness() []byte { + if x != nil { + return x.Witness + } + return nil +} + +func (x *StatelessPublicInputs) GetOldAccInputHash() []byte { + if x != nil { + return x.OldAccInputHash + } + return nil +} + +func (x *StatelessPublicInputs) GetOldBatchNum() uint64 { + if x != nil { + return x.OldBatchNum + } + return 0 +} + +func (x *StatelessPublicInputs) GetChainId() uint64 { + if x != nil { + return x.ChainId + } + return 0 +} + +func (x *StatelessPublicInputs) GetForkId() uint64 { + if x != nil { + return x.ForkId + } + return 0 +} + +func (x *StatelessPublicInputs) GetBatchL2Data() []byte { + if x != nil { + return x.BatchL2Data + } + return nil +} + +func (x *StatelessPublicInputs) GetL1InfoRoot() []byte { + if x != nil { + return x.L1InfoRoot + } + return nil +} + +func (x *StatelessPublicInputs) GetTimestampLimit() uint64 { + if x != nil { + return x.TimestampLimit + } + return 0 +} + +func (x *StatelessPublicInputs) GetSequencerAddr() string { + if x != nil { + return x.SequencerAddr + } + return "" +} + +func (x *StatelessPublicInputs) GetForcedBlockhashL1() []byte { + if x != nil { + return x.ForcedBlockhashL1 + } + return nil +} + +func (x *StatelessPublicInputs) GetAggregatorAddr() string { + if x != nil { + return x.AggregatorAddr + } + return "" +} + +func (x *StatelessPublicInputs) GetL1InfoTreeData() map[uint32]*L1Data { + if x != nil { + return x.L1InfoTreeData + } + return nil +} + +// l1InfoTree leaf values +type L1Data struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GlobalExitRoot []byte `protobuf:"bytes,1,opt,name=global_exit_root,json=globalExitRoot,proto3" json:"global_exit_root,omitempty"` + BlockhashL1 []byte `protobuf:"bytes,2,opt,name=blockhash_l1,json=blockhashL1,proto3" json:"blockhash_l1,omitempty"` + MinTimestamp uint32 `protobuf:"varint,3,opt,name=min_timestamp,json=minTimestamp,proto3" json:"min_timestamp,omitempty"` + SmtProof [][]byte `protobuf:"bytes,4,rep,name=smt_proof,json=smtProof,proto3" json:"smt_proof,omitempty"` +} + +func (x *L1Data) Reset() { + *x = L1Data{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *L1Data) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*L1Data) ProtoMessage() {} + +func (x *L1Data) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use L1Data.ProtoReflect.Descriptor instead. +func (*L1Data) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{19} +} + +func (x *L1Data) GetGlobalExitRoot() []byte { + if x != nil { + return x.GlobalExitRoot + } + return nil +} + +func (x *L1Data) GetBlockhashL1() []byte { + if x != nil { + return x.BlockhashL1 + } + return nil +} + +func (x *L1Data) GetMinTimestamp() uint32 { + if x != nil { + return x.MinTimestamp + } + return 0 +} + +func (x *L1Data) GetSmtProof() [][]byte { + if x != nil { + return x.SmtProof + } + return nil +} + +// * +// @dev InputProver +// @param {public_inputs} - public inputs +// @param {db} - database containing all key-values in smt matching the old state root +// @param {contracts_bytecode} - key is the hash(contractBytecode), value is the bytecode itself +type InputProver struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PublicInputs *PublicInputs `protobuf:"bytes,1,opt,name=public_inputs,json=publicInputs,proto3" json:"public_inputs,omitempty"` + Db map[string]string `protobuf:"bytes,4,rep,name=db,proto3" json:"db,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // For debug/testing purpposes only. Don't fill this on production + ContractsBytecode map[string]string `protobuf:"bytes,5,rep,name=contracts_bytecode,json=contractsBytecode,proto3" json:"contracts_bytecode,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // For debug/testing purpposes only. Don't fill this on production +} + +func (x *InputProver) Reset() { + *x = InputProver{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InputProver) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InputProver) ProtoMessage() {} + +func (x *InputProver) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InputProver.ProtoReflect.Descriptor instead. +func (*InputProver) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{20} +} + +func (x *InputProver) GetPublicInputs() *PublicInputs { + if x != nil { + return x.PublicInputs + } + return nil +} + +func (x *InputProver) GetDb() map[string]string { + if x != nil { + return x.Db + } + return nil +} + +func (x *InputProver) GetContractsBytecode() map[string]string { + if x != nil { + return x.ContractsBytecode + } + return nil +} + +type StatelessInputProver struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PublicInputs *StatelessPublicInputs `protobuf:"bytes,1,opt,name=public_inputs,json=publicInputs,proto3" json:"public_inputs,omitempty"` +} + +func (x *StatelessInputProver) Reset() { + *x = StatelessInputProver{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatelessInputProver) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatelessInputProver) ProtoMessage() {} + +func (x *StatelessInputProver) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatelessInputProver.ProtoReflect.Descriptor instead. +func (*StatelessInputProver) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{21} +} + +func (x *StatelessInputProver) GetPublicInputs() *StatelessPublicInputs { + if x != nil { + return x.PublicInputs + } + return nil +} + +// * +// @dev PublicInputsExtended +// @param {public_inputs} - public inputs +// @param {new_state_root} - final state root. Used as a sanity check. +// @param {new_acc_input_hash} - final accumulate input hash. Used as a sanity check. +// @param {new_local_exit_root} - new local exit root. Used as a sanity check. +// @param {new_batch_num} - final num batch. Used as a sanity check. +type PublicInputsExtended struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PublicInputs *PublicInputs `protobuf:"bytes,1,opt,name=public_inputs,json=publicInputs,proto3" json:"public_inputs,omitempty"` + NewStateRoot []byte `protobuf:"bytes,2,opt,name=new_state_root,json=newStateRoot,proto3" json:"new_state_root,omitempty"` + NewAccInputHash []byte `protobuf:"bytes,3,opt,name=new_acc_input_hash,json=newAccInputHash,proto3" json:"new_acc_input_hash,omitempty"` + NewLocalExitRoot []byte `protobuf:"bytes,4,opt,name=new_local_exit_root,json=newLocalExitRoot,proto3" json:"new_local_exit_root,omitempty"` + NewBatchNum uint64 `protobuf:"varint,5,opt,name=new_batch_num,json=newBatchNum,proto3" json:"new_batch_num,omitempty"` +} + +func (x *PublicInputsExtended) Reset() { + *x = PublicInputsExtended{} + if protoimpl.UnsafeEnabled { + mi := &file_aggregator_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PublicInputsExtended) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PublicInputsExtended) ProtoMessage() {} + +func (x *PublicInputsExtended) ProtoReflect() protoreflect.Message { + mi := &file_aggregator_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PublicInputsExtended.ProtoReflect.Descriptor instead. +func (*PublicInputsExtended) Descriptor() ([]byte, []int) { + return file_aggregator_proto_rawDescGZIP(), []int{22} +} + +func (x *PublicInputsExtended) GetPublicInputs() *PublicInputs { + if x != nil { + return x.PublicInputs + } + return nil +} + +func (x *PublicInputsExtended) GetNewStateRoot() []byte { + if x != nil { + return x.NewStateRoot + } + return nil +} + +func (x *PublicInputsExtended) GetNewAccInputHash() []byte { + if x != nil { + return x.NewAccInputHash + } + return nil +} + +func (x *PublicInputsExtended) GetNewLocalExitRoot() []byte { + if x != nil { + return x.NewLocalExitRoot + } + return nil +} + +func (x *PublicInputsExtended) GetNewBatchNum() uint64 { + if x != nil { + return x.NewBatchNum + } + return 0 +} + +var File_aggregator_proto protoreflect.FileDescriptor + +var file_aggregator_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0d, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, + 0x31, 0x22, 0x1f, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x06, + 0x76, 0x30, 0x5f, 0x30, 0x5f, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x76, 0x30, + 0x30, 0x31, 0x22, 0xb7, 0x05, 0x0a, 0x11, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, + 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x4f, 0x0a, 0x12, 0x67, 0x65, 0x74, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, + 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x10, 0x67, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5c, 0x0a, 0x17, 0x67, 0x65, 0x6e, + 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, + 0x00, 0x52, 0x14, 0x67, 0x65, 0x6e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6b, 0x0a, 0x1c, 0x67, 0x65, 0x6e, 0x5f, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, + 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x6e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x19, 0x67, 0x65, 0x6e, 0x41, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x5c, 0x0a, 0x17, 0x67, 0x65, 0x6e, 0x5f, 0x66, 0x69, 0x6e, 0x61, + 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x14, 0x67, 0x65, + 0x6e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x45, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x11, 0x67, 0x65, 0x74, + 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, + 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x67, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x78, 0x0a, 0x21, 0x67, 0x65, 0x6e, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x70, + 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x48, 0x00, 0x52, 0x1d, 0x67, 0x65, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, + 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xcc, 0x04, 0x0a, + 0x0d, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x52, + 0x0a, 0x13, 0x67, 0x65, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x72, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, + 0x11, 0x67, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x18, 0x67, 0x65, 0x6e, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, + 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, + 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, + 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x15, 0x67, 0x65, + 0x6e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x1d, 0x67, 0x65, 0x6e, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x41, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x1a, 0x67, 0x65, 0x6e, 0x41, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x18, 0x67, 0x65, 0x6e, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, + 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x15, 0x67, + 0x65, 0x6e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x5f, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0e, + 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, + 0x0a, 0x12, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x10, 0x67, + 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, + 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x47, + 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x48, 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x50, 0x72, 0x6f, 0x76, + 0x65, 0x72, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x5a, 0x0a, 0x1d, 0x47, 0x65, 0x6e, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x05, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6c, + 0x65, 0x73, 0x73, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x52, 0x05, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x73, 0x0a, 0x19, 0x47, 0x65, 0x6e, 0x41, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x5f, + 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x5f, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, + 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x31, 0x12, 0x2a, + 0x0a, 0x11, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x6f, + 0x66, 0x5f, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x75, 0x72, + 0x73, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x32, 0x22, 0x68, 0x0a, 0x14, 0x47, 0x65, + 0x6e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x5f, + 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x63, + 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x27, 0x0a, 0x0f, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, + 0x41, 0x64, 0x64, 0x72, 0x22, 0x1f, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6f, + 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x22, 0xfc, 0x05, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x6c, 0x61, 0x73, + 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x49, 0x64, 0x12, 0x33, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x75, + 0x74, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x13, 0x6c, 0x61, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x64, + 0x45, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x19, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x69, 0x6e, 0x67, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x25, + 0x0a, 0x0e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x19, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x64, 0x73, + 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x49, 0x64, 0x12, 0x26, + 0x0a, 0x0f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6f, 0x66, 0x5f, 0x63, 0x6f, 0x72, 0x65, + 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4f, + 0x66, 0x43, 0x6f, 0x72, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, + 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x72, 0x65, + 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, + 0x66, 0x72, 0x65, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x6f, + 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x66, 0x6f, 0x72, + 0x6b, 0x49, 0x64, 0x22, 0x6c, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, + 0x12, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x42, 0x4f, 0x4f, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, + 0x0f, 0x0a, 0x0b, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x03, + 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x48, 0x41, 0x4c, 0x54, 0x10, + 0x04, 0x22, 0x56, 0x0a, 0x15, 0x47, 0x65, 0x6e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, + 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2d, 0x0a, 0x06, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x5b, 0x0a, 0x1a, 0x47, 0x65, 0x6e, + 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2d, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x56, 0x0a, 0x15, 0x47, 0x65, 0x6e, 0x46, 0x69, 0x6e, + 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x2d, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x15, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x3f, + 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2d, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x15, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, + 0xa5, 0x03, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x3c, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, + 0x6f, 0x6f, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x50, + 0x72, 0x6f, 0x6f, 0x66, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, + 0x6f, 0x66, 0x12, 0x29, 0x0a, 0x0f, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x5f, + 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x72, + 0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x3e, 0x0a, + 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, + 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x23, 0x0a, + 0x0d, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x22, 0xa9, 0x01, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, + 0x12, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, + 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x5f, 0x4f, 0x4b, 0x10, 0x01, 0x12, 0x10, + 0x0a, 0x0c, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, + 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4c, + 0x45, 0x54, 0x45, 0x44, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, + 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x04, + 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, + 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x52, + 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x10, 0x06, 0x42, 0x07, + 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x5f, 0x0a, 0x0a, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x3b, 0x0a, 0x06, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x52, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x22, 0xde, 0x04, 0x0a, 0x0c, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x6f, 0x6c, 0x64, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0c, 0x6f, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, + 0x2b, 0x0a, 0x12, 0x6f, 0x6c, 0x64, 0x5f, 0x61, 0x63, 0x63, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x6f, 0x6c, 0x64, + 0x41, 0x63, 0x63, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x0d, + 0x6f, 0x6c, 0x64, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6f, 0x6c, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4e, 0x75, 0x6d, + 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x66, + 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x66, 0x6f, + 0x72, 0x6b, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x32, + 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x61, 0x74, + 0x63, 0x68, 0x4c, 0x32, 0x44, 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0c, 0x6c, 0x31, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, + 0x6c, 0x31, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x72, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x63, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x6f, + 0x72, 0x63, 0x65, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x6c, + 0x31, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x68, 0x61, 0x73, 0x68, 0x4c, 0x31, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x41, + 0x64, 0x64, 0x72, 0x12, 0x5a, 0x0a, 0x11, 0x6c, 0x31, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x74, + 0x72, 0x65, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, + 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x2e, 0x4c, 0x31, 0x49, 0x6e, + 0x66, 0x6f, 0x54, 0x72, 0x65, 0x65, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0e, 0x6c, 0x31, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x72, 0x65, 0x65, 0x44, 0x61, 0x74, 0x61, 0x1a, + 0x58, 0x0a, 0x13, 0x4c, 0x31, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x72, 0x65, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe4, 0x04, 0x0a, 0x15, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x2b, 0x0a, + 0x12, 0x6f, 0x6c, 0x64, 0x5f, 0x61, 0x63, 0x63, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x6f, 0x6c, 0x64, 0x41, 0x63, + 0x63, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x6c, + 0x64, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0b, 0x6f, 0x6c, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4e, 0x75, 0x6d, 0x12, 0x19, + 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x6f, 0x72, + 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6b, + 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6c, 0x32, 0x5f, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x4c, 0x32, 0x44, 0x61, 0x74, 0x61, 0x12, 0x20, 0x0a, 0x0c, 0x6c, 0x31, 0x5f, 0x69, 0x6e, 0x66, + 0x6f, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6c, 0x31, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x72, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x71, 0x75, 0x65, + 0x6e, 0x63, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x6f, 0x72, 0x63, + 0x65, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x6c, 0x31, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x68, 0x61, 0x73, 0x68, 0x4c, 0x31, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x64, 0x64, + 0x72, 0x12, 0x63, 0x0a, 0x11, 0x6c, 0x31, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x74, 0x72, 0x65, + 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x73, 0x2e, 0x4c, 0x31, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x72, 0x65, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x6c, 0x31, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x72, + 0x65, 0x65, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x58, 0x0a, 0x13, 0x4c, 0x31, 0x49, 0x6e, 0x66, 0x6f, + 0x54, 0x72, 0x65, 0x65, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x97, 0x01, 0x0a, 0x06, 0x4c, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x28, 0x0a, 0x10, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x45, 0x78, 0x69, + 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x68, 0x61, + 0x73, 0x68, 0x5f, 0x6c, 0x31, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x68, 0x61, 0x73, 0x68, 0x4c, 0x31, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0c, 0x6d, 0x69, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1b, 0x0a, + 0x09, 0x73, 0x6d, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, + 0x52, 0x08, 0x73, 0x6d, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0xe2, 0x02, 0x0a, 0x0b, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0d, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x52, 0x0c, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x02, + 0x64, 0x62, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x50, 0x72, + 0x6f, 0x76, 0x65, 0x72, 0x2e, 0x44, 0x62, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x02, 0x64, 0x62, + 0x12, 0x60, 0x0a, 0x12, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x5f, 0x62, 0x79, + 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, + 0x74, 0x73, 0x42, 0x79, 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x42, 0x79, 0x74, 0x65, 0x63, 0x6f, + 0x64, 0x65, 0x1a, 0x35, 0x0a, 0x07, 0x44, 0x62, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x44, 0x0a, 0x16, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x42, 0x79, 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0x61, 0x0a, 0x14, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x12, 0x49, 0x0a, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, + 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x73, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x73, 0x22, 0xfe, 0x01, 0x0a, 0x14, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x73, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x40, 0x0a, 0x0d, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x52, + 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x24, 0x0a, + 0x0e, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x6f, 0x6f, 0x74, 0x12, 0x2b, 0x0a, 0x12, 0x6e, 0x65, 0x77, 0x5f, 0x61, 0x63, 0x63, 0x5f, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0f, 0x6e, 0x65, 0x77, 0x41, 0x63, 0x63, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x48, 0x61, 0x73, 0x68, + 0x12, 0x2d, 0x0a, 0x13, 0x6e, 0x65, 0x77, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x65, 0x78, + 0x69, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x6e, + 0x65, 0x77, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x45, 0x78, 0x69, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, + 0x22, 0x0a, 0x0d, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6e, 0x75, 0x6d, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6e, 0x65, 0x77, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x4e, 0x75, 0x6d, 0x2a, 0x5c, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, + 0x12, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, + 0x4f, 0x4b, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x45, + 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, + 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, + 0x03, 0x32, 0x64, 0x0a, 0x11, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4f, 0x0a, 0x07, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x12, 0x1c, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, + 0x20, 0x2e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x78, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x48, + 0x65, 0x72, 0x6d, 0x65, 0x7a, 0x2f, 0x7a, 0x6b, 0x65, 0x76, 0x6d, 0x2d, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_aggregator_proto_rawDescOnce sync.Once + file_aggregator_proto_rawDescData = file_aggregator_proto_rawDesc +) + +func file_aggregator_proto_rawDescGZIP() []byte { + file_aggregator_proto_rawDescOnce.Do(func() { + file_aggregator_proto_rawDescData = protoimpl.X.CompressGZIP(file_aggregator_proto_rawDescData) + }) + return file_aggregator_proto_rawDescData +} + +var file_aggregator_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_aggregator_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_aggregator_proto_goTypes = []interface{}{ + (Result)(0), // 0: aggregator.v1.Result + (GetStatusResponse_Status)(0), // 1: aggregator.v1.GetStatusResponse.Status + (GetProofResponse_Result)(0), // 2: aggregator.v1.GetProofResponse.Result + (*Version)(nil), // 3: aggregator.v1.Version + (*AggregatorMessage)(nil), // 4: aggregator.v1.AggregatorMessage + (*ProverMessage)(nil), // 5: aggregator.v1.ProverMessage + (*GetStatusRequest)(nil), // 6: aggregator.v1.GetStatusRequest + (*GenBatchProofRequest)(nil), // 7: aggregator.v1.GenBatchProofRequest + (*GenStatelessBatchProofRequest)(nil), // 8: aggregator.v1.GenStatelessBatchProofRequest + (*GenAggregatedProofRequest)(nil), // 9: aggregator.v1.GenAggregatedProofRequest + (*GenFinalProofRequest)(nil), // 10: aggregator.v1.GenFinalProofRequest + (*CancelRequest)(nil), // 11: aggregator.v1.CancelRequest + (*GetProofRequest)(nil), // 12: aggregator.v1.GetProofRequest + (*GetStatusResponse)(nil), // 13: aggregator.v1.GetStatusResponse + (*GenBatchProofResponse)(nil), // 14: aggregator.v1.GenBatchProofResponse + (*GenAggregatedProofResponse)(nil), // 15: aggregator.v1.GenAggregatedProofResponse + (*GenFinalProofResponse)(nil), // 16: aggregator.v1.GenFinalProofResponse + (*CancelResponse)(nil), // 17: aggregator.v1.CancelResponse + (*GetProofResponse)(nil), // 18: aggregator.v1.GetProofResponse + (*FinalProof)(nil), // 19: aggregator.v1.FinalProof + (*PublicInputs)(nil), // 20: aggregator.v1.PublicInputs + (*StatelessPublicInputs)(nil), // 21: aggregator.v1.StatelessPublicInputs + (*L1Data)(nil), // 22: aggregator.v1.L1Data + (*InputProver)(nil), // 23: aggregator.v1.InputProver + (*StatelessInputProver)(nil), // 24: aggregator.v1.StatelessInputProver + (*PublicInputsExtended)(nil), // 25: aggregator.v1.PublicInputsExtended + nil, // 26: aggregator.v1.PublicInputs.L1InfoTreeDataEntry + nil, // 27: aggregator.v1.StatelessPublicInputs.L1InfoTreeDataEntry + nil, // 28: aggregator.v1.InputProver.DbEntry + nil, // 29: aggregator.v1.InputProver.ContractsBytecodeEntry +} +var file_aggregator_proto_depIdxs = []int32{ + 6, // 0: aggregator.v1.AggregatorMessage.get_status_request:type_name -> aggregator.v1.GetStatusRequest + 7, // 1: aggregator.v1.AggregatorMessage.gen_batch_proof_request:type_name -> aggregator.v1.GenBatchProofRequest + 9, // 2: aggregator.v1.AggregatorMessage.gen_aggregated_proof_request:type_name -> aggregator.v1.GenAggregatedProofRequest + 10, // 3: aggregator.v1.AggregatorMessage.gen_final_proof_request:type_name -> aggregator.v1.GenFinalProofRequest + 11, // 4: aggregator.v1.AggregatorMessage.cancel_request:type_name -> aggregator.v1.CancelRequest + 12, // 5: aggregator.v1.AggregatorMessage.get_proof_request:type_name -> aggregator.v1.GetProofRequest + 8, // 6: aggregator.v1.AggregatorMessage.gen_stateless_batch_proof_request:type_name -> aggregator.v1.GenStatelessBatchProofRequest + 13, // 7: aggregator.v1.ProverMessage.get_status_response:type_name -> aggregator.v1.GetStatusResponse + 14, // 8: aggregator.v1.ProverMessage.gen_batch_proof_response:type_name -> aggregator.v1.GenBatchProofResponse + 15, // 9: aggregator.v1.ProverMessage.gen_aggregated_proof_response:type_name -> aggregator.v1.GenAggregatedProofResponse + 16, // 10: aggregator.v1.ProverMessage.gen_final_proof_response:type_name -> aggregator.v1.GenFinalProofResponse + 17, // 11: aggregator.v1.ProverMessage.cancel_response:type_name -> aggregator.v1.CancelResponse + 18, // 12: aggregator.v1.ProverMessage.get_proof_response:type_name -> aggregator.v1.GetProofResponse + 23, // 13: aggregator.v1.GenBatchProofRequest.input:type_name -> aggregator.v1.InputProver + 24, // 14: aggregator.v1.GenStatelessBatchProofRequest.input:type_name -> aggregator.v1.StatelessInputProver + 1, // 15: aggregator.v1.GetStatusResponse.status:type_name -> aggregator.v1.GetStatusResponse.Status + 0, // 16: aggregator.v1.GenBatchProofResponse.result:type_name -> aggregator.v1.Result + 0, // 17: aggregator.v1.GenAggregatedProofResponse.result:type_name -> aggregator.v1.Result + 0, // 18: aggregator.v1.GenFinalProofResponse.result:type_name -> aggregator.v1.Result + 0, // 19: aggregator.v1.CancelResponse.result:type_name -> aggregator.v1.Result + 19, // 20: aggregator.v1.GetProofResponse.final_proof:type_name -> aggregator.v1.FinalProof + 2, // 21: aggregator.v1.GetProofResponse.result:type_name -> aggregator.v1.GetProofResponse.Result + 25, // 22: aggregator.v1.FinalProof.public:type_name -> aggregator.v1.PublicInputsExtended + 26, // 23: aggregator.v1.PublicInputs.l1_info_tree_data:type_name -> aggregator.v1.PublicInputs.L1InfoTreeDataEntry + 27, // 24: aggregator.v1.StatelessPublicInputs.l1_info_tree_data:type_name -> aggregator.v1.StatelessPublicInputs.L1InfoTreeDataEntry + 20, // 25: aggregator.v1.InputProver.public_inputs:type_name -> aggregator.v1.PublicInputs + 28, // 26: aggregator.v1.InputProver.db:type_name -> aggregator.v1.InputProver.DbEntry + 29, // 27: aggregator.v1.InputProver.contracts_bytecode:type_name -> aggregator.v1.InputProver.ContractsBytecodeEntry + 21, // 28: aggregator.v1.StatelessInputProver.public_inputs:type_name -> aggregator.v1.StatelessPublicInputs + 20, // 29: aggregator.v1.PublicInputsExtended.public_inputs:type_name -> aggregator.v1.PublicInputs + 22, // 30: aggregator.v1.PublicInputs.L1InfoTreeDataEntry.value:type_name -> aggregator.v1.L1Data + 22, // 31: aggregator.v1.StatelessPublicInputs.L1InfoTreeDataEntry.value:type_name -> aggregator.v1.L1Data + 5, // 32: aggregator.v1.AggregatorService.Channel:input_type -> aggregator.v1.ProverMessage + 4, // 33: aggregator.v1.AggregatorService.Channel:output_type -> aggregator.v1.AggregatorMessage + 33, // [33:34] is the sub-list for method output_type + 32, // [32:33] is the sub-list for method input_type + 32, // [32:32] is the sub-list for extension type_name + 32, // [32:32] is the sub-list for extension extendee + 0, // [0:32] is the sub-list for field type_name +} + +func init() { file_aggregator_proto_init() } +func file_aggregator_proto_init() { + if File_aggregator_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_aggregator_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Version); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AggregatorMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProverMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatusRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenBatchProofRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenStatelessBatchProofRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenAggregatedProofRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenFinalProofRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CancelRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProofRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetStatusResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenBatchProofResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenAggregatedProofResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenFinalProofResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CancelResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProofResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FinalProof); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PublicInputs); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatelessPublicInputs); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*L1Data); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InputProver); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatelessInputProver); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_aggregator_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PublicInputsExtended); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_aggregator_proto_msgTypes[1].OneofWrappers = []interface{}{ + (*AggregatorMessage_GetStatusRequest)(nil), + (*AggregatorMessage_GenBatchProofRequest)(nil), + (*AggregatorMessage_GenAggregatedProofRequest)(nil), + (*AggregatorMessage_GenFinalProofRequest)(nil), + (*AggregatorMessage_CancelRequest)(nil), + (*AggregatorMessage_GetProofRequest)(nil), + (*AggregatorMessage_GenStatelessBatchProofRequest)(nil), + } + file_aggregator_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*ProverMessage_GetStatusResponse)(nil), + (*ProverMessage_GenBatchProofResponse)(nil), + (*ProverMessage_GenAggregatedProofResponse)(nil), + (*ProverMessage_GenFinalProofResponse)(nil), + (*ProverMessage_CancelResponse)(nil), + (*ProverMessage_GetProofResponse)(nil), + } + file_aggregator_proto_msgTypes[15].OneofWrappers = []interface{}{ + (*GetProofResponse_FinalProof)(nil), + (*GetProofResponse_RecursiveProof)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_aggregator_proto_rawDesc, + NumEnums: 3, + NumMessages: 27, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_aggregator_proto_goTypes, + DependencyIndexes: file_aggregator_proto_depIdxs, + EnumInfos: file_aggregator_proto_enumTypes, + MessageInfos: file_aggregator_proto_msgTypes, + }.Build() + File_aggregator_proto = out.File + file_aggregator_proto_rawDesc = nil + file_aggregator_proto_goTypes = nil + file_aggregator_proto_depIdxs = nil +} diff --git a/aggregator/prover/aggregator_grpc.pb.go b/aggregator/prover/aggregator_grpc.pb.go new file mode 100644 index 00000000..32bdb107 --- /dev/null +++ b/aggregator/prover/aggregator_grpc.pb.go @@ -0,0 +1,150 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.4.0 +// - protoc v5.27.0 +// source: aggregator.proto + +package prover + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 + +const ( + AggregatorService_Channel_FullMethodName = "/aggregator.v1.AggregatorService/Channel" +) + +// AggregatorServiceClient is the client API for AggregatorService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// * +// Define all methods implementes by the gRPC +// Channel: prover receives aggregator messages and returns prover messages with the same id +type AggregatorServiceClient interface { + Channel(ctx context.Context, opts ...grpc.CallOption) (AggregatorService_ChannelClient, error) +} + +type aggregatorServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewAggregatorServiceClient(cc grpc.ClientConnInterface) AggregatorServiceClient { + return &aggregatorServiceClient{cc} +} + +func (c *aggregatorServiceClient) Channel(ctx context.Context, opts ...grpc.CallOption) (AggregatorService_ChannelClient, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &AggregatorService_ServiceDesc.Streams[0], AggregatorService_Channel_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &aggregatorServiceChannelClient{ClientStream: stream} + return x, nil +} + +type AggregatorService_ChannelClient interface { + Send(*ProverMessage) error + Recv() (*AggregatorMessage, error) + grpc.ClientStream +} + +type aggregatorServiceChannelClient struct { + grpc.ClientStream +} + +func (x *aggregatorServiceChannelClient) Send(m *ProverMessage) error { + return x.ClientStream.SendMsg(m) +} + +func (x *aggregatorServiceChannelClient) Recv() (*AggregatorMessage, error) { + m := new(AggregatorMessage) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// AggregatorServiceServer is the server API for AggregatorService service. +// All implementations must embed UnimplementedAggregatorServiceServer +// for forward compatibility +// +// * +// Define all methods implementes by the gRPC +// Channel: prover receives aggregator messages and returns prover messages with the same id +type AggregatorServiceServer interface { + Channel(AggregatorService_ChannelServer) error + mustEmbedUnimplementedAggregatorServiceServer() +} + +// UnimplementedAggregatorServiceServer must be embedded to have forward compatible implementations. +type UnimplementedAggregatorServiceServer struct { +} + +func (UnimplementedAggregatorServiceServer) Channel(AggregatorService_ChannelServer) error { + return status.Errorf(codes.Unimplemented, "method Channel not implemented") +} +func (UnimplementedAggregatorServiceServer) mustEmbedUnimplementedAggregatorServiceServer() {} + +// UnsafeAggregatorServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AggregatorServiceServer will +// result in compilation errors. +type UnsafeAggregatorServiceServer interface { + mustEmbedUnimplementedAggregatorServiceServer() +} + +func RegisterAggregatorServiceServer(s grpc.ServiceRegistrar, srv AggregatorServiceServer) { + s.RegisterService(&AggregatorService_ServiceDesc, srv) +} + +func _AggregatorService_Channel_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(AggregatorServiceServer).Channel(&aggregatorServiceChannelServer{ServerStream: stream}) +} + +type AggregatorService_ChannelServer interface { + Send(*AggregatorMessage) error + Recv() (*ProverMessage, error) + grpc.ServerStream +} + +type aggregatorServiceChannelServer struct { + grpc.ServerStream +} + +func (x *aggregatorServiceChannelServer) Send(m *AggregatorMessage) error { + return x.ServerStream.SendMsg(m) +} + +func (x *aggregatorServiceChannelServer) Recv() (*ProverMessage, error) { + m := new(ProverMessage) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// AggregatorService_ServiceDesc is the grpc.ServiceDesc for AggregatorService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var AggregatorService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "aggregator.v1.AggregatorService", + HandlerType: (*AggregatorServiceServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "Channel", + Handler: _AggregatorService_Channel_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "aggregator.proto", +} diff --git a/aggregator/prover/prover.go b/aggregator/prover/prover.go new file mode 100644 index 00000000..b8734984 --- /dev/null +++ b/aggregator/prover/prover.go @@ -0,0 +1,327 @@ +package prover + +import ( + "context" + "errors" + "fmt" + "net" + "time" + + "github.com/0xPolygon/cdk/config/types" + "github.com/0xPolygon/cdk/log" +) + +var ( + ErrBadProverResponse = errors.New("Prover returned wrong type for response") //nolint:revive + ErrProverInternalError = errors.New("Prover returned INTERNAL_ERROR response") //nolint:revive + ErrProverCompletedError = errors.New("Prover returned COMPLETED_ERROR response") //nolint:revive + ErrBadRequest = errors.New("Prover returned ERROR for a bad request") //nolint:revive + ErrUnspecified = errors.New("Prover returned an UNSPECIFIED response") //nolint:revive + ErrUnknown = errors.New("Prover returned an unknown response") //nolint:revive + ErrProofCanceled = errors.New("Proof has been canceled") //nolint:revive +) + +// Prover abstraction of the grpc prover client. +type Prover struct { + name string + id string + address net.Addr + proofStatePollingInterval types.Duration + stream AggregatorService_ChannelServer +} + +// New returns a new Prover instance. +func New(stream AggregatorService_ChannelServer, addr net.Addr, proofStatePollingInterval types.Duration) (*Prover, error) { + p := &Prover{ + stream: stream, + address: addr, + proofStatePollingInterval: proofStatePollingInterval, + } + status, err := p.Status() + if err != nil { + return nil, fmt.Errorf("Failed to retrieve prover id %w", err) + } + p.name = status.ProverName + p.id = status.ProverId + return p, nil +} + +// Name returns the Prover name. +func (p *Prover) Name() string { return p.name } + +// ID returns the Prover ID. +func (p *Prover) ID() string { return p.id } + +// Addr returns the prover IP address. +func (p *Prover) Addr() string { + if p.address == nil { + return "" + } + return p.address.String() +} + +// Status gets the prover status. +func (p *Prover) Status() (*GetStatusResponse, error) { + req := &AggregatorMessage{ + Request: &AggregatorMessage_GetStatusRequest{ + GetStatusRequest: &GetStatusRequest{}, + }, + } + res, err := p.call(req) + if err != nil { + return nil, err + } + if msg, ok := res.Response.(*ProverMessage_GetStatusResponse); ok { + return msg.GetStatusResponse, nil + } + return nil, fmt.Errorf("%w, wanted %T, got %T", ErrBadProverResponse, &ProverMessage_GetStatusResponse{}, res.Response) +} + +// IsIdle returns true if the prover is idling. +func (p *Prover) IsIdle() (bool, error) { + status, err := p.Status() + if err != nil { + return false, err + } + return status.Status == GetStatusResponse_STATUS_IDLE, nil +} + +// SupportsForkID returns true if the prover supports the given fork id. +func (p *Prover) SupportsForkID(forkID uint64) bool { + status, err := p.Status() + if err != nil { + log.Warnf("Error asking status for prover ID %s: %v", p.ID(), err) + return false + } + + log.Debugf("Prover %s supports fork ID %d", p.ID(), status.ForkId) + + return status.ForkId == forkID +} + +// BatchProof instructs the prover to generate a batch proof for the provided +// input. It returns the ID of the proof being computed. +func (p *Prover) BatchProof(input *StatelessInputProver) (*string, error) { + req := &AggregatorMessage{ + Request: &AggregatorMessage_GenStatelessBatchProofRequest{ + GenStatelessBatchProofRequest: &GenStatelessBatchProofRequest{Input: input}, + }, + } + res, err := p.call(req) + if err != nil { + return nil, err + } + + if msg, ok := res.Response.(*ProverMessage_GenBatchProofResponse); ok { + switch msg.GenBatchProofResponse.Result { + case Result_RESULT_UNSPECIFIED: + return nil, fmt.Errorf("failed to generate proof %s, %w, input %v", msg.GenBatchProofResponse.String(), ErrUnspecified, input) + case Result_RESULT_OK: + return &msg.GenBatchProofResponse.Id, nil + case Result_RESULT_ERROR: + return nil, fmt.Errorf("failed to generate proof %s, %w, input %v", msg.GenBatchProofResponse.String(), ErrBadRequest, input) + case Result_RESULT_INTERNAL_ERROR: + return nil, fmt.Errorf("failed to generate proof %s, %w, input %v", msg.GenBatchProofResponse.String(), ErrProverInternalError, input) + default: + return nil, fmt.Errorf("failed to generate proof %s, %w,input %v", msg.GenBatchProofResponse.String(), ErrUnknown, input) + } + } + + return nil, fmt.Errorf("%w, wanted %T, got %T", ErrBadProverResponse, &ProverMessage_GenBatchProofResponse{}, res.Response) +} + +// AggregatedProof instructs the prover to generate an aggregated proof from +// the two inputs provided. It returns the ID of the proof being computed. +func (p *Prover) AggregatedProof(inputProof1, inputProof2 string) (*string, error) { + req := &AggregatorMessage{ + Request: &AggregatorMessage_GenAggregatedProofRequest{ + GenAggregatedProofRequest: &GenAggregatedProofRequest{ + RecursiveProof_1: inputProof1, + RecursiveProof_2: inputProof2, + }, + }, + } + res, err := p.call(req) + if err != nil { + return nil, err + } + + if msg, ok := res.Response.(*ProverMessage_GenAggregatedProofResponse); ok { + switch msg.GenAggregatedProofResponse.Result { + case Result_RESULT_UNSPECIFIED: + return nil, fmt.Errorf("failed to aggregate proofs %s, %w, input 1 %s, input 2 %s", + msg.GenAggregatedProofResponse.String(), ErrUnspecified, inputProof1, inputProof2) + case Result_RESULT_OK: + return &msg.GenAggregatedProofResponse.Id, nil + case Result_RESULT_ERROR: + return nil, fmt.Errorf("failed to aggregate proofs %s, %w, input 1 %s, input 2 %s", + msg.GenAggregatedProofResponse.String(), ErrBadRequest, inputProof1, inputProof2) + case Result_RESULT_INTERNAL_ERROR: + return nil, fmt.Errorf("failed to aggregate proofs %s, %w, input 1 %s, input 2 %s", + msg.GenAggregatedProofResponse.String(), ErrProverInternalError, inputProof1, inputProof2) + default: + return nil, fmt.Errorf("failed to aggregate proofs %s, %w, input 1 %s, input 2 %s", + msg.GenAggregatedProofResponse.String(), ErrUnknown, inputProof1, inputProof2) + } + } + + return nil, fmt.Errorf("%w, wanted %T, got %T", ErrBadProverResponse, &ProverMessage_GenAggregatedProofResponse{}, res.Response) +} + +// FinalProof instructs the prover to generate a final proof for the given +// input. It returns the ID of the proof being computed. +func (p *Prover) FinalProof(inputProof string, aggregatorAddr string) (*string, error) { + req := &AggregatorMessage{ + Request: &AggregatorMessage_GenFinalProofRequest{ + GenFinalProofRequest: &GenFinalProofRequest{ + RecursiveProof: inputProof, + AggregatorAddr: aggregatorAddr, + }, + }, + } + res, err := p.call(req) + if err != nil { + return nil, err + } + + if msg, ok := res.Response.(*ProverMessage_GenFinalProofResponse); ok { + switch msg.GenFinalProofResponse.Result { + case Result_RESULT_UNSPECIFIED: + return nil, fmt.Errorf("failed to generate final proof %s, %w, input %s", + msg.GenFinalProofResponse.String(), ErrUnspecified, inputProof) + case Result_RESULT_OK: + return &msg.GenFinalProofResponse.Id, nil + case Result_RESULT_ERROR: + return nil, fmt.Errorf("failed to generate final proof %s, %w, input %s", + msg.GenFinalProofResponse.String(), ErrBadRequest, inputProof) + case Result_RESULT_INTERNAL_ERROR: + return nil, fmt.Errorf("failed to generate final proof %s, %w, input %s", + msg.GenFinalProofResponse.String(), ErrProverInternalError, inputProof) + default: + return nil, fmt.Errorf("failed to generate final proof %s, %w, input %s", + msg.GenFinalProofResponse.String(), ErrUnknown, inputProof) + } + } + return nil, fmt.Errorf("%w, wanted %T, got %T", ErrBadProverResponse, &ProverMessage_GenFinalProofResponse{}, res.Response) +} + +// CancelProofRequest asks the prover to stop the generation of the proof +// matching the provided proofID. +func (p *Prover) CancelProofRequest(proofID string) error { + req := &AggregatorMessage{ + Request: &AggregatorMessage_CancelRequest{ + CancelRequest: &CancelRequest{Id: proofID}, + }, + } + res, err := p.call(req) + if err != nil { + return err + } + if msg, ok := res.Response.(*ProverMessage_CancelResponse); ok { + switch msg.CancelResponse.Result { + case Result_RESULT_UNSPECIFIED: + return fmt.Errorf("failed to cancel proof id [%s], %w, %s", + proofID, ErrUnspecified, msg.CancelResponse.String()) + case Result_RESULT_OK: + return nil + case Result_RESULT_ERROR: + return fmt.Errorf("failed to cancel proof id [%s], %w, %s", + proofID, ErrBadRequest, msg.CancelResponse.String()) + case Result_RESULT_INTERNAL_ERROR: + return fmt.Errorf("failed to cancel proof id [%s], %w, %s", + proofID, ErrProverInternalError, msg.CancelResponse.String()) + default: + return fmt.Errorf("failed to cancel proof id [%s], %w, %s", + proofID, ErrUnknown, msg.CancelResponse.String()) + } + } + return fmt.Errorf("%w, wanted %T, got %T", ErrBadProverResponse, &ProverMessage_CancelResponse{}, res.Response) +} + +// WaitRecursiveProof waits for a recursive proof to be generated by the prover +// and returns it. +func (p *Prover) WaitRecursiveProof(ctx context.Context, proofID string) (string, error) { + res, err := p.waitProof(ctx, proofID) + if err != nil { + return "", err + } + resProof := res.Proof.(*GetProofResponse_RecursiveProof) + return resProof.RecursiveProof, nil +} + +// WaitFinalProof waits for the final proof to be generated by the prover and +// returns it. +func (p *Prover) WaitFinalProof(ctx context.Context, proofID string) (*FinalProof, error) { + res, err := p.waitProof(ctx, proofID) + if err != nil { + return nil, err + } + resProof := res.Proof.(*GetProofResponse_FinalProof) + return resProof.FinalProof, nil +} + +// waitProof waits for a proof to be generated by the prover and returns the +// prover response. +func (p *Prover) waitProof(ctx context.Context, proofID string) (*GetProofResponse, error) { + req := &AggregatorMessage{ + Request: &AggregatorMessage_GetProofRequest{ + GetProofRequest: &GetProofRequest{ + // TODO(pg): set Timeout field? + Id: proofID, + }, + }, + } + + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + res, err := p.call(req) + if err != nil { + return nil, err + } + if msg, ok := res.Response.(*ProverMessage_GetProofResponse); ok { + switch msg.GetProofResponse.Result { + case GetProofResponse_RESULT_PENDING: + time.Sleep(p.proofStatePollingInterval.Duration) + continue + case GetProofResponse_RESULT_UNSPECIFIED: + return nil, fmt.Errorf("failed to get proof ID: %s, %w, prover response: %s", + proofID, ErrUnspecified, msg.GetProofResponse.String()) + case GetProofResponse_RESULT_COMPLETED_OK: + return msg.GetProofResponse, nil + case GetProofResponse_RESULT_ERROR: + return nil, fmt.Errorf("failed to get proof with ID %s, %w, prover response: %s", + proofID, ErrBadRequest, msg.GetProofResponse.String()) + case GetProofResponse_RESULT_COMPLETED_ERROR: + return nil, fmt.Errorf("failed to get proof with ID %s, %w, prover response: %s", + proofID, ErrProverCompletedError, msg.GetProofResponse.String()) + case GetProofResponse_RESULT_INTERNAL_ERROR: + return nil, fmt.Errorf("failed to get proof ID: %s, %w, prover response: %s", + proofID, ErrProverInternalError, msg.GetProofResponse.String()) + case GetProofResponse_RESULT_CANCEL: + return nil, fmt.Errorf("proof generation was cancelled for proof ID %s, %w, prover response: %s", + proofID, ErrProofCanceled, msg.GetProofResponse.String()) + default: + return nil, fmt.Errorf("failed to get proof ID: %s, %w, prover response: %s", + proofID, ErrUnknown, msg.GetProofResponse.String()) + } + } + return nil, fmt.Errorf("%w, wanted %T, got %T", ErrBadProverResponse, &ProverMessage_GetProofResponse{}, res.Response) + } + } +} + +// call sends a message to the prover and waits to receive the response over +// the connection stream. +func (p *Prover) call(req *AggregatorMessage) (*ProverMessage, error) { + if err := p.stream.Send(req); err != nil { + return nil, err + } + res, err := p.stream.Recv() + if err != nil { + return nil, err + } + return res, nil +} diff --git a/cmd/cdk/main.go b/cmd/cdk/main.go deleted file mode 100644 index 06ab7d0f..00000000 --- a/cmd/cdk/main.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 00000000..ce319729 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,84 @@ +package main + +import ( + "os" + + zkevm "github.com/0xPolygon/cdk" + "github.com/0xPolygon/cdk/config" + "github.com/0xPolygon/cdk/log" + "github.com/urfave/cli/v2" +) + +const appName = "cdk" + +const ( + // SEQUENCE_SENDER name to identify the sequence-sender component + SEQUENCE_SENDER = "sequence-sender" + // AGGREGATOR name to identify the aggregator component + AGGREGATOR = "aggregator" +) + +const ( + // NETWORK_CONFIGFILE name to identify the netowk_custom (genesis) config-file + NETWORK_CONFIGFILE = "custom_network" +) + +var ( + configFileFlag = cli.StringFlag{ + Name: config.FlagCfg, + Aliases: []string{"c"}, + Usage: "Configuration `FILE`", + Required: true, + } + customNetworkFlag = cli.StringFlag{ + Name: config.FlagCustomNetwork, + Aliases: []string{"net-file"}, + Usage: "Load the network configuration file if --network=custom", + Required: false, + } + yesFlag = cli.BoolFlag{ + Name: config.FlagYes, + Aliases: []string{"y"}, + Usage: "Automatically accepts any confirmation to execute the command", + Required: false, + } + componentsFlag = cli.StringSliceFlag{ + Name: config.FlagComponents, + Aliases: []string{"co"}, + Usage: "List of components to run", + Required: false, + Value: cli.NewStringSlice(SEQUENCE_SENDER, AGGREGATOR), + } +) + +func main() { + app := cli.NewApp() + app.Name = appName + app.Version = zkevm.Version + flags := []cli.Flag{ + &configFileFlag, + &yesFlag, + &componentsFlag, + } + app.Commands = []*cli.Command{ + { + Name: "version", + Aliases: []string{}, + Usage: "Application version and build", + Action: versionCmd, + }, + { + Name: "run", + Aliases: []string{}, + Usage: "Run the cdk client", + Action: start, + Flags: append(flags, &customNetworkFlag), + }, + } + + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + os.Exit(1) + } +} diff --git a/cmd/run.go b/cmd/run.go new file mode 100644 index 00000000..db1090bb --- /dev/null +++ b/cmd/run.go @@ -0,0 +1,257 @@ +package main + +import ( + "context" + "crypto/ecdsa" + "fmt" + "os" + "os/signal" + "runtime" + + zkevm "github.com/0xPolygon/cdk" + dataCommitteeClient "github.com/0xPolygon/cdk-data-availability/client" + "github.com/0xPolygon/cdk/aggregator" + "github.com/0xPolygon/cdk/aggregator/db" + "github.com/0xPolygon/cdk/config" + "github.com/0xPolygon/cdk/dataavailability" + "github.com/0xPolygon/cdk/dataavailability/datacommittee" + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/sequencesender" + "github.com/0xPolygon/cdk/state" + "github.com/0xPolygon/cdk/state/pgstatestorage" + ethtxman "github.com/0xPolygonHermez/zkevm-ethtx-manager/etherman" + "github.com/0xPolygonHermez/zkevm-ethtx-manager/etherman/etherscan" + "github.com/jackc/pgx/v4/pgxpool" + "github.com/urfave/cli/v2" +) + +func start(cliCtx *cli.Context) error { + c, err := config.Load(cliCtx, true) + if err != nil { + return err + } + + log.Init(c.Log) + + if c.Log.Environment == log.EnvironmentDevelopment { + zkevm.PrintVersion(os.Stdout) + log.Info("Starting application") + } else if c.Log.Environment == log.EnvironmentProduction { + logVersion() + } + + components := cliCtx.StringSlice(config.FlagComponents) + + for _, component := range components { + switch component { + case SEQUENCE_SENDER: + c.SequenceSender.Log = c.Log + seqSender := createSequenceSender(*c) + // start sequence sender in a goroutine, checking for errors + go seqSender.Start(cliCtx.Context) + + case AGGREGATOR: + aggregator := createAggregator(cliCtx.Context, *c, !cliCtx.Bool(config.FlagMigrations)) + // start aggregator in a goroutine, checking for errors + go func() { + if err := aggregator.Start(cliCtx.Context); err != nil { + log.Fatal(err) + } + }() + } + } + + waitSignal(nil) + + return nil +} + +func createAggregator(ctx context.Context, c config.Config, runMigrations bool) *aggregator.Aggregator { + // Migrations + if runMigrations { + log.Infof("Running DB migrations host: %s:%s db:%s user:%s", c.Aggregator.DB.Host, c.Aggregator.DB.Port, c.Aggregator.DB.Name, c.Aggregator.DB.User) + runAggregatorMigrations(c.Aggregator.DB) + } + + // DB + stateSqlDB, err := db.NewSQLDB(c.Aggregator.DB) + if err != nil { + log.Fatal(err) + } + + etherman, err := newEtherman(c) + if err != nil { + log.Fatal(err) + } + + // READ CHAIN ID FROM POE SC + l2ChainID, err := etherman.GetL2ChainID() + if err != nil { + log.Fatal(err) + } + + st := newState(&c, l2ChainID, stateSqlDB) + + c.Aggregator.ChainID = l2ChainID + + checkAggregatorMigrations(c.Aggregator.DB) + + // Populate Network config + c.Aggregator.Synchronizer.Etherman.Contracts.GlobalExitRootManagerAddr = c.NetworkConfig.L1Config.GlobalExitRootManagerAddr + c.Aggregator.Synchronizer.Etherman.Contracts.RollupManagerAddr = c.NetworkConfig.L1Config.RollupManagerAddr + c.Aggregator.Synchronizer.Etherman.Contracts.ZkEVMAddr = c.NetworkConfig.L1Config.ZkEVMAddr + + aggregator, err := aggregator.New(ctx, c.Aggregator, st, etherman) + if err != nil { + log.Fatal(err) + } + + return aggregator +} + +func createSequenceSender(cfg config.Config) *sequencesender.SequenceSender { + ethman, err := etherman.NewClient(etherman.Config{ + EthermanConfig: ethtxman.Config{ + URL: cfg.SequenceSender.EthTxManager.Etherman.URL, + MultiGasProvider: cfg.SequenceSender.EthTxManager.Etherman.MultiGasProvider, + L1ChainID: cfg.SequenceSender.EthTxManager.Etherman.L1ChainID, + Etherscan: etherscan.Config{ + ApiKey: cfg.SequenceSender.EthTxManager.Etherman.Etherscan.ApiKey, + Url: cfg.SequenceSender.EthTxManager.Etherman.Etherscan.Url, + }, + HTTPHeaders: cfg.SequenceSender.EthTxManager.Etherman.HTTPHeaders, + }, + IsValidiumMode: cfg.SequenceSender.IsValidiumMode, + }, cfg.NetworkConfig.L1Config) + if err != nil { + log.Fatal(err) + } + + auth, _, err := ethman.LoadAuthFromKeyStore(cfg.SequenceSender.PrivateKey.Path, cfg.SequenceSender.PrivateKey.Password) + if err != nil { + log.Fatal(err) + } + cfg.SequenceSender.SenderAddress = auth.From + + da, err := newDataAvailability(cfg, ethman) + if err != nil { + log.Fatal(err) + } + + seqSender, err := sequencesender.New(cfg.SequenceSender, ethman, da) + if err != nil { + log.Fatal(err) + } + + return seqSender +} + +func newDataAvailability(c config.Config, etherman *etherman.Client) (*dataavailability.DataAvailability, error) { + if !c.SequenceSender.IsValidiumMode { + return nil, nil + } + + // Backend specific config + daProtocolName, err := etherman.GetDAProtocolName() + if err != nil { + return nil, fmt.Errorf("error getting data availability protocol name: %v", err) + } + var daBackend dataavailability.DABackender + switch daProtocolName { + case string(dataavailability.DataAvailabilityCommittee): + var ( + pk *ecdsa.PrivateKey + err error + ) + _, pk, err = etherman.LoadAuthFromKeyStore(c.SequenceSender.PrivateKey.Path, c.SequenceSender.PrivateKey.Password) + if err != nil { + return nil, err + } + dacAddr, err := etherman.GetDAProtocolAddr() + if err != nil { + return nil, fmt.Errorf("error getting trusted sequencer URI. Error: %v", err) + } + + daBackend, err = datacommittee.New( + c.SequenceSender.EthTxManager.Etherman.URL, + dacAddr, + pk, + dataCommitteeClient.NewFactory(), + ) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("unexpected / unsupported DA protocol: %s", daProtocolName) + } + + return dataavailability.New(daBackend) +} + +func runAggregatorMigrations(c db.Config) { + runMigrations(c, db.AggregatorMigrationName) +} + +func checkAggregatorMigrations(c db.Config) { + err := db.CheckMigrations(c, db.AggregatorMigrationName) + if err != nil { + log.Fatal(err) + } +} + +func runMigrations(c db.Config, name string) { + log.Infof("running migrations for %v", name) + err := db.RunMigrationsUp(c, name) + if err != nil { + log.Fatal(err) + } +} + +func newEtherman(c config.Config) (*etherman.Client, error) { + config := etherman.Config{ + URL: c.Aggregator.EthTxManager.Etherman.URL, + } + return etherman.NewClient(config, c.NetworkConfig.L1Config) +} + +func logVersion() { + log.Infow("Starting application", + // version is already logged by default + "gitRevision", zkevm.GitRev, + "gitBranch", zkevm.GitBranch, + "goVersion", runtime.Version(), + "built", zkevm.BuildDate, + "os/arch", fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), + ) +} + +func waitSignal(cancelFuncs []context.CancelFunc) { + signals := make(chan os.Signal, 1) + signal.Notify(signals, os.Interrupt) + + for sig := range signals { + switch sig { + case os.Interrupt, os.Kill: + log.Info("terminating application gracefully...") + + exitStatus := 0 + for _, cancel := range cancelFuncs { + cancel() + } + os.Exit(exitStatus) + } + } +} + +func newState(c *config.Config, l2ChainID uint64, sqlDB *pgxpool.Pool) *state.State { + stateCfg := state.Config{ + DB: c.Aggregator.DB, + ChainID: l2ChainID, + } + + stateDb := pgstatestorage.NewPostgresStorage(stateCfg, sqlDB) + + st := state.NewState(stateCfg, stateDb) + return st +} diff --git a/cmd/version.go b/cmd/version.go new file mode 100644 index 00000000..51766c02 --- /dev/null +++ b/cmd/version.go @@ -0,0 +1,13 @@ +package main + +import ( + "os" + + zkevm "github.com/0xPolygon/cdk" + "github.com/urfave/cli/v2" +) + +func versionCmd(*cli.Context) error { + zkevm.PrintVersion(os.Stdout) + return nil +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 00000000..c38da5e6 --- /dev/null +++ b/config/config.go @@ -0,0 +1,134 @@ +package config + +import ( + "bytes" + "path/filepath" + "strings" + + "github.com/0xPolygon/cdk/aggregator" + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/sequencesender" + "github.com/0xPolygonHermez/zkevm-ethtx-manager/ethtxmanager" + "github.com/mitchellh/mapstructure" + "github.com/spf13/viper" + "github.com/urfave/cli/v2" +) + +const ( + // FlagYes is the flag for yes. + FlagYes = "yes" + // FlagCfg is the flag for cfg. + FlagCfg = "cfg" + // FlagCustomNetwork is the flag for the custom network file. + FlagCustomNetwork = "custom-network-file" + // FlagAmount is the flag for amount. + FlagAmount = "amount" + // FlagRemoteMT is the flag for remote-merkletree. + FlagRemoteMT = "remote-merkletree" + // FlagComponents is the flag for components. + FlagComponents = "components" + // FlagHTTPAPI is the flag for http.api. + FlagHTTPAPI = "http.api" + // FlagKeyStorePath is the path of the key store file containing the private key of the account going to sing and approve the tokens + FlagKeyStorePath = "key-store-path" + // FlagPassword is the password needed to decrypt the key store + FlagPassword = "password" + // FlagMigrations is the flag for migrations. + FlagMigrations = "migrations" + // FlagOutputFile is the flag for the output file + FlagOutputFile = "output" + // FlagMaxAmount is the flag to avoid to use the flag FlagAmount + FlagMaxAmount = "max-amount" +) + +/* +Config represents the configuration of the entire Hermez Node +The file is [TOML format] +You could find some examples: + - `config/environments/local/local.node.config.toml`: running a permisionless node + - `config/environments/mainnet/node.config.toml` + - `config/environments/public/node.config.toml` + - `test/config/test.node.config.toml`: configuration for a trusted node used in CI + +[TOML format]: https://en.wikipedia.org/wiki/TOML +*/ +type Config struct { + // Configuration of the etherman (client for access L1) + Etherman etherman.Config + // Configuration for ethereum transaction manager + EthTxManager ethtxmanager.Config + // Configuration of the aggregator + Aggregator aggregator.Config + // Configure Log level for all the services, allow also to store the logs in a file + Log log.Config + // Configuration of the genesis of the network. This is used to known the initial state of the network + NetworkConfig NetworkConfig + // Configuration of the sequence sender service + SequenceSender sequencesender.Config +} + +// Default parses the default configuration values. +func Default() (*Config, error) { + var cfg Config + viper.SetConfigType("toml") + + err := viper.ReadConfig(bytes.NewBuffer([]byte(DefaultValues))) + if err != nil { + return nil, err + } + err = viper.Unmarshal(&cfg, viper.DecodeHook(mapstructure.TextUnmarshallerHookFunc())) + if err != nil { + return nil, err + } + return &cfg, nil +} + +// Load loads the configuration +func Load(ctx *cli.Context, loadNetworkConfig bool) (*Config, error) { + cfg, err := Default() + if err != nil { + return nil, err + } + configFilePath := ctx.String(FlagCfg) + if configFilePath != "" { + dirName, fileName := filepath.Split(configFilePath) + + fileExtension := strings.TrimPrefix(filepath.Ext(fileName), ".") + fileNameWithoutExtension := strings.TrimSuffix(fileName, "."+fileExtension) + + viper.AddConfigPath(dirName) + viper.SetConfigName(fileNameWithoutExtension) + viper.SetConfigType(fileExtension) + } + viper.AutomaticEnv() + replacer := strings.NewReplacer(".", "_") + viper.SetEnvKeyReplacer(replacer) + viper.SetEnvPrefix("CDK") + err = viper.ReadInConfig() + if err != nil { + _, ok := err.(viper.ConfigFileNotFoundError) + if ok { + log.Infof("config file not found") + } else { + log.Infof("error reading config file: ", err) + return nil, err + } + } + + decodeHooks := []viper.DecoderConfigOption{ + // this allows arrays to be decoded from env var separated by ",", example: MY_VAR="value1,value2,value3" + viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(mapstructure.TextUnmarshallerHookFunc(), mapstructure.StringToSliceHookFunc(","))), + } + + err = viper.Unmarshal(&cfg, decodeHooks...) + if err != nil { + return nil, err + } + + if loadNetworkConfig { + // Load genesis parameters + cfg.loadNetworkConfig(ctx) + } + return cfg, nil +} diff --git a/config/default.go b/config/default.go new file mode 100644 index 00000000..f8dce471 --- /dev/null +++ b/config/default.go @@ -0,0 +1,118 @@ +package config + +// DefaultValues is the default configuration +const DefaultValues = ` +ForkUpgradeBatchNumber = 0 +ForkUpgradeNewForkId = 0 + +[Log] +Environment = "development" # "production" or "development" +Level = "info" +Outputs = ["stderr"] + +[SequenceSender] +IsValidiumMode = false +WaitPeriodSendSequence = "15s" +LastBatchVirtualizationTimeMaxWaitPeriod = "10s" +L1BlockTimestampMargin = "30s" +MaxTxSizeForL1 = 131072 +L2Coinbase = "0xfa3b44587990f97ba8b6ba7e230a5f0e95d14b3d" +PrivateKey = {Path = "./test/sequencer.keystore", Password = "testonly"} +SequencesTxFileName = "sequencesender.json" +GasOffset = 80000 +WaitPeriodPurgeTxFile = "15m" +MaxPendingTx = 1 + [SequenceSender.StreamClient] + Server = "127.0.0.1:6900" + [SequenceSender.EthTxManager] + FrequencyToMonitorTxs = "1s" + WaitTxToBeMined = "2m" + GetReceiptMaxTime = "250ms" + GetReceiptWaitInterval = "1s" + PrivateKeys = [ + {Path = "./test/sequencer.keystore", Password = "testonly"}, + ] + ForcedGas = 0 + GasPriceMarginFactor = 1 + MaxGasPriceLimit = 0 + PersistenceFilename = "ethtxmanager.json" + ReadPendingL1Txs = false + SafeStatusL1NumberOfBlocks = 0 + FinalizedStatusL1NumberOfBlocks = 0 + [SequenceSender.EthTxManager.Etherman] + URL = "http://127.0.0.1:8545" + MultiGasProvider = false + L1ChainID = 1337 +[Aggregator] +Host = "0.0.0.0" +Port = 50081 +RetryTime = "5s" +VerifyProofInterval = "10s" +TxProfitabilityCheckerType = "acceptall" +TxProfitabilityMinReward = "1.1" +ProofStatePollingInterval = "5s" +SenderAddress = "" +CleanupLockedProofsInterval = "2m" +GeneratingProofCleanupThreshold = "10m" +ForkId = 9 +GasOffset = 0 +WitnessURL = "localhost:8123" +UseL1BatchData = true +UseFullWitness = false +SettlementBackend = "l1" +AggLayerTxTimeout = "5m" +AggLayerURL = "" +SequencerPrivateKey = {} + [Aggregator.DB] + Name = "aggregator_db" + User = "aggregator_user" + Password = "aggregator_password" + Host = "cdk-aggregator-db" + Port = "5432" + EnableLog = false + MaxConns = 200 + [Aggregator.Log] + Environment = "development" # "production" or "development" + Level = "info" + Outputs = ["stderr"] + [Aggregator.StreamClient] + Server = "localhost:6900" + [Aggregator.EthTxManager] + FrequencyToMonitorTxs = "1s" + WaitTxToBeMined = "2m" + GetReceiptMaxTime = "250ms" + GetReceiptWaitInterval = "1s" + PrivateKeys = [ + {Path = "/pk/aggregator.keystore", Password = "testonly"}, + ] + ForcedGas = 0 + GasPriceMarginFactor = 1 + MaxGasPriceLimit = 0 + PersistenceFilename = "" + ReadPendingL1Txs = false + SafeStatusL1NumberOfBlocks = 0 + FinalizedStatusL1NumberOfBlocks = 0 + [Aggregator.EthTxManager.Etherman] + URL = "" + L1ChainID = 11155111 + HTTPHeaders = [] + [Aggregator.Synchronizer] + [Aggregator.Synchronizer.DB] + Name = "sync_db" + User = "sync_user" + Password = "sync_password" + Host = "cdk-l1-sync-db" + Port = "5432" + EnableLog = false + MaxConns = 10 + [Aggregator.Synchronizer.Synchronizer] + SyncInterval = "10s" + SyncChunkSize = 1000 + GenesisBlockNumber = 5511080 + SyncUpToBlock = "finalized" + BlockFinality = "finalized" + OverrideStorageCheck = false + [Aggregator.Synchronizer.Etherman] + [Aggregator.Synchronizer.Etherman.Validium] + Enabled = false +` diff --git a/config/network.go b/config/network.go new file mode 100644 index 00000000..cbdef441 --- /dev/null +++ b/config/network.go @@ -0,0 +1,166 @@ +package config + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "os" + + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/state" + "github.com/ethereum/go-ethereum/common" + "github.com/urfave/cli/v2" +) + +// NetworkConfig is the configuration struct for the different environments +type NetworkConfig struct { + // L1: Configuration related to L1 + L1Config etherman.L1Config `json:"l1Config"` + // L1: Genesis of the rollup, first block number and root + Genesis state.Genesis +} + +type network string +type leafType uint8 + +const ( + custom network = "custom" + // LeafTypeBalance specifies that leaf stores Balance + LeafTypeBalance leafType = 0 + // LeafTypeNonce specifies that leaf stores Nonce + LeafTypeNonce leafType = 1 + // LeafTypeCode specifies that leaf stores Code + LeafTypeCode leafType = 2 + // LeafTypeStorage specifies that leaf stores Storage Value + LeafTypeStorage leafType = 3 + // LeafTypeSCLength specifies that leaf stores Storage Value + LeafTypeSCLength leafType = 4 +) + +// GenesisFromJSON is the config file for network_custom +type GenesisFromJSON struct { + // L1: root hash of the genesis block + Root string `json:"root"` + // L1: block number of the genesis block + GenesisBlockNum uint64 `json:"genesisBlockNumber"` + // L2: List of states contracts used to populate merkle tree at initial state + Genesis []genesisAccountFromJSON `json:"genesis"` + // L1: configuration of the network + L1Config etherman.L1Config +} + +type genesisAccountFromJSON struct { + // Address of the account + Balance string `json:"balance"` + // Nonce of the account + Nonce string `json:"nonce"` + // Address of the contract + Address string `json:"address"` + // Byte code of the contract + Bytecode string `json:"bytecode"` + // Initial storage of the contract + Storage map[string]string `json:"storage"` + // Name of the contract in L1 (e.g. "PolygonZkEVMDeployer", "PolygonZkEVMBridge",...) + ContractName string `json:"contractName"` +} + +func (cfg *Config) loadNetworkConfig(ctx *cli.Context) { + cfgPath := ctx.String(FlagCustomNetwork) + networkJSON, err := LoadGenesisFileAsString(cfgPath) + if err != nil { + panic(err.Error()) + } + + config, err := LoadGenesisFromJSONString(networkJSON) + if err != nil { + panic(fmt.Errorf("failed to load genesis configuration from file. Error: %v", err)) + } + cfg.NetworkConfig = config +} + +// LoadGenesisFileAsString loads the genesis file as a string +func LoadGenesisFileAsString(cfgPath string) (string, error) { + if cfgPath != "" { + f, err := os.Open(cfgPath) //nolint:gosec + if err != nil { + return "", err + } + defer func() { + err := f.Close() + if err != nil { + log.Error(err) + } + }() + + b, err := io.ReadAll(f) + if err != nil { + return "", err + } + return string(b), nil + } else { + return "", errors.New("custom netwrork file not provided. Please use the custom-network-file flag") + } +} + +// LoadGenesisFromJSONString loads the genesis file from JSON string +func LoadGenesisFromJSONString(jsonStr string) (NetworkConfig, error) { + var cfg NetworkConfig + + var cfgJSON GenesisFromJSON + if err := json.Unmarshal([]byte(jsonStr), &cfgJSON); err != nil { + return NetworkConfig{}, err + } + + if len(cfgJSON.Genesis) == 0 { + return cfg, nil + } + + cfg.L1Config = cfgJSON.L1Config + cfg.Genesis = state.Genesis{ + BlockNumber: cfgJSON.GenesisBlockNum, + Root: common.HexToHash(cfgJSON.Root), + Actions: []*state.GenesisAction{}, + } + + for _, account := range cfgJSON.Genesis { + if account.Balance != "" && account.Balance != "0" { + action := &state.GenesisAction{ + Address: account.Address, + Type: int(LeafTypeBalance), + Value: account.Balance, + } + cfg.Genesis.Actions = append(cfg.Genesis.Actions, action) + } + if account.Nonce != "" && account.Nonce != "0" { + action := &state.GenesisAction{ + Address: account.Address, + Type: int(LeafTypeNonce), + Value: account.Nonce, + } + cfg.Genesis.Actions = append(cfg.Genesis.Actions, action) + } + if account.Bytecode != "" { + action := &state.GenesisAction{ + Address: account.Address, + Type: int(LeafTypeCode), + Bytecode: account.Bytecode, + } + cfg.Genesis.Actions = append(cfg.Genesis.Actions, action) + } + if len(account.Storage) > 0 { + for storageKey, storageValue := range account.Storage { + action := &state.GenesisAction{ + Address: account.Address, + Type: int(LeafTypeStorage), + StoragePosition: storageKey, + Value: storageValue, + } + cfg.Genesis.Actions = append(cfg.Genesis.Actions, action) + } + } + } + + return cfg, nil +} diff --git a/config/types/duration.go b/config/types/duration.go new file mode 100644 index 00000000..7612291f --- /dev/null +++ b/config/types/duration.go @@ -0,0 +1,40 @@ +package types + +import ( + "time" + + "github.com/invopop/jsonschema" +) + +// Duration is a wrapper type that parses time duration from text. +type Duration struct { + time.Duration `validate:"required"` +} + +// UnmarshalText unmarshalls time duration from text. +func (d *Duration) UnmarshalText(data []byte) error { + duration, err := time.ParseDuration(string(data)) + if err != nil { + return err + } + d.Duration = duration + return nil +} + +// NewDuration returns Duration wrapper +func NewDuration(duration time.Duration) Duration { + return Duration{duration} +} + +// JSONSchema returns a custom schema to be used for the JSON Schema generation of this type +func (Duration) JSONSchema() *jsonschema.Schema { + return &jsonschema.Schema{ + Type: "string", + Title: "Duration", + Description: "Duration expressed in units: [ns, us, ms, s, m, h, d]", + Examples: []interface{}{ + "1m", + "300ms", + }, + } +} diff --git a/config/types/duration_test.go b/config/types/duration_test.go new file mode 100644 index 00000000..71e06a04 --- /dev/null +++ b/config/types/duration_test.go @@ -0,0 +1,54 @@ +package types + +import ( + "encoding/json" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestDurationUnmarshal(t *testing.T) { + type testCase struct { + name string + input string + expectedResult *Duration + expectedErr error + } + + testCases := []testCase{ + { + name: "valid duration", + input: "60s", + expectedResult: &Duration{Duration: time.Minute}, + }, + { + name: "int value", + input: "60", + expectedErr: fmt.Errorf("time: missing unit in duration \"60\""), + }, + { + name: "no duration value", + input: "abc", + expectedErr: fmt.Errorf("time: invalid duration \"abc\""), + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var d Duration + input, err := json.Marshal(testCase.input) + require.NoError(t, err) + err = json.Unmarshal(input, &d) + + if testCase.expectedResult != nil { + require.Equal(t, (*testCase.expectedResult).Nanoseconds(), d.Nanoseconds()) + } + + if err != nil { + require.Equal(t, testCase.expectedErr.Error(), err.Error()) + } + }) + } +} diff --git a/config/types/keystore.go b/config/types/keystore.go new file mode 100644 index 00000000..620834e9 --- /dev/null +++ b/config/types/keystore.go @@ -0,0 +1,10 @@ +package types + +// KeystoreFileConfig has all the information needed to load a private key from a key store file +type KeystoreFileConfig struct { + // Path is the file path for the key store file + Path string `mapstructure:"Path"` + + // Password is the password to decrypt the key store file + Password string `mapstructure:"Password"` +} diff --git a/dataavailability/config.go b/dataavailability/config.go new file mode 100644 index 00000000..8163e7bc --- /dev/null +++ b/dataavailability/config.go @@ -0,0 +1,9 @@ +package dataavailability + +// DABackendType is the data availability protocol for the CDK +type DABackendType string + +const ( + // DataAvailabilityCommittee is the DAC protocol backend + DataAvailabilityCommittee DABackendType = "DataAvailabilityCommittee" +) diff --git a/dataavailability/dataavailability.go b/dataavailability/dataavailability.go new file mode 100644 index 00000000..a6ae49bd --- /dev/null +++ b/dataavailability/dataavailability.go @@ -0,0 +1,34 @@ +package dataavailability + +import ( + "context" + + ethmanTypes "github.com/0xPolygon/cdk/aggregator/ethmantypes" +) + +// DataAvailability implements an abstract data availability integration +type DataAvailability struct { + backend DABackender +} + +// New creates a DataAvailability instance +func New(backend DABackender) (*DataAvailability, error) { + da := &DataAvailability{ + backend: backend, + } + + return da, da.backend.Init() +} + +// PostSequence sends the sequence data to the data availability backend, and returns the dataAvailabilityMessage +// as expected by the contract +func (d *DataAvailability) PostSequence(ctx context.Context, sequences []ethmanTypes.Sequence) ([]byte, error) { + batchesData := [][]byte{} + for _, batch := range sequences { + // Do not send to the DA backend data that will be stored to L1 + if batch.ForcedBatchTimestamp == 0 { + batchesData = append(batchesData, batch.BatchL2Data) + } + } + return d.backend.PostSequence(ctx, batchesData) +} diff --git a/dataavailability/datacommittee/datacommittee.go b/dataavailability/datacommittee/datacommittee.go new file mode 100644 index 00000000..fe8ee2c1 --- /dev/null +++ b/dataavailability/datacommittee/datacommittee.go @@ -0,0 +1,318 @@ +package datacommittee + +import ( + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + "math/rand" + "sort" + "strings" + + "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygondatacommittee" + "github.com/0xPolygon/cdk-data-availability/client" + daTypes "github.com/0xPolygon/cdk-data-availability/types" + "github.com/0xPolygon/cdk/log" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "golang.org/x/net/context" +) + +const unexpectedHashTemplate = "missmatch on transaction data. Expected hash %s, actual hash: %s" + +// DataCommitteeMember represents a member of the Data Committee +type DataCommitteeMember struct { + Addr common.Address + URL string +} + +// DataCommittee represents a specific committee +type DataCommittee struct { + AddressesHash common.Hash + Members []DataCommitteeMember + RequiredSignatures uint64 +} + +// Backend implements the DAC integration +type Backend struct { + dataCommitteeContract *polygondatacommittee.Polygondatacommittee + privKey *ecdsa.PrivateKey + dataCommitteeClientFactory client.Factory + + committeeMembers []DataCommitteeMember + selectedCommitteeMember int + ctx context.Context +} + +// New creates an instance of Backend +func New( + l1RPCURL string, + dataCommitteeAddr common.Address, + privKey *ecdsa.PrivateKey, + dataCommitteeClientFactory client.Factory, +) (*Backend, error) { + ethClient, err := ethclient.Dial(l1RPCURL) + if err != nil { + log.Errorf("error connecting to %s: %+v", l1RPCURL, err) + return nil, err + } + + dataCommittee, err := polygondatacommittee.NewPolygondatacommittee(dataCommitteeAddr, ethClient) + if err != nil { + return nil, err + } + + return &Backend{ + dataCommitteeContract: dataCommittee, + privKey: privKey, + dataCommitteeClientFactory: dataCommitteeClientFactory, + ctx: context.Background(), + }, nil +} + +// Init loads the DAC to be cached when needed +func (d *Backend) Init() error { + committee, err := d.getCurrentDataCommittee() + if err != nil { + return err + } + selectedCommitteeMember := -1 + if committee != nil { + d.committeeMembers = committee.Members + if len(committee.Members) > 0 { + selectedCommitteeMember = rand.Intn(len(committee.Members)) //nolint:gosec + } + } + d.selectedCommitteeMember = selectedCommitteeMember + return nil +} + +// GetSequence gets backend data one hash at a time. This should be optimized on the DAC side to get them all at once. +func (d *Backend) GetSequence(_ context.Context, hashes []common.Hash, _ []byte) ([][]byte, error) { + // TODO: optimize this on the DAC side by implementing a multi batch retrieve api) + var batchData [][]byte + for _, h := range hashes { + data, err := d.GetBatchL2Data(h) + if err != nil { + return nil, err + } + batchData = append(batchData, data) + } + return batchData, nil +} + +// GetBatchL2Data returns the data from the DAC. It checks that it matches with the expected hash +func (d *Backend) GetBatchL2Data(hash common.Hash) ([]byte, error) { + intialMember := d.selectedCommitteeMember + found := false + for !found && intialMember != -1 { + member := d.committeeMembers[d.selectedCommitteeMember] + log.Infof("trying to get data from %s at %s", member.Addr.Hex(), member.URL) + c := d.dataCommitteeClientFactory.New(member.URL) + data, err := c.GetOffChainData(d.ctx, hash) + if err != nil { + log.Warnf( + "error getting data from DAC node %s at %s: %s", + member.Addr.Hex(), member.URL, err, + ) + d.selectedCommitteeMember = (d.selectedCommitteeMember + 1) % len(d.committeeMembers) + if d.selectedCommitteeMember == intialMember { + break + } + continue + } + actualTransactionsHash := crypto.Keccak256Hash(data) + if actualTransactionsHash != hash { + unexpectedHash := fmt.Errorf( + unexpectedHashTemplate, hash, actualTransactionsHash, + ) + log.Warnf( + "error getting data from DAC node %s at %s: %s", + member.Addr.Hex(), member.URL, unexpectedHash, + ) + d.selectedCommitteeMember = (d.selectedCommitteeMember + 1) % len(d.committeeMembers) + if d.selectedCommitteeMember == intialMember { + break + } + continue + } + return data, nil + } + if err := d.Init(); err != nil { + return nil, fmt.Errorf("error loading data committee: %s", err) + } + return nil, fmt.Errorf("couldn't get the data from any committee member") +} + +type signatureMsg struct { + addr common.Address + signature []byte + err error +} + +// PostSequence sends the sequence data to the data availability backend, and returns the dataAvailabilityMessage +// as expected by the contract +func (s *Backend) PostSequence(ctx context.Context, batchesData [][]byte) ([]byte, error) { + // Get current committee + committee, err := s.getCurrentDataCommittee() + if err != nil { + return nil, err + } + + // Authenticate as trusted sequencer by signing the sequence + sequence := make(daTypes.Sequence, 0, len(batchesData)) + for _, batchData := range batchesData { + sequence = append(sequence, batchData) + } + signedSequence, err := sequence.Sign(s.privKey) + if err != nil { + return nil, err + } + + // Request signatures to all members in parallel + ch := make(chan signatureMsg, len(committee.Members)) + signatureCtx, cancelSignatureCollection := context.WithCancel(ctx) + for _, member := range committee.Members { + go requestSignatureFromMember(signatureCtx, *signedSequence, member, ch) + } + + // Collect signatures + var ( + msgs = make(signatureMsgs, 0, len(committee.Members)) + collectedSignatures uint64 + failedToCollect uint64 + ) + for collectedSignatures < committee.RequiredSignatures { + msg := <-ch + if msg.err != nil { + log.Errorf("error when trying to get signature from %s: %s", msg.addr, msg.err) + failedToCollect++ + if len(committee.Members)-int(failedToCollect) < int(committee.RequiredSignatures) { + cancelSignatureCollection() + return nil, errors.New("too many members failed to send their signature") + } + } else { + log.Infof("received signature from %s", msg.addr) + collectedSignatures++ + } + msgs = append(msgs, msg) + } + + // Stop requesting as soon as we have N valid signatures + cancelSignatureCollection() + + return buildSignaturesAndAddrs(msgs, committee.Members), nil +} + +func requestSignatureFromMember(ctx context.Context, signedSequence daTypes.SignedSequence, member DataCommitteeMember, ch chan signatureMsg) { + select { + case <-ctx.Done(): + return + default: + } + + // request + c := client.New(member.URL) + log.Infof("sending request to sign the sequence to %s at %s", member.Addr.Hex(), member.URL) + signature, err := c.SignSequence(signedSequence) + if err != nil { + ch <- signatureMsg{ + addr: member.Addr, + err: err, + } + return + } + // verify returned signature + signedSequence.Signature = signature + signer, err := signedSequence.Signer() + if err != nil { + ch <- signatureMsg{ + addr: member.Addr, + err: err, + } + return + } + if signer != member.Addr { + ch <- signatureMsg{ + addr: member.Addr, + err: fmt.Errorf("invalid signer. Expected %s, actual %s", member.Addr.Hex(), signer.Hex()), + } + return + } + ch <- signatureMsg{ + addr: member.Addr, + signature: signature, + } +} + +func buildSignaturesAndAddrs(sigs signatureMsgs, members []DataCommitteeMember) []byte { + const ( + sigLen = 65 + ) + res := make([]byte, 0, len(sigs)*sigLen+len(members)*common.AddressLength) + sort.Sort(sigs) + for _, msg := range sigs { + log.Debugf("adding signature %s from %s", common.Bytes2Hex(msg.signature), msg.addr.Hex()) + res = append(res, msg.signature...) + } + for _, member := range members { + log.Debugf("adding addr %s", common.Bytes2Hex(member.Addr.Bytes())) + res = append(res, member.Addr.Bytes()...) + } + log.Debugf("full res %s", common.Bytes2Hex(res)) + return res +} + +type signatureMsgs []signatureMsg + +func (s signatureMsgs) Len() int { return len(s) } +func (s signatureMsgs) Less(i, j int) bool { + return strings.ToUpper(s[i].addr.Hex()) < strings.ToUpper(s[j].addr.Hex()) +} +func (s signatureMsgs) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// getCurrentDataCommittee return the currently registered data committee +func (d *Backend) getCurrentDataCommittee() (*DataCommittee, error) { + addrsHash, err := d.dataCommitteeContract.CommitteeHash(&bind.CallOpts{Pending: false}) + if err != nil { + return nil, fmt.Errorf("error getting CommitteeHash from L1 SC: %w", err) + } + + reqSign, err := d.dataCommitteeContract.RequiredAmountOfSignatures(&bind.CallOpts{Pending: false}) + if err != nil { + return nil, fmt.Errorf("error getting RequiredAmountOfSignatures from L1 SC: %w", err) + } + + members, err := d.getCurrentDataCommitteeMembers() + if err != nil { + return nil, err + } + + return &DataCommittee{ + AddressesHash: addrsHash, + RequiredSignatures: reqSign.Uint64(), + Members: members, + }, nil +} + +// getCurrentDataCommitteeMembers return the currently registered data committee members +func (d *Backend) getCurrentDataCommitteeMembers() ([]DataCommitteeMember, error) { + nMembers, err := d.dataCommitteeContract.GetAmountOfMembers(&bind.CallOpts{Pending: false}) + if err != nil { + return nil, fmt.Errorf("error getting GetAmountOfMembers from L1 SC: %w", err) + } + members := make([]DataCommitteeMember, 0, nMembers.Int64()) + for i := int64(0); i < nMembers.Int64(); i++ { + member, err := d.dataCommitteeContract.Members(&bind.CallOpts{Pending: false}, big.NewInt(i)) + if err != nil { + return nil, fmt.Errorf("error getting Members %d from L1 SC: %w", i, err) + } + members = append(members, DataCommitteeMember{ + Addr: member.Addr, + URL: member.Url, + }) + } + return members, nil +} diff --git a/dataavailability/datacommittee/datacommittee_test.go b/dataavailability/datacommittee/datacommittee_test.go new file mode 100644 index 00000000..2c1c6af8 --- /dev/null +++ b/dataavailability/datacommittee/datacommittee_test.go @@ -0,0 +1,131 @@ +package datacommittee + +import ( + "math/big" + "testing" + + "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygondatacommittee" + "github.com/0xPolygon/cdk/log" + "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/ethereum/go-ethereum/ethclient/simulated" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUpdateDataCommitteeEvent(t *testing.T) { + // Set up testing environment + dac, ethBackend, auth, da := newTestingEnv(t) + + // Update the committee + requiredAmountOfSignatures := big.NewInt(2) + URLs := []string{"1", "2", "3"} + addrs := []common.Address{ + common.HexToAddress("0x1"), + common.HexToAddress("0x2"), + common.HexToAddress("0x3"), + } + addrsBytes := []byte{} + for _, addr := range addrs { + addrsBytes = append(addrsBytes, addr.Bytes()...) + } + _, err := da.SetupCommittee(auth, requiredAmountOfSignatures, URLs, addrsBytes) + require.NoError(t, err) + ethBackend.Commit() + + // Assert the committee update + actualSetup, err := dac.getCurrentDataCommittee() + require.NoError(t, err) + expectedMembers := []DataCommitteeMember{} + expectedSetup := DataCommittee{ + RequiredSignatures: uint64(len(URLs) - 1), + AddressesHash: crypto.Keccak256Hash(addrsBytes), + } + for i, url := range URLs { + expectedMembers = append(expectedMembers, DataCommitteeMember{ + URL: url, + Addr: addrs[i], + }) + } + expectedSetup.Members = expectedMembers + assert.Equal(t, expectedSetup, *actualSetup) +} + +func init() { + log.Init(log.Config{ + Level: "debug", + Outputs: []string{"stderr"}, + }) +} + +// This function prepare the blockchain, the wallet with funds and deploy the smc +func newTestingEnv(t *testing.T) ( + dac *Backend, + ethBackend *simulated.Backend, + auth *bind.TransactOpts, + da *polygondatacommittee.Polygondatacommittee, +) { + t.Helper() + privateKey, err := crypto.GenerateKey() + if err != nil { + log.Fatal(err) + } + auth, err = bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(1337)) + if err != nil { + log.Fatal(err) + } + dac, ethBackend, da, err = newSimulatedDacman(t, auth) + if err != nil { + log.Fatal(err) + } + return dac, ethBackend, auth, da +} + +// NewSimulatedEtherman creates an etherman that uses a simulated blockchain. It's important to notice that the ChainID of the auth +// must be 1337. The address that holds the auth will have an initial balance of 10 ETH +func newSimulatedDacman(t *testing.T, auth *bind.TransactOpts) ( + dacman *Backend, + ethBackend *simulated.Backend, + da *polygondatacommittee.Polygondatacommittee, + err error, +) { + t.Helper() + if auth == nil { + // read only client + return &Backend{}, nil, nil, nil + } + // 10000000 ETH in wei + balance, _ := new(big.Int).SetString("10000000000000000000000000", 10) //nolint:gomnd + address := auth.From + genesisAlloc := map[common.Address]types.Account{ + address: { + Balance: balance, + }, + } + blockGasLimit := uint64(999999999999999999) //nolint:gomnd + client := simulated.NewBackend(genesisAlloc, simulated.WithBlockGasLimit(blockGasLimit)) + + // DAC Setup + _, _, da, err = polygondatacommittee.DeployPolygondatacommittee(auth, client.Client()) + if err != nil { + return &Backend{}, nil, nil, err + } + client.Commit() + _, err = da.Initialize(auth) + if err != nil { + return &Backend{}, nil, nil, err + } + client.Commit() + _, err = da.SetupCommittee(auth, big.NewInt(0), []string{}, []byte{}) + if err != nil { + return &Backend{}, nil, nil, err + } + client.Commit() + + c := &Backend{ + dataCommitteeContract: da, + } + return c, client, da, nil +} diff --git a/dataavailability/interfaces.go b/dataavailability/interfaces.go new file mode 100644 index 00000000..ba86e1a6 --- /dev/null +++ b/dataavailability/interfaces.go @@ -0,0 +1,40 @@ +package dataavailability + +import ( + "context" + + "github.com/ethereum/go-ethereum/common" +) + +// DABackender is an interface for components that store and retrieve batch data +type DABackender interface { + SequenceRetriever + SequenceSender + // Init initializes the DABackend + Init() error +} + +// SequenceSender is used to send provided sequence of batches +type SequenceSender interface { + // PostSequence sends the sequence data to the data availability backend, and returns the dataAvailabilityMessage + // as expected by the contract + PostSequence(ctx context.Context, batchesData [][]byte) ([]byte, error) +} + +// SequenceRetriever is used to retrieve batch data +type SequenceRetriever interface { + // GetSequence retrieves the sequence data from the data availability backend + GetSequence(ctx context.Context, batchHashes []common.Hash, dataAvailabilityMessage []byte) ([][]byte, error) +} + +// BatchDataProvider is used to retrieve batch data +type BatchDataProvider interface { + // GetBatchL2Data retrieve the data of a batch from the DA backend. The returned data must be the pre-image of the hash + GetBatchL2Data(batchNum []uint64, batchHashes []common.Hash, dataAvailabilityMessage []byte) ([][]byte, error) +} + +// DataManager is an interface for components that send and retrieve batch data +type DataManager interface { + BatchDataProvider + SequenceSender +} diff --git a/etherman/aggregator.go b/etherman/aggregator.go new file mode 100644 index 00000000..a85f4f8b --- /dev/null +++ b/etherman/aggregator.go @@ -0,0 +1,121 @@ +package etherman + +import ( + "context" + "encoding/hex" + "errors" + "fmt" + "math/big" + "strings" + + ethmanTypes "github.com/0xPolygon/cdk/aggregator/ethmantypes" + "github.com/0xPolygon/cdk/log" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +// BuildTrustedVerifyBatchesTxData builds a []bytes to be sent to the PoE SC method TrustedVerifyBatches. +func (etherMan *Client) BuildTrustedVerifyBatchesTxData(lastVerifiedBatch, newVerifiedBatch uint64, inputs *ethmanTypes.FinalProofInputs, beneficiary common.Address) (to *common.Address, data []byte, err error) { + opts, err := etherMan.generateRandomAuth() + if err != nil { + return nil, nil, fmt.Errorf("failed to build trusted verify batches, err: %w", err) + } + opts.NoSend = true + // force nonce, gas limit and gas price to avoid querying it from the chain + opts.Nonce = big.NewInt(1) + opts.GasLimit = uint64(1) + opts.GasPrice = big.NewInt(1) + + var newLocalExitRoot [32]byte + copy(newLocalExitRoot[:], inputs.NewLocalExitRoot) + + var newStateRoot [32]byte + copy(newStateRoot[:], inputs.NewStateRoot) + + proof, err := convertProof(inputs.FinalProof.Proof) + if err != nil { + log.Errorf("error converting proof. Error: %v, Proof: %s", err, inputs.FinalProof.Proof) + return nil, nil, err + } + + const pendStateNum = 0 // TODO hardcoded for now until we implement the pending state feature + + tx, err := etherMan.RollupManager.VerifyBatchesTrustedAggregator( + &opts, + etherMan.RollupID, + pendStateNum, + lastVerifiedBatch, + newVerifiedBatch, + newLocalExitRoot, + newStateRoot, + beneficiary, + proof, + ) + if err != nil { + if parsedErr, ok := tryParseError(err); ok { + err = parsedErr + } + return nil, nil, err + } + + return tx.To(), tx.Data(), nil +} + +// GetBatchAccInputHash gets the batch accumulated input hash from the ethereum +func (etherman *Client) GetBatchAccInputHash(ctx context.Context, batchNumber uint64) (common.Hash, error) { + rollupData, err := etherman.RollupManager.GetRollupSequencedBatches(&bind.CallOpts{Pending: false}, etherman.RollupID, batchNumber) + if err != nil { + return common.Hash{}, err + } + return rollupData.AccInputHash, nil +} + +// GetRollupId returns the rollup id +func (etherMan *Client) GetRollupId() uint32 { + return etherMan.RollupID +} + +// generateRandomAuth generates an authorization instance from a +// randomly generated private key to be used to estimate gas for PoE +// operations NOT restricted to the Trusted Sequencer +func (etherMan *Client) generateRandomAuth() (bind.TransactOpts, error) { + privateKey, err := crypto.GenerateKey() + if err != nil { + return bind.TransactOpts{}, errors.New("failed to generate a private key to estimate L1 txs") + } + chainID := big.NewInt(0).SetUint64(etherMan.l1Cfg.L1ChainID) + auth, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID) + if err != nil { + return bind.TransactOpts{}, errors.New("failed to generate a fake authorization to estimate L1 txs") + } + + return *auth, nil +} + +func convertProof(p string) ([24][32]byte, error) { + if len(p) != 24*32*2+2 { + return [24][32]byte{}, fmt.Errorf("invalid proof length. Length: %d", len(p)) + } + p = strings.TrimPrefix(p, "0x") + proof := [24][32]byte{} + for i := 0; i < 24; i++ { + data := p[i*64 : (i+1)*64] + p, err := DecodeBytes(&data) + if err != nil { + return [24][32]byte{}, fmt.Errorf("failed to decode proof, err: %w", err) + } + var aux [32]byte + copy(aux[:], p) + proof[i] = aux + } + return proof, nil +} + +// DecodeBytes decodes a hex string into a []byte +func DecodeBytes(val *string) ([]byte, error) { + if val == nil { + return []byte{}, nil + } + return hex.DecodeString(strings.TrimPrefix(*val, "0x")) +} diff --git a/etherman/config.go b/etherman/config.go new file mode 100644 index 00000000..15904d91 --- /dev/null +++ b/etherman/config.go @@ -0,0 +1,17 @@ +package etherman + +import "github.com/0xPolygonHermez/zkevm-ethtx-manager/etherman" + +// Config represents the configuration of the etherman +type Config struct { + // URL is the URL of the Ethereum node for L1 + URL string `mapstructure:"URL"` + + EthermanConfig etherman.Config + + // IsValidiumMode is a flag to indicate if the sequence sender is running in validium mode + IsValidiumMode bool + + // ForkIDChunkSize is the max interval for each call to L1 provider to get the forkIDs + ForkIDChunkSize uint64 `mapstructure:"ForkIDChunkSize"` +} diff --git a/etherman/errors.go b/etherman/errors.go new file mode 100644 index 00000000..38a4e47d --- /dev/null +++ b/etherman/errors.go @@ -0,0 +1,52 @@ +package etherman + +import ( + "errors" + "strings" +) + +var ( + // ErrGasRequiredExceedsAllowance gas required exceeds the allowance + ErrGasRequiredExceedsAllowance = errors.New("gas required exceeds allowance") + // ErrContentLengthTooLarge content length is too large + ErrContentLengthTooLarge = errors.New("content length too large") + //ErrTimestampMustBeInsideRange Timestamp must be inside range + ErrTimestampMustBeInsideRange = errors.New("timestamp must be inside range") + //ErrInsufficientAllowance insufficient allowance + ErrInsufficientAllowance = errors.New("insufficient allowance") + // ErrBothGasPriceAndMaxFeeGasAreSpecified both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified + ErrBothGasPriceAndMaxFeeGasAreSpecified = errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") + // ErrMaxFeeGasAreSpecifiedButLondonNotActive maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet + ErrMaxFeeGasAreSpecifiedButLondonNotActive = errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") + // ErrNoSigner no signer to authorize the transaction with + ErrNoSigner = errors.New("no signer to authorize the transaction with") + // ErrMissingTrieNode means that a node is missing on the trie + ErrMissingTrieNode = errors.New("missing trie node") + // ErrNotFound is used when the object is not found + ErrNotFound = errors.New("not found") + // ErrPrivateKeyNotFound used when the provided sender does not have a private key registered to be used + ErrPrivateKeyNotFound = errors.New("can't find sender private key to sign tx") + + errorsCache = map[string]error{ + ErrGasRequiredExceedsAllowance.Error(): ErrGasRequiredExceedsAllowance, + ErrContentLengthTooLarge.Error(): ErrContentLengthTooLarge, + ErrTimestampMustBeInsideRange.Error(): ErrTimestampMustBeInsideRange, + ErrInsufficientAllowance.Error(): ErrInsufficientAllowance, + ErrBothGasPriceAndMaxFeeGasAreSpecified.Error(): ErrBothGasPriceAndMaxFeeGasAreSpecified, + ErrMaxFeeGasAreSpecifiedButLondonNotActive.Error(): ErrMaxFeeGasAreSpecifiedButLondonNotActive, + ErrNoSigner.Error(): ErrNoSigner, + ErrMissingTrieNode.Error(): ErrMissingTrieNode, + } +) + +func tryParseError(err error) (error, bool) { + parsedError, exists := errorsCache[err.Error()] + if !exists { + for errStr, actualErr := range errorsCache { + if strings.Contains(err.Error(), errStr) { + return actualErr, true + } + } + } + return parsedError, exists +} diff --git a/etherman/errors_test.go b/etherman/errors_test.go new file mode 100644 index 00000000..186768e6 --- /dev/null +++ b/etherman/errors_test.go @@ -0,0 +1,37 @@ +package etherman + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTryParseWithExactMatch(t *testing.T) { + expected := ErrTimestampMustBeInsideRange + smartContractErr := expected + + actualErr, ok := tryParseError(smartContractErr) + + assert.ErrorIs(t, actualErr, expected) + assert.True(t, ok) +} + +func TestTryParseWithContains(t *testing.T) { + expected := ErrTimestampMustBeInsideRange + smartContractErr := fmt.Errorf(" execution reverted: ProofOfEfficiency::sequenceBatches: %s", expected) + + actualErr, ok := tryParseError(smartContractErr) + + assert.ErrorIs(t, actualErr, expected) + assert.True(t, ok) +} + +func TestTryParseWithNonExistingErr(t *testing.T) { + smartContractErr := fmt.Errorf("some non-existing err") + + actualErr, ok := tryParseError(smartContractErr) + + assert.Nil(t, actualErr) + assert.False(t, ok) +} diff --git a/etherman/etherman.go b/etherman/etherman.go new file mode 100644 index 00000000..b7f2da47 --- /dev/null +++ b/etherman/etherman.go @@ -0,0 +1,713 @@ +package etherman + +import ( + "context" + "crypto/ecdsa" + "encoding/json" + "errors" + "fmt" + "math/big" + "os" + "path/filepath" + "strings" + "time" + + "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/idataavailabilityprotocol" + "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonrollupmanager" + "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonvalidiumetrog" + ethmanTypes "github.com/0xPolygon/cdk/aggregator/ethmantypes" + "github.com/0xPolygon/cdk/log" + "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/accounts/keystore" + "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" + "golang.org/x/crypto/sha3" +) + +// EventOrder is the type used to identify the events order +type EventOrder string + +const ( + // GlobalExitRootsOrder identifies a GlobalExitRoot event + GlobalExitRootsOrder EventOrder = "GlobalExitRoots" + // L1InfoTreeOrder identifies a L1InTree event + L1InfoTreeOrder EventOrder = "L1InfoTreeOrder" + // SequenceBatchesOrder identifies a VerifyBatch event + SequenceBatchesOrder EventOrder = "SequenceBatches" + // ForcedBatchesOrder identifies a ForcedBatches event + ForcedBatchesOrder EventOrder = "ForcedBatches" + // TrustedVerifyBatchOrder identifies a TrustedVerifyBatch event + TrustedVerifyBatchOrder EventOrder = "TrustedVerifyBatch" + // VerifyBatchOrder identifies a VerifyBatch event + VerifyBatchOrder EventOrder = "VerifyBatch" + // SequenceForceBatchesOrder identifies a SequenceForceBatches event + SequenceForceBatchesOrder EventOrder = "SequenceForceBatches" + // ForkIDsOrder identifies an updateZkevmVersion event + ForkIDsOrder EventOrder = "forkIDs" +) + +type ethereumClient interface { + ethereum.ChainReader + ethereum.ChainStateReader + ethereum.ContractCaller + ethereum.GasEstimator + ethereum.GasPricer + ethereum.GasPricer1559 + ethereum.LogFilterer + ethereum.TransactionReader + ethereum.TransactionSender + + bind.DeployBackend +} + +// L1Config represents the configuration of the network used in L1 +type L1Config struct { + // Chain ID of the L1 network + L1ChainID uint64 `json:"chainId"` + // ZkEVMAddr Address of the L1 contract polygonZkEVMAddress + ZkEVMAddr common.Address `json:"polygonZkEVMAddress"` + // RollupManagerAddr Address of the L1 contract + RollupManagerAddr common.Address `json:"polygonRollupManagerAddress"` + // PolAddr Address of the L1 Pol token Contract + PolAddr common.Address `json:"polTokenAddress"` + // GlobalExitRootManagerAddr Address of the L1 GlobalExitRootManager contract + GlobalExitRootManagerAddr common.Address `json:"polygonZkEVMGlobalExitRootAddress"` +} + +// Client is a simple implementation of EtherMan. +type Client struct { + EthClient ethereumClient + ZkEVM *polygonvalidiumetrog.Polygonvalidiumetrog + RollupManager *polygonrollupmanager.Polygonrollupmanager + DAProtocol *idataavailabilityprotocol.Idataavailabilityprotocol + // Pol *pol.Pol + SCAddresses []common.Address + + RollupID uint32 + + l1Cfg L1Config + cfg Config + auth map[common.Address]bind.TransactOpts // empty in case of read-only client +} + +// NewClient creates a new etherman. +func NewClient(cfg Config, l1Config L1Config) (*Client, error) { + // Connect to ethereum node + ethClient, err := ethclient.Dial(cfg.EthermanConfig.URL) + if err != nil { + log.Errorf("error connecting to %s: %+v", cfg.EthermanConfig.URL, err) + return nil, err + } + + // Create smc clients + zkevm, err := polygonvalidiumetrog.NewPolygonvalidiumetrog(l1Config.ZkEVMAddr, ethClient) + if err != nil { + return nil, err + } + + rollupManager, err := polygonrollupmanager.NewPolygonrollupmanager(l1Config.RollupManagerAddr, ethClient) + if err != nil { + return nil, err + } + + var scAddresses []common.Address + scAddresses = append(scAddresses, l1Config.ZkEVMAddr, l1Config.RollupManagerAddr, l1Config.GlobalExitRootManagerAddr) + + // Get RollupID + rollupID, err := rollupManager.RollupAddressToID(&bind.CallOpts{Pending: false}, l1Config.ZkEVMAddr) + if err != nil { + return nil, err + } + log.Debug("rollupID: ", rollupID) + + client := &Client{ + EthClient: ethClient, + ZkEVM: zkevm, + RollupManager: rollupManager, + SCAddresses: scAddresses, + RollupID: rollupID, + l1Cfg: l1Config, + cfg: cfg, + auth: map[common.Address]bind.TransactOpts{}, + } + + if cfg.IsValidiumMode { + dapAddr, err := zkevm.DataAvailabilityProtocol(&bind.CallOpts{Pending: false}) + if err != nil { + return nil, err + } + + client.DAProtocol, err = idataavailabilityprotocol.NewIdataavailabilityprotocol(dapAddr, ethClient) + if err != nil { + return nil, err + } + } + + return client, nil +} + +// Order contains the event order to let the synchronizer store the information following this order. +type Order struct { + Name EventOrder + Pos int +} + +// WaitTxToBeMined waits for an L1 tx to be mined. It will return error if the tx is reverted or timeout is exceeded +func (etherMan *Client) WaitTxToBeMined(ctx context.Context, tx *types.Transaction, timeout time.Duration) (bool, error) { + // err := operations.WaitTxToBeMined(ctx, etherMan.EthClient, tx, timeout) + // if errors.Is(err, context.DeadlineExceeded) { + // return false, nil + // } + // if err != nil { + // return false, err + // } + return true, nil +} + +// BuildSequenceBatchesTx builds a tx to be sent to the PoE SC method SequenceBatches. +func (etherMan *Client) BuildSequenceBatchesTx(sender common.Address, sequences []ethmanTypes.Sequence, maxSequenceTimestamp uint64, lastSequencedBatchNumber uint64, l2Coinbase common.Address, dataAvailabilityMessage []byte) (*types.Transaction, error) { + opts, err := etherMan.getAuthByAddress(sender) + if err == ErrNotFound { + return nil, fmt.Errorf("failed to build sequence batches, err: %w", ErrPrivateKeyNotFound) + } + + opts.NoSend = true + + // force nonce, gas limit and gas price to avoid querying it from the chain + opts.Nonce = big.NewInt(1) + opts.GasLimit = uint64(1) + opts.GasPrice = big.NewInt(1) + + return etherMan.sequenceBatches(opts, sequences, maxSequenceTimestamp, lastSequencedBatchNumber, l2Coinbase, dataAvailabilityMessage) +} + +func (etherMan *Client) sequenceBatches(opts bind.TransactOpts, sequences []ethmanTypes.Sequence, maxSequenceTimestamp uint64, lastSequencedBatchNumber uint64, l2Coinbase common.Address, dataAvailabilityMessage []byte) (tx *types.Transaction, err error) { + if etherMan.cfg.IsValidiumMode { + return etherMan.sequenceBatchesValidium(opts, sequences, maxSequenceTimestamp, lastSequencedBatchNumber, l2Coinbase, dataAvailabilityMessage) + } + + return etherMan.sequenceBatchesRollup(opts, sequences, maxSequenceTimestamp, lastSequencedBatchNumber, l2Coinbase) +} + +func (etherMan *Client) sequenceBatchesRollup(opts bind.TransactOpts, sequences []ethmanTypes.Sequence, maxSequenceTimestamp uint64, lastSequencedBatchNumber uint64, l2Coinbase common.Address) (*types.Transaction, error) { + batches := make([]polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData, len(sequences)) + for i, seq := range sequences { + var ger common.Hash + if seq.ForcedBatchTimestamp > 0 { + ger = seq.GlobalExitRoot + } + + batches[i] = polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData{ + Transactions: seq.BatchL2Data, + ForcedGlobalExitRoot: ger, + ForcedTimestamp: uint64(seq.ForcedBatchTimestamp), + ForcedBlockHashL1: seq.PrevBlockHash, + } + } + + tx, err := etherMan.ZkEVM.SequenceBatches(&opts, batches, maxSequenceTimestamp, lastSequencedBatchNumber, l2Coinbase) + if err != nil { + log.Debugf("Batches to send: %+v", batches) + log.Debug("l2CoinBase: ", l2Coinbase) + log.Debug("Sequencer address: ", opts.From) + a, err2 := polygonvalidiumetrog.PolygonvalidiumetrogMetaData.GetAbi() + if err2 != nil { + log.Error("error getting abi. Error: ", err2) + } + input, err3 := a.Pack("sequenceBatches", batches, maxSequenceTimestamp, lastSequencedBatchNumber, l2Coinbase) + if err3 != nil { + log.Error("error packing call. Error: ", err3) + } + ctx := context.Background() + var b string + block, err4 := etherMan.EthClient.BlockByNumber(ctx, nil) + if err4 != nil { + log.Error("error getting blockNumber. Error: ", err4) + b = "latest" + } else { + b = fmt.Sprintf("%x", block.Number()) + } + log.Warnf(`Use the next command to debug it manually. + curl --location --request POST 'http://localhost:8545' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{"from": "%s","to":"%s","data":"0x%s"},"0x%s"], + "id": 1 + }'`, opts.From, etherMan.l1Cfg.ZkEVMAddr, common.Bytes2Hex(input), b) + if parsedErr, ok := tryParseError(err); ok { + err = parsedErr + } + } + + return tx, err +} + +func (etherMan *Client) sequenceBatchesValidium(opts bind.TransactOpts, sequences []ethmanTypes.Sequence, maxSequenceTimestamp uint64, lastSequencedBatchNumber uint64, l2Coinbase common.Address, dataAvailabilityMessage []byte) (*types.Transaction, error) { + batches := make([]polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData, len(sequences)) + for i, seq := range sequences { + var ger common.Hash + if seq.ForcedBatchTimestamp > 0 { + ger = seq.GlobalExitRoot + } + + batches[i] = polygonvalidiumetrog.PolygonValidiumEtrogValidiumBatchData{ + TransactionsHash: crypto.Keccak256Hash(seq.BatchL2Data), + ForcedGlobalExitRoot: ger, + ForcedTimestamp: uint64(seq.ForcedBatchTimestamp), + ForcedBlockHashL1: seq.PrevBlockHash, + } + } + + tx, err := etherMan.ZkEVM.SequenceBatchesValidium(&opts, batches, maxSequenceTimestamp, lastSequencedBatchNumber, l2Coinbase, dataAvailabilityMessage) + if err != nil { + log.Debugf("Batches to send: %+v", batches) + log.Debug("l2CoinBase: ", l2Coinbase) + log.Debug("Sequencer address: ", opts.From) + a, err2 := polygonvalidiumetrog.PolygonvalidiumetrogMetaData.GetAbi() + if err2 != nil { + log.Error("error getting abi. Error: ", err2) + } + input, err3 := a.Pack("sequenceBatchesValidium", batches, maxSequenceTimestamp, lastSequencedBatchNumber, l2Coinbase, dataAvailabilityMessage) + if err3 != nil { + log.Error("error packing call. Error: ", err3) + } + ctx := context.Background() + var b string + block, err4 := etherMan.EthClient.BlockByNumber(ctx, nil) + if err4 != nil { + log.Error("error getting blockNumber. Error: ", err4) + b = "latest" + } else { + b = fmt.Sprintf("%x", block.Number()) + } + log.Warnf(`Use the next command to debug it manually. + curl --location --request POST 'http://localhost:8545' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{"from": "%s","to":"%s","data":"0x%s"},"0x%s"], + "id": 1 + }'`, opts.From, etherMan.l1Cfg.ZkEVMAddr, common.Bytes2Hex(input), b) + if parsedErr, ok := tryParseError(err); ok { + err = parsedErr + } + } + + return tx, err +} + +// GetSendSequenceFee get super/trusted sequencer fee +func (etherMan *Client) GetSendSequenceFee(numBatches uint64) (*big.Int, error) { + f, err := etherMan.RollupManager.GetBatchFee(&bind.CallOpts{Pending: false}) + if err != nil { + return nil, err + } + fee := new(big.Int).Mul(f, new(big.Int).SetUint64(numBatches)) + return fee, nil +} + +// TrustedSequencer gets trusted sequencer address +func (etherMan *Client) TrustedSequencer() (common.Address, error) { + return etherMan.ZkEVM.TrustedSequencer(&bind.CallOpts{Pending: false}) +} + +func decodeSequences(txData []byte, lastBatchNumber uint64, sequencer common.Address, txHash common.Hash, nonce uint64, l1InfoRoot common.Hash) ([]SequencedBatch, error) { + // Extract coded txs. + // Load contract ABI + smcAbi, err := abi.JSON(strings.NewReader(polygonvalidiumetrog.PolygonvalidiumetrogABI)) + if err != nil { + return nil, err + } + + // Recover Method from signature and ABI + method, err := smcAbi.MethodById(txData[:4]) + if err != nil { + return nil, err + } + + // Unpack method inputs + data, err := method.Inputs.Unpack(txData[4:]) + if err != nil { + return nil, err + } + var sequences []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData + bytedata, err := json.Marshal(data[0]) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytedata, &sequences) + if err != nil { + return nil, err + } + coinbase := (data[1]).(common.Address) + sequencedBatches := make([]SequencedBatch, len(sequences)) + for i, seq := range sequences { + bn := lastBatchNumber - uint64(len(sequences)-(i+1)) + s := seq + sequencedBatches[i] = SequencedBatch{ + BatchNumber: bn, + L1InfoRoot: &l1InfoRoot, + SequencerAddr: sequencer, + TxHash: txHash, + Nonce: nonce, + Coinbase: coinbase, + PolygonRollupBaseEtrogBatchData: &s, + } + } + + return sequencedBatches, nil +} + +func (etherMan *Client) verifyBatches( + ctx context.Context, + vLog types.Log, + blocks *[]Block, + blocksOrder *map[common.Hash][]Order, + numBatch uint64, + stateRoot common.Hash, + aggregator common.Address, + orderName EventOrder) error { + var verifyBatch VerifiedBatch + verifyBatch.BlockNumber = vLog.BlockNumber + verifyBatch.BatchNumber = numBatch + verifyBatch.TxHash = vLog.TxHash + verifyBatch.StateRoot = stateRoot + verifyBatch.Aggregator = aggregator + + if len(*blocks) == 0 || ((*blocks)[len(*blocks)-1].BlockHash != vLog.BlockHash || (*blocks)[len(*blocks)-1].BlockNumber != vLog.BlockNumber) { + fullBlock, err := etherMan.EthClient.BlockByHash(ctx, vLog.BlockHash) + if err != nil { + return fmt.Errorf("error getting hashParent. BlockNumber: %d. Error: %w", vLog.BlockNumber, err) + } + block := prepareBlock(vLog, time.Unix(int64(fullBlock.Time()), 0), fullBlock) + block.VerifiedBatches = append(block.VerifiedBatches, verifyBatch) + *blocks = append(*blocks, block) + } else if (*blocks)[len(*blocks)-1].BlockHash == vLog.BlockHash && (*blocks)[len(*blocks)-1].BlockNumber == vLog.BlockNumber { + (*blocks)[len(*blocks)-1].VerifiedBatches = append((*blocks)[len(*blocks)-1].VerifiedBatches, verifyBatch) + } else { + log.Error("Error processing verifyBatch event. BlockHash:", vLog.BlockHash, ". BlockNumber: ", vLog.BlockNumber) + return fmt.Errorf("error processing verifyBatch event") + } + or := Order{ + Name: orderName, + Pos: len((*blocks)[len(*blocks)-1].VerifiedBatches) - 1, + } + (*blocksOrder)[(*blocks)[len(*blocks)-1].BlockHash] = append((*blocksOrder)[(*blocks)[len(*blocks)-1].BlockHash], or) + return nil +} + +func decodeSequencedForceBatches(txData []byte, lastBatchNumber uint64, sequencer common.Address, txHash common.Hash, block *types.Block, nonce uint64) ([]SequencedForceBatch, error) { + // Extract coded txs. + // Load contract ABI + abi, err := abi.JSON(strings.NewReader(polygonvalidiumetrog.PolygonvalidiumetrogABI)) + if err != nil { + return nil, err + } + + // Recover Method from signature and ABI + method, err := abi.MethodById(txData[:4]) + if err != nil { + return nil, err + } + + // Unpack method inputs + data, err := method.Inputs.Unpack(txData[4:]) + if err != nil { + return nil, err + } + + var forceBatches []polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData + bytedata, err := json.Marshal(data[0]) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytedata, &forceBatches) + if err != nil { + return nil, err + } + + sequencedForcedBatches := make([]SequencedForceBatch, len(forceBatches)) + for i, force := range forceBatches { + bn := lastBatchNumber - uint64(len(forceBatches)-(i+1)) + sequencedForcedBatches[i] = SequencedForceBatch{ + BatchNumber: bn, + Coinbase: sequencer, + TxHash: txHash, + Timestamp: time.Unix(int64(block.Time()), 0), + Nonce: nonce, + PolygonRollupBaseEtrogBatchData: force, + } + } + return sequencedForcedBatches, nil +} + +func prepareBlock(vLog types.Log, t time.Time, fullBlock *types.Block) Block { + var block Block + block.BlockNumber = vLog.BlockNumber + block.BlockHash = vLog.BlockHash + block.ParentHash = fullBlock.ParentHash() + block.ReceivedAt = t + return block +} + +func hash(data ...[32]byte) [32]byte { + var res [32]byte + hash := sha3.NewLegacyKeccak256() + for _, d := range data { + hash.Write(d[:]) //nolint:errcheck,gosec + } + copy(res[:], hash.Sum(nil)) + return res +} + +// HeaderByNumber returns a block header from the current canonical chain. If number is +// nil, the latest known header is returned. +func (etherMan *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + return etherMan.EthClient.HeaderByNumber(ctx, number) +} + +// EthBlockByNumber function retrieves the ethereum block information by ethereum block number. +func (etherMan *Client) EthBlockByNumber(ctx context.Context, blockNumber uint64) (*types.Block, error) { + block, err := etherMan.EthClient.BlockByNumber(ctx, new(big.Int).SetUint64(blockNumber)) + if err != nil { + if errors.Is(err, ethereum.NotFound) || err.Error() == "block does not exist in blockchain" { + return nil, ErrNotFound + } + return nil, err + } + return block, nil +} + +// GetLatestBatchNumber function allows to retrieve the latest proposed batch in the smc +func (etherMan *Client) GetLatestBatchNumber() (uint64, error) { + rollupData, err := etherMan.RollupManager.RollupIDToRollupData(&bind.CallOpts{Pending: false}, etherMan.RollupID) + if err != nil { + return 0, err + } + return rollupData.LastBatchSequenced, nil +} + +// GetLatestBlockNumber gets the latest block number from the ethereum +func (etherMan *Client) GetLatestBlockNumber(ctx context.Context) (uint64, error) { + return etherMan.getBlockNumber(ctx, rpc.LatestBlockNumber) +} + +// GetSafeBlockNumber gets the safe block number from the ethereum +func (etherMan *Client) GetSafeBlockNumber(ctx context.Context) (uint64, error) { + return etherMan.getBlockNumber(ctx, rpc.SafeBlockNumber) +} + +// GetFinalizedBlockNumber gets the Finalized block number from the ethereum +func (etherMan *Client) GetFinalizedBlockNumber(ctx context.Context) (uint64, error) { + return etherMan.getBlockNumber(ctx, rpc.FinalizedBlockNumber) +} + +// getBlockNumber gets the block header by the provided block number from the ethereum +func (etherMan *Client) getBlockNumber(ctx context.Context, blockNumber rpc.BlockNumber) (uint64, error) { + header, err := etherMan.EthClient.HeaderByNumber(ctx, big.NewInt(int64(blockNumber))) + if err != nil || header == nil { + return 0, err + } + return header.Number.Uint64(), nil +} + +// GetLatestBlockTimestamp gets the latest block timestamp from the ethereum +func (etherMan *Client) GetLatestBlockTimestamp(ctx context.Context) (uint64, error) { + header, err := etherMan.EthClient.HeaderByNumber(ctx, nil) + if err != nil || header == nil { + return 0, err + } + return header.Time, nil +} + +// GetLatestVerifiedBatchNum gets latest verified batch from ethereum +func (etherMan *Client) GetLatestVerifiedBatchNum() (uint64, error) { + rollupData, err := etherMan.RollupManager.RollupIDToRollupData(&bind.CallOpts{Pending: false}, etherMan.RollupID) + if err != nil { + return 0, err + } + return rollupData.LastVerifiedBatch, nil +} + +// GetTx function get ethereum tx +func (etherMan *Client) GetTx(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) { + return etherMan.EthClient.TransactionByHash(ctx, txHash) +} + +// GetTxReceipt function gets ethereum tx receipt +func (etherMan *Client) GetTxReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { + return etherMan.EthClient.TransactionReceipt(ctx, txHash) +} + +// GetTrustedSequencerURL Gets the trusted sequencer url from rollup smc +func (etherMan *Client) GetTrustedSequencerURL() (string, error) { + return etherMan.ZkEVM.TrustedSequencerURL(&bind.CallOpts{Pending: false}) +} + +// GetL2ChainID returns L2 Chain ID +func (etherMan *Client) GetL2ChainID() (uint64, error) { + rollupData, err := etherMan.RollupManager.RollupIDToRollupData(&bind.CallOpts{Pending: false}, etherMan.RollupID) + log.Debug("chainID read from rollupManager: ", rollupData.ChainID) + if err != nil { + log.Debug("error from rollupManager: ", err) + return 0, err + } else if rollupData.ChainID == 0 { + return rollupData.ChainID, fmt.Errorf("error: chainID received is 0") + } + return rollupData.ChainID, nil +} + +// SendTx sends a tx to L1 +func (etherMan *Client) SendTx(ctx context.Context, tx *types.Transaction) error { + return etherMan.EthClient.SendTransaction(ctx, tx) +} + +// CurrentNonce returns the current nonce for the provided account +func (etherMan *Client) CurrentNonce(ctx context.Context, account common.Address) (uint64, error) { + return etherMan.EthClient.NonceAt(ctx, account, nil) +} + +// EstimateGas returns the estimated gas for the tx +func (etherMan *Client) EstimateGas(ctx context.Context, from common.Address, to *common.Address, value *big.Int, data []byte) (uint64, error) { + return etherMan.EthClient.EstimateGas(ctx, ethereum.CallMsg{ + From: from, + To: to, + Value: value, + Data: data, + }) +} + +// CheckTxWasMined check if a tx was already mined +func (etherMan *Client) CheckTxWasMined(ctx context.Context, txHash common.Hash) (bool, *types.Receipt, error) { + receipt, err := etherMan.EthClient.TransactionReceipt(ctx, txHash) + if errors.Is(err, ethereum.NotFound) { + return false, nil, nil + } else if err != nil { + return false, nil, err + } + + return true, receipt, nil +} + +// SignTx tries to sign a transaction accordingly to the provided sender +func (etherMan *Client) SignTx(ctx context.Context, sender common.Address, tx *types.Transaction) (*types.Transaction, error) { + auth, err := etherMan.getAuthByAddress(sender) + if err == ErrNotFound { + return nil, ErrPrivateKeyNotFound + } + signedTx, err := auth.Signer(auth.From, tx) + if err != nil { + return nil, err + } + return signedTx, nil +} + +// GetRevertMessage tries to get a revert message of a transaction +func (etherMan *Client) GetRevertMessage(ctx context.Context, tx *types.Transaction) (string, error) { + // if tx == nil { + // return "", nil + // } + + // receipt, err := etherMan.GetTxReceipt(ctx, tx.Hash()) + // if err != nil { + // return "", err + // } + + // if receipt.Status == types.ReceiptStatusFailed { + // revertMessage, err := operations.RevertReason(ctx, etherMan.EthClient, tx, receipt.BlockNumber) + // if err != nil { + // return "", err + // } + // return revertMessage, nil + // } + return "", nil +} + +// AddOrReplaceAuth adds an authorization or replace an existent one to the same account +func (etherMan *Client) AddOrReplaceAuth(auth bind.TransactOpts) error { + log.Infof("added or replaced authorization for address: %v", auth.From.String()) + etherMan.auth[auth.From] = auth + return nil +} + +// LoadAuthFromKeyStore loads an authorization from a key store file +func (etherMan *Client) LoadAuthFromKeyStore(path, password string) (*bind.TransactOpts, *ecdsa.PrivateKey, error) { + auth, pk, err := newAuthFromKeystore(path, password, etherMan.l1Cfg.L1ChainID) + if err != nil { + return nil, nil, err + } + + log.Infof("loaded authorization for address: %v", auth.From.String()) + etherMan.auth[auth.From] = auth + return &auth, pk, nil +} + +// newKeyFromKeystore creates an instance of a keystore key from a keystore file +func newKeyFromKeystore(path, password string) (*keystore.Key, error) { + if path == "" && password == "" { + return nil, nil + } + keystoreEncrypted, err := os.ReadFile(filepath.Clean(path)) + if err != nil { + return nil, err + } + log.Infof("decrypting key from: %v", path) + key, err := keystore.DecryptKey(keystoreEncrypted, password) + if err != nil { + return nil, err + } + return key, nil +} + +// newAuthFromKeystore an authorization instance from a keystore file +func newAuthFromKeystore(path, password string, chainID uint64) (bind.TransactOpts, *ecdsa.PrivateKey, error) { + log.Infof("reading key from: %v", path) + key, err := newKeyFromKeystore(path, password) + if err != nil { + return bind.TransactOpts{}, nil, err + } + if key == nil { + return bind.TransactOpts{}, nil, nil + } + auth, err := bind.NewKeyedTransactorWithChainID(key.PrivateKey, new(big.Int).SetUint64(chainID)) + if err != nil { + return bind.TransactOpts{}, nil, err + } + return *auth, key.PrivateKey, nil +} + +// getAuthByAddress tries to get an authorization from the authorizations map +func (etherMan *Client) getAuthByAddress(addr common.Address) (bind.TransactOpts, error) { + auth, found := etherMan.auth[addr] + if !found { + return bind.TransactOpts{}, ErrNotFound + } + return auth, nil +} + +// GetLatestBlockHeader gets the latest block header from the ethereum +func (etherMan *Client) GetLatestBlockHeader(ctx context.Context) (*types.Header, error) { + header, err := etherMan.EthClient.HeaderByNumber(ctx, big.NewInt(int64(rpc.LatestBlockNumber))) + if err != nil || header == nil { + return nil, err + } + return header, nil +} + +// GetDAProtocolAddr returns the address of the data availability protocol +func (etherMan *Client) GetDAProtocolAddr() (common.Address, error) { + return etherMan.ZkEVM.DataAvailabilityProtocol(&bind.CallOpts{Pending: false}) +} + +// GetDAProtocolName returns the name of the data availability protocol +func (etherMan *Client) GetDAProtocolName() (string, error) { + return etherMan.DAProtocol.GetProcotolName(&bind.CallOpts{Pending: false}) +} diff --git a/etherman/types.go b/etherman/types.go new file mode 100644 index 00000000..f7ba1df6 --- /dev/null +++ b/etherman/types.go @@ -0,0 +1,130 @@ +package etherman + +import ( + "fmt" + "math/big" + "time" + + "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonvalidiumetrog" + "github.com/ethereum/go-ethereum/common" +) + +// Block struct +type Block struct { + BlockNumber uint64 + BlockHash common.Hash + ParentHash common.Hash + ForcedBatches []ForcedBatch + SequencedBatches [][]SequencedBatch + VerifiedBatches []VerifiedBatch + SequencedForceBatches [][]SequencedForceBatch + ForkIDs []ForkID + ReceivedAt time.Time + // GER data + GlobalExitRoots, L1InfoTree []GlobalExitRoot +} + +// GlobalExitRoot struct +type GlobalExitRoot struct { + BlockNumber uint64 + MainnetExitRoot common.Hash + RollupExitRoot common.Hash + GlobalExitRoot common.Hash + Timestamp time.Time + PreviousBlockHash common.Hash +} + +// PolygonZkEVMBatchData represents PolygonZkEVMBatchData +type PolygonZkEVMBatchData struct { + Transactions []byte + GlobalExitRoot [32]byte + Timestamp uint64 + MinForcedTimestamp uint64 +} + +// SequencedBatch represents virtual batch +type SequencedBatch struct { + BatchNumber uint64 + L1InfoRoot *common.Hash + SequencerAddr common.Address + TxHash common.Hash + Nonce uint64 + Coinbase common.Address + // Struct used in preEtrog forks + *PolygonZkEVMBatchData + // Struct used in Etrog + *polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData +} + +// ForcedBatch represents a ForcedBatch +type ForcedBatch struct { + BlockNumber uint64 + ForcedBatchNumber uint64 + Sequencer common.Address + GlobalExitRoot common.Hash + RawTxsData []byte + ForcedAt time.Time +} + +// VerifiedBatch represents a VerifiedBatch +type VerifiedBatch struct { + BlockNumber uint64 + BatchNumber uint64 + Aggregator common.Address + StateRoot common.Hash + TxHash common.Hash +} + +// SequencedForceBatch is a sturct to track the ForceSequencedBatches event. +type SequencedForceBatch struct { + BatchNumber uint64 + Coinbase common.Address + TxHash common.Hash + Timestamp time.Time + Nonce uint64 + polygonvalidiumetrog.PolygonRollupBaseEtrogBatchData +} + +// ForkID is a sturct to track the ForkID event. +type ForkID struct { + BatchNumber uint64 + ForkID uint64 + Version string +} + +type BlockNumberFinality string + +const ( + FinalizedBlock = BlockNumberFinality("FinalizedBlock") + SafeBlock = BlockNumberFinality("SafeBlock") + PendingBlock = BlockNumberFinality("PendingBlock") + LatestBlock = BlockNumberFinality("LatestBlock") + EarliestBlock = BlockNumberFinality("EarliestBlock") +) + +func (b *BlockNumberFinality) ToBlockNum() (*big.Int, error) { + switch *b { + case FinalizedBlock: + return big.NewInt(int64(Finalized)), nil + case SafeBlock: + return big.NewInt(int64(Safe)), nil + case PendingBlock: + return big.NewInt(int64(Pending)), nil + case LatestBlock: + return big.NewInt(int64(Latest)), nil + case EarliestBlock: + return big.NewInt(int64(Earliest)), nil + default: + return nil, fmt.Errorf("invalid finality keyword: %s", string(*b)) + } +} + +type BlockNumber int64 + +const ( + Finalized = BlockNumber(-5) + Safe = BlockNumber(-4) + Pending = BlockNumber(-3) + Latest = BlockNumber(-2) + Earliest = BlockNumber(-1) +) diff --git a/go.mod b/go.mod index c90ef606..18711d78 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,156 @@ module github.com/0xPolygon/cdk -go 1.22 +go 1.22.4 + +require ( + github.com/0xPolygon/cdk-contracts-tooling v0.0.0-20240627125553-80db9d8a41b5 + github.com/0xPolygon/cdk-data-availability v0.0.7 + github.com/0xPolygon/cdk-rpc v0.0.0-20240419104226-c0a62ba0f49d + github.com/0xPolygonHermez/zkevm-data-streamer v0.2.2 + github.com/0xPolygonHermez/zkevm-ethtx-manager v0.1.9 + github.com/0xPolygonHermez/zkevm-synchronizer-l1 v0.6.1 + github.com/ethereum/go-ethereum v1.14.5 + github.com/gobuffalo/packr/v2 v2.8.3 + github.com/hermeznetwork/tracerr v0.3.2 + github.com/iden3/go-iden3-crypto v0.0.16 + github.com/invopop/jsonschema v0.12.0 + github.com/jackc/pgconn v1.14.3 + github.com/jackc/pgx/v4 v4.18.3 + github.com/ledgerwatch/erigon-lib v1.0.0 + github.com/mitchellh/mapstructure v1.5.0 + github.com/rubenv/sql-migrate v1.6.1 + github.com/spf13/viper v1.19.0 + github.com/stretchr/testify v1.9.0 + github.com/urfave/cli/v2 v2.27.2 + go.uber.org/zap v1.27.0 + golang.org/x/crypto v0.24.0 + golang.org/x/net v0.26.0 + google.golang.org/grpc v1.64.0 + google.golang.org/protobuf v1.34.2 +) + +require ( + github.com/DataDog/zstd v1.5.2 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/VictoriaMetrics/fastcache v1.12.2 // indirect + github.com/VictoriaMetrics/metrics v1.23.1 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/buger/jsonparser v1.1.1 // indirect + github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.0 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect + github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/didip/tollbooth/v6 v6.1.2 // indirect + github.com/erigontech/mdbx-go v0.27.14 // indirect + github.com/ethereum/c-kzg-4844 v1.0.0 // indirect + github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 // indirect + github.com/fjl/memsize v0.0.2 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect + github.com/getsentry/sentry-go v0.18.0 // indirect + github.com/go-gorp/gorp/v3 v3.1.0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-pkgz/expirable-cache v0.0.3 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/gobuffalo/logger v1.0.7 // indirect + github.com/gobuffalo/packd v1.0.2 // indirect + github.com/gofrs/flock v0.8.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/hashicorp/go-bexpr v0.1.10 // indirect + github.com/hashicorp/hcl v1.0.1-0.20180906183839-65a6292f0157 // indirect + github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect + github.com/holiman/uint256 v1.2.4 // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.3.3 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgtype v1.14.0 // indirect + github.com/jackc/puddle v1.3.0 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jmoiron/sqlx v1.2.0 // indirect + github.com/karrick/godirwalk v1.17.0 // indirect + github.com/klauspost/compress v1.17.2 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/ledgerwatch/log/v3 v3.9.0 // indirect + github.com/logrusorgru/aurora v2.0.3+incompatible // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/markbates/errx v1.1.0 // indirect + github.com/markbates/oncer v1.0.0 // indirect + github.com/markbates/safe v1.0.1 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/miguelmota/go-solidity-sha3 v0.1.1 // indirect + github.com/mitchellh/pointerstructure v1.2.0 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/onsi/gomega v1.27.10 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rs/cors v1.7.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/status-im/keycard-go v0.2.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/supranational/blst v0.3.11 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/tyler-smith/go-bip39 v1.1.0 // indirect + github.com/valyala/fastrand v1.1.0 // indirect + github.com/valyala/histogram v1.2.0 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect + github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect + go.uber.org/multierr v1.10.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect +) diff --git a/go.sum b/go.sum index e69de29b..8eff1443 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,1083 @@ +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.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.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +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= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/0xPolygon/cdk-contracts-tooling v0.0.0-20240627125553-80db9d8a41b5 h1:bezOoVvpQvGvf4aCZQpMPFJGKXhhvktpApr+TsD8rdk= +github.com/0xPolygon/cdk-contracts-tooling v0.0.0-20240627125553-80db9d8a41b5/go.mod h1:mFlcEjsm2YBBsu8atHJ3zyVnwM+Z/fMXpVmIJge+WVU= +github.com/0xPolygon/cdk-data-availability v0.0.7 h1:i5v2I8uEgHSZ5BjnJs3Dsy1XpKdSvfZbON9D16gSCUg= +github.com/0xPolygon/cdk-data-availability v0.0.7/go.mod h1:qF+xt2gYwBab8XPh3fr6pnNUMEJq/32rmJN0D630hmY= +github.com/0xPolygon/cdk-rpc v0.0.0-20240419104226-c0a62ba0f49d h1:sxh6hZ2jF/sxxj2jd5o1vuNNCZjYmn4aRG9SRlVaEFs= +github.com/0xPolygon/cdk-rpc v0.0.0-20240419104226-c0a62ba0f49d/go.mod h1:2scWqMMufrQXu7TikDgQ3BsyaKoX8qP26D6E262vSOg= +github.com/0xPolygonHermez/zkevm-data-streamer v0.2.2 h1:XRMTk+W6vtJVGVjuEznfWyNt7HkRkkuSmlN5Y6p60Sc= +github.com/0xPolygonHermez/zkevm-data-streamer v0.2.2/go.mod h1:0QkAXcFa92mFJrCbN3UPUJGJYes851yEgYHLONnaosE= +github.com/0xPolygonHermez/zkevm-ethtx-manager v0.1.9 h1:vrAezzwTNke6NroDAltGh1k2AJ6ibmZPBsG0bCltbRc= +github.com/0xPolygonHermez/zkevm-ethtx-manager v0.1.9/go.mod h1:pRqfLQVM3nbzdhy3buqjAgcVyNDKAXOHqTSgkwiKpic= +github.com/0xPolygonHermez/zkevm-synchronizer-l1 v0.6.1 h1:pf0lvUJNl5/3AnpnFPvgaYTUoOydVUaqI0hyZGglI2E= +github.com/0xPolygonHermez/zkevm-synchronizer-l1 v0.6.1/go.mod h1:3L6m4ZYs2tTTNhxMtq1KHSclxr7RmlZ3eWtOe8lsYsM= +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/DATA-DOG/go-sqlmock v1.5.1 h1:FK6RCIUSfmbnI/imIICmboyQBkOckutaa6R5YYlLZyo= +github.com/DATA-DOG/go-sqlmock v1.5.1/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= +github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +github.com/VictoriaMetrics/metrics v1.23.1 h1:/j8DzeJBxSpL2qSIdqnRFLvQQhbJyJbbEi22yMm7oL0= +github.com/VictoriaMetrics/metrics v1.23.1/go.mod h1:rAr/llLpEnAdTehiNlUxKgnjcOuROSzpw0GvjpEbvFc= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= +github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +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/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= +github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-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/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= +github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= +github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/didip/tollbooth/v6 v6.1.2 h1:Kdqxmqw9YTv0uKajBUiWQg+GURL/k4vy9gmLCL01PjQ= +github.com/didip/tollbooth/v6 v6.1.2/go.mod h1:xjcse6CTHCLuOkzsWrEgdy9WPJFv+p/x6v+MyfP+O9s= +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/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/erigontech/mdbx-go v0.27.14 h1:IVVeQVCAjZRpAR8bThlP2ISxrOwdV35NZdGwAgotaRw= +github.com/erigontech/mdbx-go v0.27.14/go.mod h1:FAMxbOgqOnRDx51j8HjuJZIgznbDwjX7LItd+/UWyA4= +github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= +github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.14.5 h1:szuFzO1MhJmweXjoM5nSAeDvjNUH3vIQoMzzQnfvjpw= +github.com/ethereum/go-ethereum v1.14.5/go.mod h1:VEDGGhSxY7IEjn98hJRFXl/uFvpRgbIIf2PpXiyGGgc= +github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 h1:KrE8I4reeVvf7C1tm8elRjj4BdscTYzz/WAbYyf/JI4= +github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= +github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= +github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-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-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= +github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-pkgz/expirable-cache v0.0.3 h1:rTh6qNPp78z0bQE6HDhXBHUwqnV9i09Vm6dksJLXQDc= +github.com/go-pkgz/expirable-cache v0.0.3/go.mod h1:+IauqN00R2FqNRLCLA+X5YljQJrwB179PfiAoMPlTlQ= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +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/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= +github.com/gobuffalo/logger v1.0.7 h1:LTLwWelETXDYyqF/ASf0nxaIcdEOIJNxRokPcfI/xbU= +github.com/gobuffalo/logger v1.0.7/go.mod h1:u40u6Bq3VVvaMcy5sRBclD8SXhBYPS0Qk95ubt+1xJM= +github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= +github.com/gobuffalo/packd v1.0.2 h1:Yg523YqnOxGIWCp69W12yYBKsoChwI7mtu6ceM9Bwfw= +github.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8= +github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= +github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-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/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +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.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/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.3/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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/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/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +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/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcl v1.0.1-0.20180906183839-65a6292f0157 h1:uyodBE3xDz0ynKs1tLBU26wOQoEkAqqiY18DbZ+FZrA= +github.com/hashicorp/hcl v1.0.1-0.20180906183839-65a6292f0157/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hermeznetwork/tracerr v0.3.2 h1:QB3TlQxO/4XHyixsg+nRZPuoel/FFQlQ7oAoHDD5l1c= +github.com/hermeznetwork/tracerr v0.3.2/go.mod h1:nsWC1+tc4qUEbUGRv4DcPJJTjLsedlPajlFmpJoohK4= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/ianlancetaylor/demangle v0.0.0-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/iden3/go-iden3-crypto v0.0.16 h1:zN867xiz6HgErXVIV/6WyteGcOukE9gybYTorBMEdsk= +github.com/iden3/go-iden3-crypto v0.0.16/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= +github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= +github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI= +github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +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/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/ledgerwatch/erigon-lib v1.0.0 h1:2o7EfgB/6CyjXAaQ8+Dh7AmY5rWvwSKg0kGp/U9kwqE= +github.com/ledgerwatch/erigon-lib v1.0.0/go.mod h1:l1i6+H9MgizD+ObQ5cXsfA9S3egYTOCnnYGjbrJMqR4= +github.com/ledgerwatch/log/v3 v3.9.0 h1:iDwrXe0PVwBC68Dd94YSsHbMgQ3ufsgjzXtFNFVZFRk= +github.com/ledgerwatch/log/v3 v3.9.0/go.mod h1:EiAY6upmI/6LkNhOVxb4eVsmsP11HZCnZ3PlJMjYiqE= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= +github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= +github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= +github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= +github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= +github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +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.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= +github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miguelmota/go-solidity-sha3 v0.1.1 h1:3Y08sKZDtudtE5kbTBPC9RYJznoSYyWI9VD6mghU0CA= +github.com/miguelmota/go-solidity-sha3 v0.1.1/go.mod h1:sax1FvQF+f71j8W1uUHMZn8NxKyl5rYLks2nqj8RFEw= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= +github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/rubenv/sql-migrate v1.6.1 h1:bo6/sjsan9HaXAsNxYP/jCEDUGibHp8JmOBw7NTGRos= +github.com/rubenv/sql-migrate v1.6.1/go.mod h1:tPzespupJS0jacLfhbwto/UjSX+8h2FdWB7ar+QlHa0= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +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.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= +github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= +github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= +github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= +github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= +github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ= +github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY= +github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= +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/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +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= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +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-20190820162420-60c769a6c586/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-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +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-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +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/lint v0.0.0-20210508222113-6edffad5e616/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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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-20190813141303-74dc4d7220e7/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-20201110031124-69a78807bb2b/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-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +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/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20181026203630-95b1ffbd15a5/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-20190222072716-a9d3bda3a223/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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/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-20190813064441-fde4db37ae7a/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-20191026070338-33540a1f6037/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-20200116001909-b77594299b42/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-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +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.5/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.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +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.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-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-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/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-20190823170909-c4a336ef6a2f/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-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/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-20200103221440-774c71fcf114/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.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +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-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-20200513103714-09dca8ec2884/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-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +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.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +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/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +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.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/hex/hex.go b/hex/hex.go new file mode 100644 index 00000000..4699eb20 --- /dev/null +++ b/hex/hex.go @@ -0,0 +1,121 @@ +package hex + +import ( + "encoding/hex" + "fmt" + "math/big" + "strconv" + "strings" +) + +const ( + // Base represents the hexadecimal base, which is 16 + Base = 16 + + // BitSize64 64 bits + BitSize64 = 64 +) + +// DecError represents an error when decoding a hex value +type DecError struct{ msg string } + +func (err DecError) Error() string { return err.msg } + +// EncodeToHex generates a hex string based on the byte representation, with the '0x' prefix +func EncodeToHex(str []byte) string { + return "0x" + hex.EncodeToString(str) +} + +// EncodeToString is a wrapper method for hex.EncodeToString +func EncodeToString(str []byte) string { + return hex.EncodeToString(str) +} + +// DecodeString returns the byte representation of the hexadecimal string +func DecodeString(str string) ([]byte, error) { + return hex.DecodeString(str) +} + +// DecodeHex converts a hex string to a byte array +func DecodeHex(str string) ([]byte, error) { + str = strings.TrimPrefix(str, "0x") + + // Check if the string has an odd length + if len(str)%2 != 0 { + // Prepend a '0' to make it even-length + str = "0" + str + } + + return hex.DecodeString(str) +} + +// MustDecodeHex type-checks and converts a hex string to a byte array +func MustDecodeHex(str string) []byte { + buf, err := DecodeHex(str) + if err != nil { + panic(fmt.Errorf("could not decode hex: %v", err)) + } + + return buf +} + +// DecodeUint64 type-checks and converts a hex string to a uint64 +func DecodeUint64(str string) uint64 { + i := DecodeBig(str) + return i.Uint64() +} + +// EncodeUint64 encodes a number as a hex string with 0x prefix. +func EncodeUint64(i uint64) string { + enc := make([]byte, 2, 10) //nolint:gomnd + copy(enc, "0x") + return string(strconv.AppendUint(enc, i, Base)) +} + +// BadNibble is a nibble that is bad +const BadNibble = ^uint64(0) + +// DecodeNibble decodes a byte into a uint64 +func DecodeNibble(in byte) uint64 { + switch { + case in >= '0' && in <= '9': + return uint64(in - '0') + case in >= 'A' && in <= 'F': + return uint64(in - 'A' + 10) //nolint:gomnd + case in >= 'a' && in <= 'f': + return uint64(in - 'a' + 10) //nolint:gomnd + default: + return BadNibble + } +} + +// EncodeBig encodes bigint as a hex string with 0x prefix. +// The sign of the integer is ignored. +func EncodeBig(bigint *big.Int) string { + numBits := bigint.BitLen() + if numBits == 0 { + return "0x0" + } + + return fmt.Sprintf("%#x", bigint) +} + +// DecodeBig converts a hex number to a big.Int value +func DecodeBig(hexNum string) *big.Int { + str := strings.TrimPrefix(hexNum, "0x") + createdNum := new(big.Int) + createdNum.SetString(str, Base) + + return createdNum +} + +// IsValid checks if the provided string is a valid hexadecimal value +func IsValid(s string) bool { + str := strings.TrimPrefix(s, "0x") + for _, b := range []byte(str) { + if !(b >= '0' && b <= '9' || b >= 'a' && b <= 'f' || b >= 'A' && b <= 'F') { + return false + } + } + return true +} diff --git a/hex/hex_test.go b/hex/hex_test.go new file mode 100644 index 00000000..da86da35 --- /dev/null +++ b/hex/hex_test.go @@ -0,0 +1,52 @@ +package hex + +import ( + "encoding/hex" + "math" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEncodeDecodeBig(t *testing.T) { + b := big.NewInt(math.MaxInt64) + e := EncodeBig(b) + d := DecodeBig(e) + assert.Equal(t, b.Uint64(), d.Uint64()) +} + +// Define a struct for test cases +type TestCase struct { + input string + output []byte + err error +} + +// Unit test function +func TestDecodeHex(t *testing.T) { + testCases := []TestCase{ + {"0", []byte{0}, nil}, + {"00", []byte{0}, nil}, + {"0x0", []byte{0}, nil}, + {"0x00", []byte{0}, nil}, + {"1", []byte{1}, nil}, + {"01", []byte{1}, nil}, + {"", []byte{}, hex.ErrLength}, + {"0x", []byte{}, hex.ErrLength}, + {"zz", []byte{}, hex.InvalidByteError('z')}, + } + + for _, tc := range testCases { + t.Run(tc.input, func(t *testing.T) { + output, err := DecodeHex(tc.input) + if tc.err != nil { + require.Error(t, tc.err, err) + } else { + require.NoError(t, err) + } + require.Equal(t, output, tc.output) + }) + } +} diff --git a/l1infotree/hash.go b/l1infotree/hash.go new file mode 100644 index 00000000..b07c3f10 --- /dev/null +++ b/l1infotree/hash.go @@ -0,0 +1,41 @@ +package l1infotree + +import ( + "encoding/binary" + + "github.com/ethereum/go-ethereum/common" + "github.com/iden3/go-iden3-crypto/keccak256" + "golang.org/x/crypto/sha3" +) + +// Hash calculates the keccak hash of elements. +func Hash(data ...[32]byte) [32]byte { + var res [32]byte + hash := sha3.NewLegacyKeccak256() + for _, d := range data { + hash.Write(d[:]) //nolint:errcheck,gosec + } + copy(res[:], hash.Sum(nil)) + return res +} + +func generateZeroHashes(height uint8) [][32]byte { + var zeroHashes = [][32]byte{ + common.Hash{}, + } + // This generates a leaf = HashZero in position 0. In the rest of the positions that are equivalent to the ascending levels, + // we set the hashes of the nodes. So all nodes from level i=5 will have the same value and same children nodes. + for i := 1; i <= int(height); i++ { + zeroHashes = append(zeroHashes, Hash(zeroHashes[i-1], zeroHashes[i-1])) + } + return zeroHashes +} + +// HashLeafData calculates the keccak hash of the leaf values. +func HashLeafData(ger, prevBlockHash common.Hash, minTimestamp uint64) [32]byte { + var res [32]byte + t := make([]byte, 8) //nolint:gomnd + binary.BigEndian.PutUint64(t, minTimestamp) + copy(res[:], keccak256.Hash(ger.Bytes(), prevBlockHash.Bytes(), t)) + return res +} diff --git a/l1infotree/hash_test.go b/l1infotree/hash_test.go new file mode 100644 index 00000000..a792c0b8 --- /dev/null +++ b/l1infotree/hash_test.go @@ -0,0 +1,20 @@ +package l1infotree + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" +) + +func TestHashLeaf(t *testing.T) { + expectedLeafHash := common.HexToHash("0xf62f487534b899b1c362242616725878188ca891fab60854b792ca0628286de7") + + prevBlockHash := common.HexToHash("0x24a5871d68723340d9eadc674aa8ad75f3e33b61d5a9db7db92af856a19270bb") + var minTimestamp uint64 = 1697231573 + ger := common.HexToHash("0x16994edfddddb9480667b64174fc00d3b6da7290d37b8db3a16571b4ddf0789f") + + leaf := HashLeafData(ger, prevBlockHash, minTimestamp) + + assert.Equal(t, expectedLeafHash, common.BytesToHash(leaf[:])) +} diff --git a/l1infotree/tree.go b/l1infotree/tree.go new file mode 100644 index 00000000..6fc01c87 --- /dev/null +++ b/l1infotree/tree.go @@ -0,0 +1,198 @@ +package l1infotree + +import ( + "fmt" + + "github.com/0xPolygon/cdk/log" + "github.com/ethereum/go-ethereum/common" +) + +// L1InfoTree provides methods to compute L1InfoTree +type L1InfoTree struct { + height uint8 + zeroHashes [][32]byte + count uint32 + siblings [][32]byte + currentRoot common.Hash +} + +// NewL1InfoTree creates new L1InfoTree. +func NewL1InfoTree(height uint8, initialLeaves [][32]byte) (*L1InfoTree, error) { + mt := &L1InfoTree{ + zeroHashes: generateZeroHashes(height), + height: height, + count: uint32(len(initialLeaves)), + } + var err error + mt.siblings, mt.currentRoot, err = mt.initSiblings(initialLeaves) + if err != nil { + log.Error("error initializing siblings. Error: ", err) + return nil, err + } + log.Debug("Initial count: ", mt.count) + log.Debug("Initial root: ", mt.currentRoot) + return mt, nil +} + +// ResetL1InfoTree resets the L1InfoTree. +func (mt *L1InfoTree) ResetL1InfoTree(initialLeaves [][32]byte) (*L1InfoTree, error) { + log.Info("Resetting L1InfoTree...") + newMT := &L1InfoTree{ + zeroHashes: generateZeroHashes(32), // nolint:gomnd + height: 32, // nolint:gomnd + count: uint32(len(initialLeaves)), + } + var err error + newMT.siblings, newMT.currentRoot, err = newMT.initSiblings(initialLeaves) + if err != nil { + log.Error("error initializing siblings. Error: ", err) + return nil, err + } + log.Debug("Reset initial count: ", newMT.count) + log.Debug("Reset initial root: ", newMT.currentRoot) + return newMT, nil +} + +func buildIntermediate(leaves [][32]byte) ([][][]byte, [][32]byte) { + var ( + nodes [][][]byte + hashes [][32]byte + ) + for i := 0; i < len(leaves); i += 2 { + var left, right int = i, i + 1 + hash := Hash(leaves[left], leaves[right]) + nodes = append(nodes, [][]byte{hash[:], leaves[left][:], leaves[right][:]}) + hashes = append(hashes, hash) + } + return nodes, hashes +} + +// BuildL1InfoRoot computes the root given the leaves of the tree +func (mt *L1InfoTree) BuildL1InfoRoot(leaves [][32]byte) (common.Hash, error) { + var ( + nodes [][][][]byte + ns [][][]byte + ) + if len(leaves) == 0 { + leaves = append(leaves, mt.zeroHashes[0]) + } + + for h := uint8(0); h < mt.height; h++ { + if len(leaves)%2 == 1 { + leaves = append(leaves, mt.zeroHashes[h]) + } + ns, leaves = buildIntermediate(leaves) + nodes = append(nodes, ns) + } + if len(ns) != 1 { + return common.Hash{}, fmt.Errorf("error: more than one root detected: %+v", nodes) + } + + return common.BytesToHash(ns[0][0]), nil +} + +// ComputeMerkleProof computes the merkleProof and root given the leaves of the tree +func (mt *L1InfoTree) ComputeMerkleProof(gerIndex uint32, leaves [][32]byte) ([][32]byte, common.Hash, error) { + var ns [][][]byte + if len(leaves) == 0 { + leaves = append(leaves, mt.zeroHashes[0]) + } + var siblings [][32]byte + index := gerIndex + for h := uint8(0); h < mt.height; h++ { + if len(leaves)%2 == 1 { + leaves = append(leaves, mt.zeroHashes[h]) + } + if index >= uint32(len(leaves)) { + siblings = append(siblings, mt.zeroHashes[h]) + } else { + if index%2 == 1 { //If it is odd + siblings = append(siblings, leaves[index-1]) + } else { // It is even + siblings = append(siblings, leaves[index+1]) + } + } + var ( + nsi [][][]byte + hashes [][32]byte + ) + for i := 0; i < len(leaves); i += 2 { + var left, right int = i, i + 1 + hash := Hash(leaves[left], leaves[right]) + nsi = append(nsi, [][]byte{hash[:], leaves[left][:], leaves[right][:]}) + hashes = append(hashes, hash) + } + // Find the index of the leave in the next level of the tree. + // Divide the index by 2 to find the position in the upper level + index = uint32(float64(index) / 2) //nolint:gomnd + ns = nsi + leaves = hashes + } + if len(ns) != 1 { + return nil, common.Hash{}, fmt.Errorf("error: more than one root detected: %+v", ns) + } + + return siblings, common.BytesToHash(ns[0][0]), nil +} + +// AddLeaf adds new leaves to the tree and computes the new root +func (mt *L1InfoTree) AddLeaf(index uint32, leaf [32]byte) (common.Hash, error) { + if index != mt.count { + return common.Hash{}, fmt.Errorf("mismatched leaf count: %d, expected: %d", index, mt.count) + } + cur := leaf + isFilledSubTree := true + + for h := uint8(0); h < mt.height; h++ { + if index&(1< 0 { + var child [32]byte + copy(child[:], cur[:]) + parent := Hash(mt.siblings[h], child) + cur = parent + } else { + if isFilledSubTree { + // we will update the sibling when the sub tree is complete + copy(mt.siblings[h][:], cur[:]) + // we have a left child in this layer, it means the right child is empty so the sub tree is not completed + isFilledSubTree = false + } + var child [32]byte + copy(child[:], cur[:]) + parent := Hash(child, mt.zeroHashes[h]) + cur = parent + // the sibling of 0 bit should be the zero hash, since we are in the last node of the tree + } + } + mt.currentRoot = cur + mt.count++ + return cur, nil +} + +// initSiblings returns the siblings of the node at the given index. +// it is used to initialize the siblings array in the beginning. +func (mt *L1InfoTree) initSiblings(initialLeaves [][32]byte) ([][32]byte, common.Hash, error) { + if mt.count != uint32(len(initialLeaves)) { + return nil, [32]byte{}, fmt.Errorf("error: mt.count and initialLeaves length mismatch") + } + if mt.count == 0 { + var siblings [][32]byte + for h := 0; h < int(mt.height); h++ { + var left [32]byte + copy(left[:], mt.zeroHashes[h][:]) + siblings = append(siblings, left) + } + root, err := mt.BuildL1InfoRoot(initialLeaves) + if err != nil { + log.Error("error calculating initial root: ", err) + return nil, [32]byte{}, err + } + return siblings, root, nil + } + + return mt.ComputeMerkleProof(mt.count, initialLeaves) +} + +// GetCurrentRootCountAndSiblings returns the latest root, count and sibblings +func (mt *L1InfoTree) GetCurrentRootCountAndSiblings() (common.Hash, uint32, [][32]byte) { + return mt.currentRoot, mt.count, mt.siblings +} diff --git a/localbridgesync/downloader.go b/localbridgesync/downloader.go new file mode 100644 index 00000000..9763f818 --- /dev/null +++ b/localbridgesync/downloader.go @@ -0,0 +1,253 @@ +package localbridgesync + +import ( + "context" + "math/big" + "time" + + "github.com/0xPolygon/cdk-contracts-tooling/contracts/etrog/polygonzkevmbridge" + "github.com/0xPolygon/cdk-contracts-tooling/contracts/etrog/polygonzkevmbridgev2" + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/log" + "github.com/ethereum/go-ethereum" + "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" +) + +const ( + waitForNewBlocksPeriod = time.Millisecond * 100 +) + +var ( + bridgeEventSignature = crypto.Keccak256Hash([]byte("BridgeEvent(uint8,uint32,address,uint32,address,uint256,bytes,uint32)")) + claimEventSignature = crypto.Keccak256Hash([]byte("ClaimEvent(uint256,uint32,address,address,uint256)")) + claimEventSignaturePreEtrog = crypto.Keccak256Hash([]byte("ClaimEvent(uint32,uint32,address,address,uint256)")) +) + +type EthClienter interface { + ethereum.LogFilterer + ethereum.BlockNumberReader + ethereum.ChainReader + bind.ContractBackend +} + +type downloaderInterface interface { + waitForNewBlocks(ctx context.Context, lastBlockSeen uint64) (newLastBlock uint64) + getEventsByBlockRange(ctx context.Context, fromBlock, toBlock uint64) []block + getLogs(ctx context.Context, fromBlock, toBlock uint64) []types.Log + appendLog(b *block, l types.Log) + getBlockHeader(ctx context.Context, blockNum uint64) blockHeader +} + +type downloader struct { + syncBlockChunkSize uint64 + downloaderInterface +} + +func newDownloader( + bridgeAddr common.Address, + ethClient EthClienter, + syncBlockChunkSize uint64, + blockFinalityType etherman.BlockNumberFinality, +) (*downloader, error) { + bridgeContractV1, err := polygonzkevmbridge.NewPolygonzkevmbridge(bridgeAddr, ethClient) + if err != nil { + return nil, err + } + bridgeContractV2, err := polygonzkevmbridgev2.NewPolygonzkevmbridgev2(bridgeAddr, ethClient) + if err != nil { + return nil, err + } + finality, err := blockFinalityType.ToBlockNum() + if err != nil { + return nil, err + } + return &downloader{ + syncBlockChunkSize: syncBlockChunkSize, + downloaderInterface: &downloaderImplementation{ + bridgeAddr: bridgeAddr, + bridgeContractV1: bridgeContractV1, + bridgeContractV2: bridgeContractV2, + ethClient: ethClient, + blockFinality: finality, + }, + }, nil +} + +func (d *downloader) download(ctx context.Context, fromBlock uint64, downloadedCh chan block) { + lastBlock := d.waitForNewBlocks(ctx, 0) + for { + select { + case <-ctx.Done(): + log.Debug("closing channel") + close(downloadedCh) + return + default: + } + toBlock := fromBlock + d.syncBlockChunkSize + if toBlock > lastBlock { + toBlock = lastBlock + } + if fromBlock > toBlock { + log.Debug("waiting for new blocks, last block ", toBlock) + lastBlock = d.waitForNewBlocks(ctx, toBlock) + continue + } + log.Debugf("getting events from blocks %d to %d", fromBlock, toBlock) + blocks := d.getEventsByBlockRange(ctx, fromBlock, toBlock) + for _, b := range blocks { + log.Debugf("sending block %d to the driver (with events)", b.Num) + downloadedCh <- b + } + if len(blocks) == 0 || blocks[len(blocks)-1].Num < toBlock { + // Indicate the last downloaded block if there are not events on it + log.Debugf("sending block %d to the driver (without evvents)", toBlock) + downloadedCh <- block{ + blockHeader: d.getBlockHeader(ctx, toBlock), + } + } + fromBlock = toBlock + 1 + } +} + +type downloaderImplementation struct { + bridgeAddr common.Address + bridgeContractV1 *polygonzkevmbridge.Polygonzkevmbridge + bridgeContractV2 *polygonzkevmbridgev2.Polygonzkevmbridgev2 + ethClient EthClienter + blockFinality *big.Int +} + +func (d *downloaderImplementation) waitForNewBlocks(ctx context.Context, lastBlockSeen uint64) (newLastBlock uint64) { + attempts := 0 + for { + header, err := d.ethClient.HeaderByNumber(ctx, d.blockFinality) + if err != nil { + attempts++ + log.Error("error geting last block num from eth client: ", err) + retryHandler("waitForNewBlocks", attempts) + continue + } + if header.Number.Uint64() > lastBlockSeen { + return header.Number.Uint64() + } + time.Sleep(waitForNewBlocksPeriod) + } +} + +func (d *downloaderImplementation) getEventsByBlockRange(ctx context.Context, fromBlock, toBlock uint64) []block { + blocks := []block{} + logs := d.getLogs(ctx, fromBlock, toBlock) + for _, l := range logs { + if len(blocks) == 0 || blocks[len(blocks)-1].Num < l.BlockNumber { + blocks = append(blocks, block{ + blockHeader: blockHeader{ + Num: l.BlockNumber, + Hash: l.BlockHash, + }, + Events: []BridgeEvent{}, + }) + } + d.appendLog(&blocks[len(blocks)-1], l) + } + + return blocks +} + +func (d *downloaderImplementation) getLogs(ctx context.Context, fromBlock, toBlock uint64) []types.Log { + query := ethereum.FilterQuery{ + FromBlock: new(big.Int).SetUint64(fromBlock), + Addresses: []common.Address{d.bridgeAddr}, + Topics: [][]common.Hash{ + {bridgeEventSignature}, + {claimEventSignature}, + {claimEventSignaturePreEtrog}, + }, + ToBlock: new(big.Int).SetUint64(toBlock), + } + attempts := 0 + for { + logs, err := d.ethClient.FilterLogs(ctx, query) + if err != nil { + attempts++ + log.Error("error calling FilterLogs to eth client: ", err) + retryHandler("getLogs", attempts) + continue + } + return logs + } +} + +func (d *downloaderImplementation) appendLog(b *block, l types.Log) { + switch l.Topics[0] { + case bridgeEventSignature: + bridge, err := d.bridgeContractV2.ParseBridgeEvent(l) + if err != nil { + log.Fatalf( + "error parsing log %+v using d.bridgeContractV2.ParseBridgeEvent: %v", + l, err, + ) + } + b.Events = append(b.Events, BridgeEvent{Bridge: &Bridge{ + LeafType: bridge.LeafType, + OriginNetwork: bridge.OriginNetwork, + OriginAddress: bridge.OriginAddress, + DestinationNetwork: bridge.DestinationNetwork, + DestinationAddress: bridge.DestinationAddress, + Amount: bridge.Amount, + Metadata: bridge.Metadata, + DepositCount: bridge.DepositCount, + }}) + case claimEventSignature: + claim, err := d.bridgeContractV2.ParseClaimEvent(l) + if err != nil { + log.Fatalf( + "error parsing log %+v using d.bridgeContractV2.ParseClaimEvent: %v", + l, err, + ) + } + b.Events = append(b.Events, BridgeEvent{Claim: &Claim{ + GlobalIndex: claim.GlobalIndex, + OriginNetwork: claim.OriginNetwork, + OriginAddress: claim.OriginAddress, + DestinationAddress: claim.DestinationAddress, + Amount: claim.Amount, + }}) + case claimEventSignaturePreEtrog: + claim, err := d.bridgeContractV1.ParseClaimEvent(l) + if err != nil { + log.Fatalf( + "error parsing log %+v using d.bridgeContractV1.ParseClaimEvent: %v", + l, err, + ) + } + b.Events = append(b.Events, BridgeEvent{Claim: &Claim{ + GlobalIndex: big.NewInt(int64(claim.Index)), + OriginNetwork: claim.OriginNetwork, + OriginAddress: claim.OriginAddress, + DestinationAddress: claim.DestinationAddress, + Amount: claim.Amount, + }}) + default: + log.Fatalf("unexpected log %+v", l) + } +} + +func (d *downloaderImplementation) getBlockHeader(ctx context.Context, blockNum uint64) blockHeader { + attempts := 0 + for { + header, err := d.ethClient.HeaderByNumber(ctx, big.NewInt(int64(blockNum))) + if err != nil { + attempts++ + log.Errorf("error getting block header for block %d, err: %v", blockNum, err) + retryHandler("getBlockHeader", attempts) + continue + } + return blockHeader{ + Num: header.Number.Uint64(), + Hash: header.Hash(), + } + } +} diff --git a/localbridgesync/downloader_test.go b/localbridgesync/downloader_test.go new file mode 100644 index 00000000..c60f4d3f --- /dev/null +++ b/localbridgesync/downloader_test.go @@ -0,0 +1,466 @@ +package localbridgesync + +import ( + "context" + "errors" + "math/big" + "testing" + "time" + + "github.com/0xPolygon/cdk-contracts-tooling/contracts/etrog/polygonzkevmbridge" + "github.com/0xPolygon/cdk-contracts-tooling/contracts/etrog/polygonzkevmbridgev2" + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/log" + "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/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +var ( + contractAddr = common.HexToAddress("1234567890") +) + +const ( + syncBlockChunck = uint64(10) +) + +func TestGetEventsByBlockRange(t *testing.T) { + type testCase struct { + description string + inputLogs []types.Log + fromBlock, toBlock uint64 + expectedBlocks []block + } + testCases := []testCase{} + clientMock := NewL2Mock(t) + ctx := context.Background() + d, err := newDownloader(contractAddr, clientMock, syncBlockChunck, etherman.LatestBlock) + require.NoError(t, err) + + // case 0: single block, no events + case0 := testCase{ + description: "case 0: single block, no events", + inputLogs: []types.Log{}, + fromBlock: 1, + toBlock: 3, + expectedBlocks: []block{}, + } + testCases = append(testCases, case0) + + // case 1: single block, single event + logC1, bridgeC1 := generateBridge(t, 3) + logsC1 := []types.Log{ + *logC1, + } + blocksC1 := []block{ + { + blockHeader: blockHeader{ + Num: logC1.BlockNumber, + Hash: logC1.BlockHash, + }, + Events: []BridgeEvent{ + { + Bridge: &bridgeC1, + }, + }, + }, + } + case1 := testCase{ + description: "case 1: single block, single event", + inputLogs: logsC1, + fromBlock: 3, + toBlock: 3, + expectedBlocks: blocksC1, + } + testCases = append(testCases, case1) + + // case 2: single block, multiple events + logC2_1, bridgeC2_1 := generateBridge(t, 5) + logC2_2, bridgeC2_2 := generateBridge(t, 5) + logC2_3, claimC2_1 := generateClaimV1(t, 5) + logC2_4, claimC2_2 := generateClaimV2(t, 5) + logsC2 := []types.Log{ + *logC2_1, + *logC2_2, + *logC2_3, + *logC2_4, + } + blocksC2 := []block{ + { + blockHeader: blockHeader{ + Num: logC2_1.BlockNumber, + Hash: logC2_1.BlockHash, + }, + Events: []BridgeEvent{ + {Bridge: &bridgeC2_1}, + {Bridge: &bridgeC2_2}, + {Claim: &claimC2_1}, + {Claim: &claimC2_2}, + }, + }, + } + case2 := testCase{ + description: "case 2: single block, multiple events", + inputLogs: logsC2, + fromBlock: 5, + toBlock: 5, + expectedBlocks: blocksC2, + } + testCases = append(testCases, case2) + + // case 3: multiple blocks, some events + logC3_1, bridgeC3_1 := generateBridge(t, 7) + logC3_2, bridgeC3_2 := generateBridge(t, 7) + logC3_3, claimC3_1 := generateClaimV1(t, 8) + logC3_4, claimC3_2 := generateClaimV2(t, 8) + logsC3 := []types.Log{ + *logC3_1, + *logC3_2, + *logC3_3, + *logC3_4, + } + blocksC3 := []block{ + { + blockHeader: blockHeader{ + Num: logC3_1.BlockNumber, + Hash: logC3_1.BlockHash, + }, + Events: []BridgeEvent{ + {Bridge: &bridgeC3_1}, + {Bridge: &bridgeC3_2}, + }, + }, + { + blockHeader: blockHeader{ + Num: logC3_3.BlockNumber, + Hash: logC3_3.BlockHash, + }, + Events: []BridgeEvent{ + {Claim: &claimC3_1}, + {Claim: &claimC3_2}, + }, + }, + } + case3 := testCase{ + description: "case 3: multiple blocks, some events", + inputLogs: logsC3, + fromBlock: 7, + toBlock: 8, + expectedBlocks: blocksC3, + } + testCases = append(testCases, case3) + + for _, tc := range testCases { + query := ethereum.FilterQuery{ + FromBlock: new(big.Int).SetUint64(tc.fromBlock), + Addresses: []common.Address{contractAddr}, + Topics: [][]common.Hash{ + {bridgeEventSignature}, + {claimEventSignature}, + {claimEventSignaturePreEtrog}, + }, + ToBlock: new(big.Int).SetUint64(tc.toBlock), + } + clientMock. + On("FilterLogs", mock.Anything, query). + Return(tc.inputLogs, nil) + + actualBlocks := d.getEventsByBlockRange(ctx, tc.fromBlock, tc.toBlock) + require.Equal(t, tc.expectedBlocks, actualBlocks, tc.description) + } +} + +func generateBridge(t *testing.T, blockNum uint32) (*types.Log, Bridge) { + b := Bridge{ + LeafType: 1, + OriginNetwork: blockNum, + OriginAddress: contractAddr, + DestinationNetwork: blockNum, + DestinationAddress: contractAddr, + Amount: big.NewInt(int64(blockNum)), + Metadata: common.Hex2Bytes("01"), + DepositCount: blockNum, + } + abi, err := polygonzkevmbridgev2.Polygonzkevmbridgev2MetaData.GetAbi() + require.NoError(t, err) + event, err := abi.EventByID(bridgeEventSignature) + require.NoError(t, err) + data, err := event.Inputs.Pack( + b.LeafType, + b.OriginNetwork, + b.OriginAddress, + b.DestinationNetwork, + b.DestinationAddress, + b.Amount, + b.Metadata, + b.DepositCount, + ) + require.NoError(t, err) + log := &types.Log{ + Address: contractAddr, + BlockNumber: uint64(blockNum), + BlockHash: common.BytesToHash(blockNum2Bytes(uint64(blockNum))), + Topics: []common.Hash{bridgeEventSignature}, + Data: data, + } + return log, b +} + +func generateClaimV1(t *testing.T, blockNum uint32) (*types.Log, Claim) { + abi, err := polygonzkevmbridge.PolygonzkevmbridgeMetaData.GetAbi() + require.NoError(t, err) + event, err := abi.EventByID(claimEventSignaturePreEtrog) + require.NoError(t, err) + return generateClaim(t, blockNum, event, true) +} + +func generateClaimV2(t *testing.T, blockNum uint32) (*types.Log, Claim) { + abi, err := polygonzkevmbridgev2.Polygonzkevmbridgev2MetaData.GetAbi() + require.NoError(t, err) + event, err := abi.EventByID(claimEventSignature) + require.NoError(t, err) + return generateClaim(t, blockNum, event, false) +} + +func generateClaim(t *testing.T, blockNum uint32, event *abi.Event, isV1 bool) (*types.Log, Claim) { + c := Claim{ + GlobalIndex: big.NewInt(int64(blockNum)), + OriginNetwork: blockNum, + OriginAddress: contractAddr, + DestinationAddress: contractAddr, + Amount: big.NewInt(int64(blockNum)), + } + var ( + data []byte + err error + signature common.Hash + ) + if isV1 { + data, err = event.Inputs.Pack( + uint32(c.GlobalIndex.Uint64()), + c.OriginNetwork, + c.OriginAddress, + c.DestinationAddress, + c.Amount, + ) + signature = claimEventSignaturePreEtrog + } else { + data, err = event.Inputs.Pack( + c.GlobalIndex, + c.OriginNetwork, + c.OriginAddress, + c.DestinationAddress, + c.Amount, + ) + signature = claimEventSignature + } + require.NoError(t, err) + log := &types.Log{ + Address: contractAddr, + BlockNumber: uint64(blockNum), + BlockHash: common.BytesToHash(blockNum2Bytes(uint64(blockNum))), + Topics: []common.Hash{signature}, + Data: data, + } + return log, c +} + +func TestDownload(t *testing.T) { + /* + NOTE: due to the concurrent nature of this test (the function being tested runs through a goroutine) + if the mock doesn't match, the goroutine will get stuck and the test will timeout + */ + d := NewDownloaderMock(t) + downloadCh := make(chan block, 1) + ctx := context.Background() + ctx1, cancel := context.WithCancel(ctx) + expectedBlocks := []block{} + clientMock := NewL2Mock(t) + dwnldr, err := newDownloader(contractAddr, clientMock, syncBlockChunck, etherman.LatestBlock) + require.NoError(t, err) + dwnldr.downloaderInterface = d + + d.On("waitForNewBlocks", mock.Anything, uint64(0)). + Return(uint64(1)) + // iteratiion 0: + // last block is 1, download that block (no events and wait) + b1 := block{ + blockHeader: blockHeader{ + Num: 1, + Hash: common.HexToHash("01"), + }, + } + expectedBlocks = append(expectedBlocks, b1) + d.On("getEventsByBlockRange", mock.Anything, uint64(0), uint64(1)). + Return([]block{}) + d.On("getBlockHeader", mock.Anything, uint64(1)). + Return(b1.blockHeader) + + // iteration 1: wait for next block to be created + d.On("waitForNewBlocks", mock.Anything, uint64(1)). + After(time.Millisecond * 100). + Return(uint64(2)).Once() + + // iteration 2: block 2 has events + b2 := block{ + blockHeader: blockHeader{ + Num: 2, + Hash: common.HexToHash("02"), + }, + } + expectedBlocks = append(expectedBlocks, b2) + d.On("getEventsByBlockRange", mock.Anything, uint64(2), uint64(2)). + Return([]block{b2}) + + // iteration 3: wait for next block to be created (jump to block 8) + d.On("waitForNewBlocks", mock.Anything, uint64(2)). + After(time.Millisecond * 100). + Return(uint64(8)).Once() + + // iteration 4: blocks 6 and 7 have events + b6 := block{ + blockHeader: blockHeader{ + Num: 6, + Hash: common.HexToHash("06"), + }, + Events: []BridgeEvent{ + {Claim: &Claim{OriginNetwork: 6}}, + }, + } + b7 := block{ + blockHeader: blockHeader{ + Num: 7, + Hash: common.HexToHash("07"), + }, + Events: []BridgeEvent{ + {Bridge: &Bridge{DestinationNetwork: 7}}, + }, + } + b8 := block{ + blockHeader: blockHeader{ + Num: 8, + Hash: common.HexToHash("08"), + }, + } + expectedBlocks = append(expectedBlocks, b6, b7, b8) + d.On("getEventsByBlockRange", mock.Anything, uint64(3), uint64(8)). + Return([]block{b6, b7}) + d.On("getBlockHeader", mock.Anything, uint64(8)). + Return(b8.blockHeader) + + // iteration 5: wait for next block to be created (jump to block 30) + d.On("waitForNewBlocks", mock.Anything, uint64(8)). + After(time.Millisecond * 100). + Return(uint64(30)).Once() + + // iteration 6: from block 9 to 19, no events + b19 := block{ + blockHeader: blockHeader{ + Num: 19, + Hash: common.HexToHash("19"), + }, + } + expectedBlocks = append(expectedBlocks, b19) + d.On("getEventsByBlockRange", mock.Anything, uint64(9), uint64(19)). + Return([]block{}) + d.On("getBlockHeader", mock.Anything, uint64(19)). + Return(b19.blockHeader) + + // iteration 7: from block 20 to 30, events on last block + b30 := block{ + blockHeader: blockHeader{ + Num: 30, + Hash: common.HexToHash("30"), + }, + Events: []BridgeEvent{ + {Bridge: &Bridge{DestinationNetwork: 30}}, + }, + } + expectedBlocks = append(expectedBlocks, b30) + d.On("getEventsByBlockRange", mock.Anything, uint64(20), uint64(30)). + Return([]block{b30}) + + // iteration 8: wait for next block to be created (jump to block 35) + d.On("waitForNewBlocks", mock.Anything, uint64(30)). + After(time.Millisecond * 100). + Return(uint64(35)).Once() + + go dwnldr.download(ctx1, 0, downloadCh) + for _, expectedBlock := range expectedBlocks { + actualBlock := <-downloadCh + log.Debugf("block %d received!", actualBlock.Num) + require.Equal(t, expectedBlock, actualBlock) + } + log.Debug("canceling") + cancel() + _, ok := <-downloadCh + require.False(t, ok) +} + +func TestWaitForNewBlocks(t *testing.T) { + retryAfterErrorPeriod = time.Millisecond * 100 + clientMock := NewL2Mock(t) + ctx := context.Background() + d, err := newDownloader(contractAddr, clientMock, syncBlockChunck, etherman.LatestBlock) + require.NoError(t, err) + + // at first attempt + currentBlock := uint64(5) + expectedBlock := uint64(6) + clientMock.On("HeaderByNumber", ctx, mock.Anything).Return(&types.Header{ + Number: big.NewInt(6), + }, nil).Once() + actualBlock := d.waitForNewBlocks(ctx, currentBlock) + assert.Equal(t, expectedBlock, actualBlock) + + // 2 iterations + clientMock.On("HeaderByNumber", ctx, mock.Anything).Return(&types.Header{ + Number: big.NewInt(5), + }, nil).Once() + clientMock.On("HeaderByNumber", ctx, mock.Anything).Return(&types.Header{ + Number: big.NewInt(6), + }, nil).Once() + actualBlock = d.waitForNewBlocks(ctx, currentBlock) + assert.Equal(t, expectedBlock, actualBlock) + + // after error from client + clientMock.On("HeaderByNumber", ctx, mock.Anything).Return(nil, errors.New("foo")).Once() + clientMock.On("HeaderByNumber", ctx, mock.Anything).Return(&types.Header{ + Number: big.NewInt(6), + }, nil).Once() + actualBlock = d.waitForNewBlocks(ctx, currentBlock) + assert.Equal(t, expectedBlock, actualBlock) +} + +func TestGetBlockHeader(t *testing.T) { + retryAfterErrorPeriod = time.Millisecond * 100 + clientMock := NewL2Mock(t) + ctx := context.Background() + d, err := newDownloader(contractAddr, clientMock, syncBlockChunck, etherman.LatestBlock) + require.NoError(t, err) + + blockNum := uint64(5) + blockNumBig := big.NewInt(5) + returnedBlock := &types.Header{ + Number: blockNumBig, + } + expectedBlock := blockHeader{ + Num: 5, + Hash: returnedBlock.Hash(), + } + + // at first attempt + clientMock.On("HeaderByNumber", ctx, blockNumBig).Return(returnedBlock, nil).Once() + actualBlock := d.getBlockHeader(ctx, blockNum) + assert.Equal(t, expectedBlock, actualBlock) + + // after error from client + clientMock.On("HeaderByNumber", ctx, blockNumBig).Return(nil, errors.New("foo")).Once() + clientMock.On("HeaderByNumber", ctx, blockNumBig).Return(returnedBlock, nil).Once() + actualBlock = d.getBlockHeader(ctx, blockNum) + assert.Equal(t, expectedBlock, actualBlock) +} diff --git a/localbridgesync/driver.go b/localbridgesync/driver.go new file mode 100644 index 00000000..997fd215 --- /dev/null +++ b/localbridgesync/driver.go @@ -0,0 +1,140 @@ +package localbridgesync + +import ( + "context" + + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/reorgdetector" + "github.com/ethereum/go-ethereum/common" +) + +const ( + downloadBufferSize = 1000 + reorgDetectorID = "localbridgesync" +) + +type downloaderFull interface { + downloaderInterface + download(ctx context.Context, fromBlock uint64, downloadedCh chan block) +} + +type driver struct { + reorgDetector ReorgDetector + reorgSub *reorgdetector.Subscription + processor processorInterface + downloader downloaderFull +} + +type processorInterface interface { + getLastProcessedBlock(ctx context.Context) (uint64, error) + storeBridgeEvents(blockNum uint64, events []BridgeEvent) error + reorg(firstReorgedBlock uint64) error +} + +type ReorgDetector interface { + Subscribe(id string) *reorgdetector.Subscription + AddBlockToTrack(ctx context.Context, id string, blockNum uint64, blockHash common.Hash) error +} + +func newDriver( + reorgDetector ReorgDetector, + processor processorInterface, + downloader downloaderFull, +) (*driver, error) { + reorgSub := reorgDetector.Subscribe(reorgDetectorID) + return &driver{ + reorgDetector: reorgDetector, + reorgSub: reorgSub, + processor: processor, + downloader: downloader, + }, nil +} + +func (d *driver) Sync(ctx context.Context) { +reset: + var ( + lastProcessedBlock uint64 + attempts int + err error + ) + for { + lastProcessedBlock, err = d.processor.getLastProcessedBlock(ctx) + if err != nil { + attempts++ + log.Error("error geting last processed block: ", err) + retryHandler("Sync", attempts) + continue + } + break + } + cancellableCtx, cancel := context.WithCancel(ctx) + defer cancel() + + // start downloading + downloadCh := make(chan block, downloadBufferSize) + go d.downloader.download(cancellableCtx, lastProcessedBlock, downloadCh) + + for { + select { + case b := <-downloadCh: + log.Debug("handleNewBlock") + d.handleNewBlock(ctx, b) + case firstReorgedBlock := <-d.reorgSub.FirstReorgedBlock: + log.Debug("handleReorg") + d.handleReorg(cancel, downloadCh, firstReorgedBlock) + goto reset + } + } +} + +func (d *driver) handleNewBlock(ctx context.Context, b block) { + attempts := 0 + for { + err := d.reorgDetector.AddBlockToTrack(ctx, reorgDetectorID, b.Num, b.Hash) + if err != nil { + attempts++ + log.Errorf("error adding block %d to tracker: %v", b.Num, err) + retryHandler("handleNewBlock", attempts) + continue + } + break + } + attempts = 0 + for { + err := d.processor.storeBridgeEvents(b.Num, b.Events) + if err != nil { + attempts++ + log.Errorf("error processing events for blcok %d, err: ", b.Num, err) + retryHandler("handleNewBlock", attempts) + continue + } + break + } +} + +func (d *driver) handleReorg( + cancel context.CancelFunc, downloadCh chan block, firstReorgedBlock uint64, +) { + // stop downloader + cancel() + _, ok := <-downloadCh + for ok { + _, ok = <-downloadCh + } + // handle reorg + attempts := 0 + for { + err := d.processor.reorg(firstReorgedBlock) + if err != nil { + attempts++ + log.Errorf( + "error processing reorg, last valid block %d, err: %v", + firstReorgedBlock, err, + ) + retryHandler("handleReorg", attempts) + continue + } + break + } + d.reorgSub.ReorgProcessed <- true +} diff --git a/localbridgesync/driver_test.go b/localbridgesync/driver_test.go new file mode 100644 index 00000000..543542f7 --- /dev/null +++ b/localbridgesync/driver_test.go @@ -0,0 +1,211 @@ +package localbridgesync + +import ( + "context" + "errors" + "sync" + "testing" + "time" + + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/reorgdetector" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestSync(t *testing.T) { + retryAfterErrorPeriod = time.Millisecond * 100 + rdm := NewReorgDetectorMock(t) + pm := NewProcessorMock(t) + dm := NewDownloaderMock(t) + firstReorgedBlock := make(chan uint64) + reorgProcessed := make(chan bool) + rdm.On("Subscribe", reorgDetectorID).Return(&reorgdetector.Subscription{ + FirstReorgedBlock: firstReorgedBlock, + ReorgProcessed: reorgProcessed, + }) + driver, err := newDriver(rdm, pm, dm) + require.NoError(t, err) + ctx := context.Background() + expectedBlock1 := block{ + blockHeader: blockHeader{ + Num: 3, + Hash: common.HexToHash("03"), + }, + } + expectedBlock2 := block{ + blockHeader: blockHeader{ + Num: 9, + Hash: common.HexToHash("09"), + }, + } + type reorgSemaphore struct { + mu sync.Mutex + green bool + } + reorg1Completed := reorgSemaphore{} + dm.On("download", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + ctx := args.Get(0).(context.Context) + downloadedCh := args.Get(2).(chan block) + log.Info("entering mock loop") + for { + select { + case <-ctx.Done(): + log.Info("closing channel") + close(downloadedCh) + return + default: + } + reorg1Completed.mu.Lock() + green := reorg1Completed.green + reorg1Completed.mu.Unlock() + if green { + downloadedCh <- expectedBlock2 + } else { + downloadedCh <- expectedBlock1 + } + time.Sleep(100 * time.Millisecond) + } + }) + + // Mocking this actions, the driver should "store" all the blocks from the downloader + pm.On("getLastProcessedBlock", ctx). + Return(uint64(3), nil) + rdm.On("AddBlockToTrack", ctx, reorgDetectorID, expectedBlock1.Num, expectedBlock1.Hash). + Return(nil) + pm.On("storeBridgeEvents", expectedBlock1.Num, expectedBlock1.Events). + Return(nil) + rdm.On("AddBlockToTrack", ctx, reorgDetectorID, expectedBlock2.Num, expectedBlock2.Hash). + Return(nil) + pm.On("storeBridgeEvents", expectedBlock2.Num, expectedBlock2.Events). + Return(nil) + go driver.Sync(ctx) + time.Sleep(time.Millisecond * 200) // time to download expectedBlock1 + + // Trigger reorg 1 + reorgedBlock1 := uint64(5) + pm.On("reorg", reorgedBlock1).Return(nil) + firstReorgedBlock <- reorgedBlock1 + ok := <-reorgProcessed + require.True(t, ok) + reorg1Completed.mu.Lock() + reorg1Completed.green = true + reorg1Completed.mu.Unlock() + time.Sleep(time.Millisecond * 200) // time to download expectedBlock2 + + // Trigger reorg 2: syncer restarts the porcess + reorgedBlock2 := uint64(7) + pm.On("reorg", reorgedBlock2).Return(nil) + firstReorgedBlock <- reorgedBlock2 + ok = <-reorgProcessed + require.True(t, ok) +} + +func TestHandleNewBlock(t *testing.T) { + retryAfterErrorPeriod = time.Millisecond * 100 + rdm := NewReorgDetectorMock(t) + pm := NewProcessorMock(t) + dm := NewDownloaderMock(t) + rdm.On("Subscribe", reorgDetectorID).Return(&reorgdetector.Subscription{}) + driver, err := newDriver(rdm, pm, dm) + require.NoError(t, err) + ctx := context.Background() + + // happy path + b1 := block{ + blockHeader: blockHeader{ + Num: 1, + Hash: common.HexToHash("f00"), + }, + } + rdm. + On("AddBlockToTrack", ctx, reorgDetectorID, b1.Num, b1.Hash). + Return(nil) + pm.On("storeBridgeEvents", b1.Num, b1.Events). + Return(nil) + driver.handleNewBlock(ctx, b1) + + // reorg deteector fails once + b2 := block{ + blockHeader: blockHeader{ + Num: 2, + Hash: common.HexToHash("f00"), + }, + } + rdm. + On("AddBlockToTrack", ctx, reorgDetectorID, b2.Num, b2.Hash). + Return(errors.New("foo")).Once() + rdm. + On("AddBlockToTrack", ctx, reorgDetectorID, b2.Num, b2.Hash). + Return(nil).Once() + pm.On("storeBridgeEvents", b2.Num, b2.Events). + Return(nil) + driver.handleNewBlock(ctx, b2) + + // processor fails once + b3 := block{ + blockHeader: blockHeader{ + Num: 3, + Hash: common.HexToHash("f00"), + }, + } + rdm. + On("AddBlockToTrack", ctx, reorgDetectorID, b3.Num, b3.Hash). + Return(nil) + pm.On("storeBridgeEvents", b3.Num, b3.Events). + Return(errors.New("foo")).Once() + pm.On("storeBridgeEvents", b3.Num, b3.Events). + Return(nil).Once() + driver.handleNewBlock(ctx, b3) + +} + +func TestHandleReorg(t *testing.T) { + retryAfterErrorPeriod = time.Millisecond * 100 + rdm := NewReorgDetectorMock(t) + pm := NewProcessorMock(t) + dm := NewDownloaderMock(t) + reorgProcessed := make(chan bool) + rdm.On("Subscribe", reorgDetectorID).Return(&reorgdetector.Subscription{ + ReorgProcessed: reorgProcessed, + }) + driver, err := newDriver(rdm, pm, dm) + require.NoError(t, err) + ctx := context.Background() + + // happy path + _, cancel := context.WithCancel(ctx) + downloadCh := make(chan block) + firstReorgedBlock := uint64(5) + pm.On("reorg", firstReorgedBlock).Return(nil) + go driver.handleReorg(cancel, downloadCh, firstReorgedBlock) + close(downloadCh) + done := <-reorgProcessed + require.True(t, done) + + // download ch sends some garbage + _, cancel = context.WithCancel(ctx) + downloadCh = make(chan block) + firstReorgedBlock = uint64(6) + pm.On("reorg", firstReorgedBlock).Return(nil) + go driver.handleReorg(cancel, downloadCh, firstReorgedBlock) + downloadCh <- block{} + downloadCh <- block{} + downloadCh <- block{} + close(downloadCh) + done = <-reorgProcessed + require.True(t, done) + + // processor fails 2 times + _, cancel = context.WithCancel(ctx) + downloadCh = make(chan block) + firstReorgedBlock = uint64(7) + pm.On("reorg", firstReorgedBlock).Return(errors.New("foo")).Once() + pm.On("reorg", firstReorgedBlock).Return(errors.New("foo")).Once() + pm.On("reorg", firstReorgedBlock).Return(nil).Once() + go driver.handleReorg(cancel, downloadCh, firstReorgedBlock) + close(downloadCh) + done = <-reorgProcessed + require.True(t, done) +} diff --git a/localbridgesync/localbridgesync.go b/localbridgesync/localbridgesync.go new file mode 100644 index 00000000..42ab67d1 --- /dev/null +++ b/localbridgesync/localbridgesync.go @@ -0,0 +1,52 @@ +package localbridgesync + +import ( + "time" + + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/log" + "github.com/ethereum/go-ethereum/common" +) + +var ( + retryAfterErrorPeriod = time.Second * 10 + maxRetryAttemptsAfterError = 5 +) + +type LocalBridgeSync struct { + *processor + *driver +} + +func New( + dbPath string, + bridge common.Address, + syncBlockChunkSize uint64, + blockFinalityType etherman.BlockNumberFinality, + rd ReorgDetector, + l2Client EthClienter, +) (*LocalBridgeSync, error) { + p, err := newProcessor(dbPath) + if err != nil { + return nil, err + } + dwn, err := newDownloader(bridge, l2Client, syncBlockChunkSize, blockFinalityType) + if err != nil { + return nil, err + } + dri, err := newDriver(rd, p, dwn) + if err != nil { + return nil, err + } + return &LocalBridgeSync{p, dri}, nil +} + +func retryHandler(funcName string, attempts int) { + if attempts >= maxRetryAttemptsAfterError { + log.Fatalf( + "%s failed too many times (%d)", + funcName, maxRetryAttemptsAfterError, + ) + } + time.Sleep(retryAfterErrorPeriod) +} diff --git a/localbridgesync/mock_downloader_test.go b/localbridgesync/mock_downloader_test.go new file mode 100644 index 00000000..f2df97d0 --- /dev/null +++ b/localbridgesync/mock_downloader_test.go @@ -0,0 +1,100 @@ +// Code generated by mockery v2.22.1. DO NOT EDIT. + +package localbridgesync + +import ( + context "context" + + types "github.com/ethereum/go-ethereum/core/types" + mock "github.com/stretchr/testify/mock" +) + +// DownloaderMock is an autogenerated mock type for the downloaderFull type +type DownloaderMock struct { + mock.Mock +} + +// appendLog provides a mock function with given fields: b, l +func (_m *DownloaderMock) appendLog(b *block, l types.Log) { + _m.Called(b, l) +} + +// download provides a mock function with given fields: ctx, fromBlock, downloadedCh +func (_m *DownloaderMock) download(ctx context.Context, fromBlock uint64, downloadedCh chan block) { + _m.Called(ctx, fromBlock, downloadedCh) +} + +// getBlockHeader provides a mock function with given fields: ctx, blockNum +func (_m *DownloaderMock) getBlockHeader(ctx context.Context, blockNum uint64) blockHeader { + ret := _m.Called(ctx, blockNum) + + var r0 blockHeader + if rf, ok := ret.Get(0).(func(context.Context, uint64) blockHeader); ok { + r0 = rf(ctx, blockNum) + } else { + r0 = ret.Get(0).(blockHeader) + } + + return r0 +} + +// getEventsByBlockRange provides a mock function with given fields: ctx, fromBlock, toBlock +func (_m *DownloaderMock) getEventsByBlockRange(ctx context.Context, fromBlock uint64, toBlock uint64) []block { + ret := _m.Called(ctx, fromBlock, toBlock) + + var r0 []block + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) []block); ok { + r0 = rf(ctx, fromBlock, toBlock) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]block) + } + } + + return r0 +} + +// getLogs provides a mock function with given fields: ctx, fromBlock, toBlock +func (_m *DownloaderMock) getLogs(ctx context.Context, fromBlock uint64, toBlock uint64) []types.Log { + ret := _m.Called(ctx, fromBlock, toBlock) + + var r0 []types.Log + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) []types.Log); ok { + r0 = rf(ctx, fromBlock, toBlock) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.Log) + } + } + + return r0 +} + +// waitForNewBlocks provides a mock function with given fields: ctx, lastBlockSeen +func (_m *DownloaderMock) waitForNewBlocks(ctx context.Context, lastBlockSeen uint64) uint64 { + ret := _m.Called(ctx, lastBlockSeen) + + var r0 uint64 + if rf, ok := ret.Get(0).(func(context.Context, uint64) uint64); ok { + r0 = rf(ctx, lastBlockSeen) + } else { + r0 = ret.Get(0).(uint64) + } + + return r0 +} + +type mockConstructorTestingTNewDownloaderMock interface { + mock.TestingT + Cleanup(func()) +} + +// NewDownloaderMock creates a new instance of DownloaderMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewDownloaderMock(t mockConstructorTestingTNewDownloaderMock) *DownloaderMock { + mock := &DownloaderMock{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/localbridgesync/mock_l2_test.go b/localbridgesync/mock_l2_test.go new file mode 100644 index 00000000..78baa614 --- /dev/null +++ b/localbridgesync/mock_l2_test.go @@ -0,0 +1,484 @@ +// Code generated by mockery v2.22.1. DO NOT EDIT. + +package localbridgesync + +import ( + context "context" + big "math/big" + + common "github.com/ethereum/go-ethereum/common" + + ethereum "github.com/ethereum/go-ethereum" + + mock "github.com/stretchr/testify/mock" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// L2Mock is an autogenerated mock type for the EthClienter type +type L2Mock struct { + mock.Mock +} + +// BlockByHash provides a mock function with given fields: ctx, hash +func (_m *L2Mock) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { + ret := _m.Called(ctx, hash) + + var r0 *types.Block + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Block, error)); ok { + return rf(ctx, hash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Block); ok { + r0 = rf(ctx, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Block) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, hash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BlockByNumber provides a mock function with given fields: ctx, number +func (_m *L2Mock) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { + ret := _m.Called(ctx, number) + + var r0 *types.Block + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Block, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *types.Block); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Block) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BlockNumber provides a mock function with given fields: ctx +func (_m *L2Mock) BlockNumber(ctx context.Context) (uint64, error) { + ret := _m.Called(ctx) + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (uint64, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) uint64); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CallContract provides a mock function with given fields: ctx, call, blockNumber +func (_m *L2Mock) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, call, blockNumber) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg, *big.Int) ([]byte, error)); ok { + return rf(ctx, call, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg, *big.Int) []byte); ok { + r0 = rf(ctx, call, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.CallMsg, *big.Int) error); ok { + r1 = rf(ctx, call, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CodeAt provides a mock function with given fields: ctx, contract, blockNumber +func (_m *L2Mock) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, contract, blockNumber) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) ([]byte, error)); ok { + return rf(ctx, contract, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) []byte); ok { + r0 = rf(ctx, contract, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { + r1 = rf(ctx, contract, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EstimateGas provides a mock function with given fields: ctx, call +func (_m *L2Mock) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) { + ret := _m.Called(ctx, call) + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg) (uint64, error)); ok { + return rf(ctx, call) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg) uint64); ok { + r0 = rf(ctx, call) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.CallMsg) error); ok { + r1 = rf(ctx, call) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FilterLogs provides a mock function with given fields: ctx, q +func (_m *L2Mock) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + ret := _m.Called(ctx, q) + + var r0 []types.Log + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) ([]types.Log, error)); ok { + return rf(ctx, q) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) []types.Log); ok { + r0 = rf(ctx, q) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.Log) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery) error); ok { + r1 = rf(ctx, q) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// HeaderByHash provides a mock function with given fields: ctx, hash +func (_m *L2Mock) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { + ret := _m.Called(ctx, hash) + + var r0 *types.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Header, error)); ok { + return rf(ctx, hash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Header); ok { + r0 = rf(ctx, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Header) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, hash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// HeaderByNumber provides a mock function with given fields: ctx, number +func (_m *L2Mock) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + ret := _m.Called(ctx, number) + + var r0 *types.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Header, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *types.Header); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Header) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PendingCodeAt provides a mock function with given fields: ctx, account +func (_m *L2Mock) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { + ret := _m.Called(ctx, account) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address) ([]byte, error)); ok { + return rf(ctx, account) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address) []byte); ok { + r0 = rf(ctx, account) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address) error); ok { + r1 = rf(ctx, account) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PendingNonceAt provides a mock function with given fields: ctx, account +func (_m *L2Mock) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + ret := _m.Called(ctx, account) + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address) (uint64, error)); ok { + return rf(ctx, account) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address) uint64); ok { + r0 = rf(ctx, account) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address) error); ok { + r1 = rf(ctx, account) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SendTransaction provides a mock function with given fields: ctx, tx +func (_m *L2Mock) SendTransaction(ctx context.Context, tx *types.Transaction) error { + ret := _m.Called(ctx, tx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction) error); ok { + r0 = rf(ctx, tx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SubscribeFilterLogs provides a mock function with given fields: ctx, q, ch +func (_m *L2Mock) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { + ret := _m.Called(ctx, q, ch) + + var r0 ethereum.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- types.Log) (ethereum.Subscription, error)); ok { + return rf(ctx, q, ch) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- types.Log) ethereum.Subscription); ok { + r0 = rf(ctx, q, ch) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(ethereum.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery, chan<- types.Log) error); ok { + r1 = rf(ctx, q, ch) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SubscribeNewHead provides a mock function with given fields: ctx, ch +func (_m *L2Mock) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) { + ret := _m.Called(ctx, ch) + + var r0 ethereum.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Header) (ethereum.Subscription, error)); ok { + return rf(ctx, ch) + } + if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Header) ethereum.Subscription); ok { + r0 = rf(ctx, ch) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(ethereum.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, chan<- *types.Header) error); ok { + r1 = rf(ctx, ch) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SuggestGasPrice provides a mock function with given fields: ctx +func (_m *L2Mock) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SuggestGasTipCap provides a mock function with given fields: ctx +func (_m *L2Mock) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TransactionCount provides a mock function with given fields: ctx, blockHash +func (_m *L2Mock) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { + ret := _m.Called(ctx, blockHash) + + var r0 uint + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (uint, error)); ok { + return rf(ctx, blockHash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) uint); ok { + r0 = rf(ctx, blockHash) + } else { + r0 = ret.Get(0).(uint) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, blockHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TransactionInBlock provides a mock function with given fields: ctx, blockHash, index +func (_m *L2Mock) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { + ret := _m.Called(ctx, blockHash, index) + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash, uint) (*types.Transaction, error)); ok { + return rf(ctx, blockHash, index) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash, uint) *types.Transaction); ok { + r0 = rf(ctx, blockHash, index) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash, uint) error); ok { + r1 = rf(ctx, blockHash, index) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewL2Mock interface { + mock.TestingT + Cleanup(func()) +} + +// NewL2Mock creates a new instance of L2Mock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewL2Mock(t mockConstructorTestingTNewL2Mock) *L2Mock { + mock := &L2Mock{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/localbridgesync/mock_processor_test.go b/localbridgesync/mock_processor_test.go new file mode 100644 index 00000000..4a629f5c --- /dev/null +++ b/localbridgesync/mock_processor_test.go @@ -0,0 +1,81 @@ +// Code generated by mockery v2.22.1. DO NOT EDIT. + +package localbridgesync + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" +) + +// ProcessorMock is an autogenerated mock type for the processorInterface type +type ProcessorMock struct { + mock.Mock +} + +// getLastProcessedBlock provides a mock function with given fields: ctx +func (_m *ProcessorMock) getLastProcessedBlock(ctx context.Context) (uint64, error) { + ret := _m.Called(ctx) + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (uint64, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) uint64); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// reorg provides a mock function with given fields: firstReorgedBlock +func (_m *ProcessorMock) reorg(firstReorgedBlock uint64) error { + ret := _m.Called(firstReorgedBlock) + + var r0 error + if rf, ok := ret.Get(0).(func(uint64) error); ok { + r0 = rf(firstReorgedBlock) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// storeBridgeEvents provides a mock function with given fields: blockNum, events +func (_m *ProcessorMock) storeBridgeEvents(blockNum uint64, events []BridgeEvent) error { + ret := _m.Called(blockNum, events) + + var r0 error + if rf, ok := ret.Get(0).(func(uint64, []BridgeEvent) error); ok { + r0 = rf(blockNum, events) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewProcessorMock interface { + mock.TestingT + Cleanup(func()) +} + +// NewProcessorMock creates a new instance of ProcessorMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewProcessorMock(t mockConstructorTestingTNewProcessorMock) *ProcessorMock { + mock := &ProcessorMock{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/localbridgesync/mock_reorgdetector_test.go b/localbridgesync/mock_reorgdetector_test.go new file mode 100644 index 00000000..d11434a1 --- /dev/null +++ b/localbridgesync/mock_reorgdetector_test.go @@ -0,0 +1,63 @@ +// Code generated by mockery v2.22.1. DO NOT EDIT. + +package localbridgesync + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" + + reorgdetector "github.com/0xPolygon/cdk/reorgdetector" +) + +// ReorgDetectorMock is an autogenerated mock type for the ReorgDetector type +type ReorgDetectorMock struct { + mock.Mock +} + +// AddBlockToTrack provides a mock function with given fields: ctx, id, blockNum, blockHash +func (_m *ReorgDetectorMock) AddBlockToTrack(ctx context.Context, id string, blockNum uint64, blockHash common.Hash) error { + ret := _m.Called(ctx, id, blockNum, blockHash) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, uint64, common.Hash) error); ok { + r0 = rf(ctx, id, blockNum, blockHash) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Subscribe provides a mock function with given fields: id +func (_m *ReorgDetectorMock) Subscribe(id string) *reorgdetector.Subscription { + ret := _m.Called(id) + + var r0 *reorgdetector.Subscription + if rf, ok := ret.Get(0).(func(string) *reorgdetector.Subscription); ok { + r0 = rf(id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*reorgdetector.Subscription) + } + } + + return r0 +} + +type mockConstructorTestingTNewReorgDetectorMock interface { + mock.TestingT + Cleanup(func()) +} + +// NewReorgDetectorMock creates a new instance of ReorgDetectorMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewReorgDetectorMock(t mockConstructorTestingTNewReorgDetectorMock) *ReorgDetectorMock { + mock := &ReorgDetectorMock{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/localbridgesync/processor.go b/localbridgesync/processor.go new file mode 100644 index 00000000..2e6f27a0 --- /dev/null +++ b/localbridgesync/processor.go @@ -0,0 +1,170 @@ +package localbridgesync + +import ( + "context" + "encoding/binary" + "encoding/json" + "errors" + + "github.com/ledgerwatch/erigon-lib/kv" + "github.com/ledgerwatch/erigon-lib/kv/mdbx" +) + +const ( + eventsTable = "localbridgesync-events" + lastBlockTable = "localbridgesync-lastBlock" +) + +var ( + ErrBlockNotProcessed = errors.New("given block(s) have not been processed yet") + lastBlokcKey = []byte("lb") +) + +type processor struct { + db kv.RwDB +} + +func tableCfgFunc(defaultBuckets kv.TableCfg) kv.TableCfg { + return kv.TableCfg{ + eventsTable: {}, + lastBlockTable: {}, + } +} + +func newProcessor(dbPath string) (*processor, error) { + db, err := mdbx.NewMDBX(nil). + Path(dbPath). + WithTableCfg(tableCfgFunc). + Open() + if err != nil { + return nil, err + } + return &processor{ + db: db, + }, nil +} + +// GetClaimsAndBridges returns the claims and bridges occurred between fromBlock, toBlock both included. +// If toBlock has not been porcessed yet, ErrBlockNotProcessed will be returned +func (p *processor) GetClaimsAndBridges( + ctx context.Context, fromBlock, toBlock uint64, +) ([]BridgeEvent, error) { + events := []BridgeEvent{} + + tx, err := p.db.BeginRo(ctx) + if err != nil { + return nil, err + } + defer tx.Rollback() + lpb, err := p.getLastProcessedBlockWithTx(tx) + if lpb < toBlock { + return nil, ErrBlockNotProcessed + } + c, err := tx.Cursor(eventsTable) + if err != nil { + return nil, err + } + defer c.Close() + + for k, v, err := c.Seek(blockNum2Bytes(fromBlock)); k != nil; k, v, err = c.Next() { + if err != nil { + return nil, err + } + if bytes2BlockNum(k) > toBlock { + break + } + blockEvents := []BridgeEvent{} + err := json.Unmarshal(v, &blockEvents) + if err != nil { + return nil, err + } + events = append(events, blockEvents...) + } + + return events, nil +} + +func (p *processor) getLastProcessedBlock(ctx context.Context) (uint64, error) { + tx, err := p.db.BeginRo(ctx) + if err != nil { + return 0, err + } + defer tx.Rollback() + return p.getLastProcessedBlockWithTx(tx) +} + +func (p *processor) getLastProcessedBlockWithTx(tx kv.Tx) (uint64, error) { + if blockNumBytes, err := tx.GetOne(lastBlockTable, lastBlokcKey); err != nil { + return 0, err + } else if blockNumBytes == nil { + return 0, nil + } else { + return bytes2BlockNum(blockNumBytes), nil + } +} + +func (p *processor) reorg(firstReorgedBlock uint64) error { + tx, err := p.db.BeginRw(context.Background()) + if err != nil { + return err + } + c, err := tx.Cursor(eventsTable) + if err != nil { + return err + } + defer c.Close() + firstKey := blockNum2Bytes(firstReorgedBlock) + for k, _, err := c.Seek(firstKey); k != nil; k, _, err = c.Next() { + if err != nil { + tx.Rollback() + return err + } + if err := tx.Delete(eventsTable, k); err != nil { + tx.Rollback() + return err + } + } + if err := p.updateLastProcessedBlock(tx, firstReorgedBlock-1); err != nil { + tx.Rollback() + return err + } + return tx.Commit() +} + +func (p *processor) storeBridgeEvents(blockNum uint64, events []BridgeEvent) error { + tx, err := p.db.BeginRw(context.Background()) + if err != nil { + return err + } + if len(events) > 0 { + value, err := json.Marshal(events) + if err != nil { + tx.Rollback() + return err + } + if err := tx.Put(eventsTable, blockNum2Bytes(blockNum), value); err != nil { + tx.Rollback() + return err + } + } + if err := p.updateLastProcessedBlock(tx, blockNum); err != nil { + tx.Rollback() + return err + } + return tx.Commit() +} + +func (p *processor) updateLastProcessedBlock(tx kv.RwTx, blockNum uint64) error { + blockNumBytes := blockNum2Bytes(blockNum) + return tx.Put(lastBlockTable, lastBlokcKey, blockNumBytes) +} + +func blockNum2Bytes(blockNum uint64) []byte { + key := make([]byte, 8) + binary.LittleEndian.PutUint64(key, blockNum) + return key +} + +func bytes2BlockNum(key []byte) uint64 { + return binary.LittleEndian.Uint64(key) +} diff --git a/localbridgesync/processor_test.go b/localbridgesync/processor_test.go new file mode 100644 index 00000000..8e6884c2 --- /dev/null +++ b/localbridgesync/processor_test.go @@ -0,0 +1,425 @@ +package localbridgesync + +import ( + "context" + "fmt" + "math/big" + "slices" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestProceessor(t *testing.T) { + path := t.TempDir() + p, err := newProcessor(path) + require.NoError(t, err) + actions := []processAction{ + // processed: ~ + &getLastProcessedBlockAction{ + p: p, + description: "on an empty processor", + ctx: context.Background(), + expectedLastProcessedBlock: 0, + expectedErr: nil, + }, + &reorgAction{ + p: p, + description: "on an empty processor: firstReorgedBlock = 0", + firstReorgedBlock: 0, + expectedErr: nil, + }, + &reorgAction{ + p: p, + description: "on an empty processor: firstReorgedBlock = 1", + firstReorgedBlock: 1, + expectedErr: nil, + }, + &getClaimsAndBridgesAction{ + p: p, + description: "on an empty processor", + ctx: context.Background(), + fromBlock: 0, + toBlock: 2, + expectedEvents: nil, + expectedErr: ErrBlockNotProcessed, + }, + &storeBridgeEventsAction{ + p: p, + description: "block1", + blockNum: block1.Num, + events: block1.Events, + expectedErr: nil, + }, + // processed: block1 + &getLastProcessedBlockAction{ + p: p, + description: "after block1", + ctx: context.Background(), + expectedLastProcessedBlock: 1, + expectedErr: nil, + }, + &getClaimsAndBridgesAction{ + p: p, + description: "after block1: range 0, 2", + ctx: context.Background(), + fromBlock: 0, + toBlock: 2, + expectedEvents: nil, + expectedErr: ErrBlockNotProcessed, + }, + &getClaimsAndBridgesAction{ + p: p, + description: "after block1: range 1, 1", + ctx: context.Background(), + fromBlock: 1, + toBlock: 1, + expectedEvents: block1.Events, + expectedErr: nil, + }, + &reorgAction{ + p: p, + description: "after block1", + firstReorgedBlock: 1, + expectedErr: nil, + }, + // processed: ~ + &getClaimsAndBridgesAction{ + p: p, + description: "after block1 reorged", + ctx: context.Background(), + fromBlock: 0, + toBlock: 2, + expectedEvents: nil, + expectedErr: ErrBlockNotProcessed, + }, + &storeBridgeEventsAction{ + p: p, + description: "block1 (after it's reorged)", + blockNum: block1.Num, + events: block1.Events, + expectedErr: nil, + }, + // processed: block3 + &storeBridgeEventsAction{ + p: p, + description: "block3", + blockNum: block3.Num, + events: block3.Events, + expectedErr: nil, + }, + // processed: block1, block3 + &getLastProcessedBlockAction{ + p: p, + description: "after block3", + ctx: context.Background(), + expectedLastProcessedBlock: 3, + expectedErr: nil, + }, + &getClaimsAndBridgesAction{ + p: p, + description: "after block3: range 2, 2", + ctx: context.Background(), + fromBlock: 2, + toBlock: 2, + expectedEvents: []BridgeEvent{}, + expectedErr: nil, + }, + &getClaimsAndBridgesAction{ + p: p, + description: "after block3: range 1, 3", + ctx: context.Background(), + fromBlock: 1, + toBlock: 3, + expectedEvents: append(block1.Events, block3.Events...), + expectedErr: nil, + }, + &reorgAction{ + p: p, + description: "after block3, with value 3", + firstReorgedBlock: 3, + expectedErr: nil, + }, + // processed: block1 + &getLastProcessedBlockAction{ + p: p, + description: "after block3 reorged", + ctx: context.Background(), + expectedLastProcessedBlock: 2, + expectedErr: nil, + }, + &reorgAction{ + p: p, + description: "after block3, with value 2", + firstReorgedBlock: 2, + expectedErr: nil, + }, + &getLastProcessedBlockAction{ + p: p, + description: "after block2 reorged", + ctx: context.Background(), + expectedLastProcessedBlock: 1, + expectedErr: nil, + }, + &storeBridgeEventsAction{ + p: p, + description: "block3 after reorg", + blockNum: block3.Num, + events: block3.Events, + expectedErr: nil, + }, + // processed: block1, block3 + &storeBridgeEventsAction{ + p: p, + description: "block4", + blockNum: block4.Num, + events: block4.Events, + expectedErr: nil, + }, + // processed: block1, block3, block4 + &storeBridgeEventsAction{ + p: p, + description: "block5", + blockNum: block5.Num, + events: block5.Events, + expectedErr: nil, + }, + // processed: block1, block3, block4, block5 + &getLastProcessedBlockAction{ + p: p, + description: "after block5", + ctx: context.Background(), + expectedLastProcessedBlock: 5, + expectedErr: nil, + }, + &getClaimsAndBridgesAction{ + p: p, + description: "after block5: range 1, 3", + ctx: context.Background(), + fromBlock: 1, + toBlock: 3, + expectedEvents: append(block1.Events, block3.Events...), + expectedErr: nil, + }, + &getClaimsAndBridgesAction{ + p: p, + description: "after block5: range 4, 5", + ctx: context.Background(), + fromBlock: 4, + toBlock: 5, + expectedEvents: append(block4.Events, block5.Events...), + expectedErr: nil, + }, + &getClaimsAndBridgesAction{ + p: p, + description: "after block5: range 0, 5", + ctx: context.Background(), + fromBlock: 0, + toBlock: 5, + expectedEvents: slices.Concat( + block1.Events, + block3.Events, + block4.Events, + block5.Events, + ), + expectedErr: nil, + }, + } + + for _, a := range actions { + t.Run(fmt.Sprintf("%s: %s", a.method(), a.desc()), a.execute) + } +} + +// BOILERPLATE + +// blocks + +var ( + block1 = block{ + blockHeader: blockHeader{ + Num: 1, + Hash: common.HexToHash("01"), + }, + Events: []BridgeEvent{ + {Bridge: &Bridge{ + LeafType: 1, + OriginNetwork: 1, + OriginAddress: common.HexToAddress("01"), + DestinationNetwork: 1, + DestinationAddress: common.HexToAddress("01"), + Amount: big.NewInt(1), + Metadata: common.Hex2Bytes("01"), + DepositCount: 1, + }}, + {Claim: &Claim{ + GlobalIndex: big.NewInt(1), + OriginNetwork: 1, + OriginAddress: common.HexToAddress("01"), + DestinationAddress: common.HexToAddress("01"), + Amount: big.NewInt(1), + }}, + }, + } + block3 = block{ + blockHeader: blockHeader{ + Num: 3, + Hash: common.HexToHash("02"), + }, + Events: []BridgeEvent{ + {Bridge: &Bridge{ + LeafType: 2, + OriginNetwork: 2, + OriginAddress: common.HexToAddress("02"), + DestinationNetwork: 2, + DestinationAddress: common.HexToAddress("02"), + Amount: big.NewInt(2), + Metadata: common.Hex2Bytes("02"), + DepositCount: 2, + }}, + {Bridge: &Bridge{ + LeafType: 3, + OriginNetwork: 3, + OriginAddress: common.HexToAddress("03"), + DestinationNetwork: 3, + DestinationAddress: common.HexToAddress("03"), + Amount: nil, + Metadata: common.Hex2Bytes("03"), + DepositCount: 3, + }}, + }, + } + block4 = block{ + blockHeader: blockHeader{ + Num: 4, + Hash: common.HexToHash("03"), + }, + Events: []BridgeEvent{}, + } + block5 = block{ + blockHeader: blockHeader{ + Num: 5, + Hash: common.HexToHash("04"), + }, + Events: []BridgeEvent{ + {Claim: &Claim{ + GlobalIndex: big.NewInt(4), + OriginNetwork: 4, + OriginAddress: common.HexToAddress("04"), + DestinationAddress: common.HexToAddress("04"), + Amount: big.NewInt(4), + }}, + {Claim: &Claim{ + GlobalIndex: big.NewInt(5), + OriginNetwork: 5, + OriginAddress: common.HexToAddress("05"), + DestinationAddress: common.HexToAddress("05"), + Amount: big.NewInt(5), + }}, + }, + } +) + +// actions + +type processAction interface { + method() string + desc() string + execute(t *testing.T) +} + +// GetClaimsAndBridges + +type getClaimsAndBridgesAction struct { + p *processor + description string + ctx context.Context + fromBlock uint64 + toBlock uint64 + expectedEvents []BridgeEvent + expectedErr error +} + +func (a *getClaimsAndBridgesAction) method() string { + return "GetClaimsAndBridges" +} + +func (a *getClaimsAndBridgesAction) desc() string { + return a.description +} + +func (a *getClaimsAndBridgesAction) execute(t *testing.T) { + actualEvents, actualErr := a.p.GetClaimsAndBridges(a.ctx, a.fromBlock, a.toBlock) + require.Equal(t, a.expectedEvents, actualEvents) + require.Equal(t, a.expectedErr, actualErr) +} + +// getLastProcessedBlock + +type getLastProcessedBlockAction struct { + p *processor + description string + ctx context.Context + expectedLastProcessedBlock uint64 + expectedErr error +} + +func (a *getLastProcessedBlockAction) method() string { + return "getLastProcessedBlock" +} + +func (a *getLastProcessedBlockAction) desc() string { + return a.description +} + +func (a *getLastProcessedBlockAction) execute(t *testing.T) { + actualLastProcessedBlock, actualErr := a.p.getLastProcessedBlock(a.ctx) + require.Equal(t, a.expectedLastProcessedBlock, actualLastProcessedBlock) + require.Equal(t, a.expectedErr, actualErr) +} + +// reorg + +type reorgAction struct { + p *processor + description string + firstReorgedBlock uint64 + expectedErr error +} + +func (a *reorgAction) method() string { + return "reorg" +} + +func (a *reorgAction) desc() string { + return a.description +} + +func (a *reorgAction) execute(t *testing.T) { + actualErr := a.p.reorg(a.firstReorgedBlock) + require.Equal(t, a.expectedErr, actualErr) +} + +// storeBridgeEvents + +type storeBridgeEventsAction struct { + p *processor + description string + blockNum uint64 + events []BridgeEvent + expectedErr error +} + +func (a *storeBridgeEventsAction) method() string { + return "storeBridgeEvents" +} + +func (a *storeBridgeEventsAction) desc() string { + return a.description +} + +func (a *storeBridgeEventsAction) execute(t *testing.T) { + actualErr := a.p.storeBridgeEvents(a.blockNum, a.events) + require.Equal(t, a.expectedErr, actualErr) +} diff --git a/localbridgesync/types.go b/localbridgesync/types.go new file mode 100644 index 00000000..3a6a508e --- /dev/null +++ b/localbridgesync/types.go @@ -0,0 +1,42 @@ +package localbridgesync + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +type Bridge struct { + LeafType uint8 + OriginNetwork uint32 + OriginAddress common.Address + DestinationNetwork uint32 + DestinationAddress common.Address + Amount *big.Int + Metadata []byte + DepositCount uint32 +} + +type Claim struct { + // TODO: pre uLxLy there was Index instead of GlobalIndex, should we treat this differently? + GlobalIndex *big.Int + OriginNetwork uint32 + OriginAddress common.Address + DestinationAddress common.Address + Amount *big.Int +} + +type BridgeEvent struct { + Bridge *Bridge + Claim *Claim +} + +type block struct { + blockHeader + Events []BridgeEvent +} + +type blockHeader struct { + Num uint64 + Hash common.Hash +} diff --git a/log/config.go b/log/config.go new file mode 100644 index 00000000..2f166ee9 --- /dev/null +++ b/log/config.go @@ -0,0 +1,13 @@ +package log + +// Config for log +type Config struct { + // Environment defining the log format ("production" or "development"). + // In development mode enables development mode (which makes DPanicLevel logs panic), uses a console encoder, writes to standard error, and disables sampling. Stacktraces are automatically included on logs of WarnLevel and above. + // Check [here](https://pkg.go.dev/go.uber.org/zap@v1.24.0#NewDevelopmentConfig) + Environment LogEnvironment `mapstructure:"Environment" jsonschema:"enum=production,enum=development"` + // Level of log. As lower value more logs are going to be generated + Level string `mapstructure:"Level" jsonschema:"enum=debug,enum=info,enum=warn,enum=error,enum=dpanic,enum=panic,enum=fatal"` + // Outputs + Outputs []string `mapstructure:"Outputs"` +} diff --git a/log/log.go b/log/log.go new file mode 100644 index 00000000..6f776fa6 --- /dev/null +++ b/log/log.go @@ -0,0 +1,320 @@ +package log + +import ( + "fmt" + "os" + "strings" + "sync/atomic" + + zkevm "github.com/0xPolygon/cdk" + "github.com/hermeznetwork/tracerr" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// LogEnvironment represents the possible log environments. +type LogEnvironment string + +const ( + // EnvironmentProduction production log environment. + EnvironmentProduction = LogEnvironment("production") + // EnvironmentDevelopment development log environment. + EnvironmentDevelopment = LogEnvironment("development") +) + +// Logger is a wrapper providing logging facilities. +type Logger struct { + x *zap.SugaredLogger +} + +// root logger +var log atomic.Pointer[Logger] + +func getDefaultLog() *Logger { + l := log.Load() + if l != nil { + return l + } + // default level: debug + zapLogger, _, err := NewLogger(Config{ + Environment: EnvironmentDevelopment, + Level: "debug", + Outputs: []string{"stderr"}, + }) + if err != nil { + panic(err) + } + log.Store(&Logger{x: zapLogger}) + return log.Load() +} + +// Init the logger with defined level. outputs defines the outputs where the +// logs will be sent. By default outputs contains "stdout", which prints the +// logs at the output of the process. To add a log file as output, the path +// should be added at the outputs array. To avoid printing the logs but storing +// them on a file, can use []string{"pathtofile.log"} +func Init(cfg Config) { + zapLogger, _, err := NewLogger(cfg) + if err != nil { + panic(err) + } + log.Store(&Logger{x: zapLogger}) +} + +// NewLogger creates the logger with defined level. outputs defines the outputs where the +// logs will be sent. By default, outputs contains "stdout", which prints the +// logs at the output of the process. To add a log file as output, the path +// should be added at the outputs array. To avoid printing the logs but storing +// them on a file, can use []string{"pathtofile.log"} +func NewLogger(cfg Config) (*zap.SugaredLogger, *zap.AtomicLevel, error) { + var level zap.AtomicLevel + err := level.UnmarshalText([]byte(cfg.Level)) + if err != nil { + return nil, nil, fmt.Errorf("error on setting log level: %s", err) + } + + var zapCfg zap.Config + + switch cfg.Environment { + case EnvironmentProduction: + zapCfg = zap.NewProductionConfig() + default: + zapCfg = zap.NewDevelopmentConfig() + zapCfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + } + zapCfg.Level = level + zapCfg.OutputPaths = cfg.Outputs + zapCfg.InitialFields = map[string]interface{}{ + "version": zkevm.Version, + "pid": os.Getpid(), + } + + logger, err := zapCfg.Build() + if err != nil { + return nil, nil, err + } + defer logger.Sync() //nolint:gosec,errcheck + + // skip 2 callers: one for our wrapper methods and one for the package functions + withOptions := logger.WithOptions(zap.AddCallerSkip(2)) //nolint:gomnd + return withOptions.Sugar(), &level, nil +} + +// WithFields returns a new Logger (derived from the root one) with additional +// fields as per keyValuePairs. The root Logger instance is not affected. +func WithFields(keyValuePairs ...interface{}) *Logger { + l := getDefaultLog().WithFields(keyValuePairs...) + + // since we are returning a new instance, remove one caller from the + // stack, because we'll be calling the retruned Logger methods + // directly, not the package functions. + x := l.x.WithOptions(zap.AddCallerSkip(-1)) + l.x = x + return l +} + +// WithFields returns a new Logger with additional fields as per keyValuePairs. +// The original Logger instance is not affected. +func (l *Logger) WithFields(keyValuePairs ...interface{}) *Logger { + return &Logger{ + x: l.x.With(keyValuePairs...), + } +} + +func sprintStackTrace(st []tracerr.Frame) string { + builder := strings.Builder{} + // Skip deepest frame because it belongs to the go runtime and we don't + // care about it. + if len(st) > 0 { + st = st[:len(st)-1] + } + for _, f := range st { + builder.WriteString(fmt.Sprintf("\n%s:%d %s()", f.Path, f.Line, f.Func)) + } + builder.WriteString("\n") + return builder.String() +} + +// appendStackTraceMaybeArgs will append the stacktrace to the args +func appendStackTraceMaybeArgs(args []interface{}) []interface{} { + for i := range args { + if err, ok := args[i].(error); ok { + err = tracerr.Wrap(err) + st := tracerr.StackTrace(err) + return append(args, sprintStackTrace(st)) + } + } + return args +} + +// Debug calls log.Debug +func (l *Logger) Debug(args ...interface{}) { + l.x.Debug(args...) +} + +// Info calls log.Info +func (l *Logger) Info(args ...interface{}) { + l.x.Info(args...) +} + +// Warn calls log.Warn +func (l *Logger) Warn(args ...interface{}) { + l.x.Warn(args...) +} + +// Error calls log.Error +func (l *Logger) Error(args ...interface{}) { + l.x.Error(args...) +} + +// Fatal calls log.Fatal +func (l *Logger) Fatal(args ...interface{}) { + l.x.Fatal(args...) +} + +// Debugf calls log.Debugf +func (l *Logger) Debugf(template string, args ...interface{}) { + l.x.Debugf(template, args...) +} + +// Infof calls log.Infof +func (l *Logger) Infof(template string, args ...interface{}) { + l.x.Infof(template, args...) +} + +// Warnf calls log.Warnf +func (l *Logger) Warnf(template string, args ...interface{}) { + l.x.Warnf(template, args...) +} + +// Fatalf calls log.Fatalf +func (l *Logger) Fatalf(template string, args ...interface{}) { + l.x.Fatalf(template, args...) +} + +// Errorf calls log.Errorf and stores the error message into the ErrorFile +func (l *Logger) Errorf(template string, args ...interface{}) { + l.x.Errorf(template, args...) +} + +// Debug calls log.Debug on the root Logger. +func Debug(args ...interface{}) { + getDefaultLog().Debug(args...) +} + +// Info calls log.Info on the root Logger. +func Info(args ...interface{}) { + getDefaultLog().Info(args...) +} + +// Warn calls log.Warn on the root Logger. +func Warn(args ...interface{}) { + getDefaultLog().Warn(args...) +} + +// Error calls log.Error on the root Logger. +func Error(args ...interface{}) { + args = appendStackTraceMaybeArgs(args) + getDefaultLog().Error(args...) +} + +// Fatal calls log.Fatal on the root Logger. +func Fatal(args ...interface{}) { + args = appendStackTraceMaybeArgs(args) + getDefaultLog().Fatal(args...) +} + +// Debugf calls log.Debugf on the root Logger. +func Debugf(template string, args ...interface{}) { + getDefaultLog().Debugf(template, args...) +} + +// Infof calls log.Infof on the root Logger. +func Infof(template string, args ...interface{}) { + getDefaultLog().Infof(template, args...) +} + +// Warnf calls log.Warnf on the root Logger. +func Warnf(template string, args ...interface{}) { + getDefaultLog().Warnf(template, args...) +} + +// Fatalf calls log.Fatalf on the root Logger. +func Fatalf(template string, args ...interface{}) { + args = appendStackTraceMaybeArgs(args) + getDefaultLog().Fatalf(template, args...) +} + +// Errorf calls log.Errorf on the root logger and stores the error message into +// the ErrorFile. +func Errorf(template string, args ...interface{}) { + args = appendStackTraceMaybeArgs(args) + getDefaultLog().Errorf(template, args...) +} + +// appendStackTraceMaybeKV will append the stacktrace to the KV +func appendStackTraceMaybeKV(msg string, kv []interface{}) string { + for i := range kv { + if i%2 == 0 { + continue + } + if err, ok := kv[i].(error); ok { + err = tracerr.Wrap(err) + st := tracerr.StackTrace(err) + return fmt.Sprintf("%v: %v%v\n", msg, err, sprintStackTrace(st)) + } + } + return msg +} + +// Debugw calls log.Debugw +func (l *Logger) Debugw(msg string, kv ...interface{}) { + l.x.Debugw(msg, kv...) +} + +// Infow calls log.Infow +func (l *Logger) Infow(msg string, kv ...interface{}) { + l.x.Infow(msg, kv...) +} + +// Warnw calls log.Warnw +func (l *Logger) Warnw(msg string, kv ...interface{}) { + l.x.Warnw(msg, kv...) +} + +// Errorw calls log.Errorw +func (l *Logger) Errorw(msg string, kv ...interface{}) { + l.x.Errorw(msg, kv...) +} + +// Fatalw calls log.Fatalw +func (l *Logger) Fatalw(msg string, kv ...interface{}) { + l.x.Fatalw(msg, kv...) +} + +// Debugw calls log.Debugw on the root Logger. +func Debugw(msg string, kv ...interface{}) { + getDefaultLog().Debugw(msg, kv...) +} + +// Infow calls log.Infow on the root Logger. +func Infow(msg string, kv ...interface{}) { + getDefaultLog().Infow(msg, kv...) +} + +// Warnw calls log.Warnw on the root Logger. +func Warnw(msg string, kv ...interface{}) { + getDefaultLog().Warnw(msg, kv...) +} + +// Errorw calls log.Errorw on the root Logger. +func Errorw(msg string, kv ...interface{}) { + msg = appendStackTraceMaybeKV(msg, kv) + getDefaultLog().Errorw(msg, kv...) +} + +// Fatalw calls log.Fatalw on the root Logger. +func Fatalw(msg string, kv ...interface{}) { + msg = appendStackTraceMaybeKV(msg, kv) + getDefaultLog().Fatalw(msg, kv...) +} diff --git a/log/log_test.go b/log/log_test.go new file mode 100644 index 00000000..9d33bcd0 --- /dev/null +++ b/log/log_test.go @@ -0,0 +1,37 @@ +package log + +import ( + "testing" +) + +func TestLogNotInitialized(t *testing.T) { + Info("Test log.Info", " value is ", 10) + Infof("Test log.Infof %d", 10) + Infow("Test log.Infow", "value", 10) + Debugf("Test log.Debugf %d", 10) + Error("Test log.Error", " value is ", 10) + Errorf("Test log.Errorf %d", 10) + Errorw("Test log.Errorw", "value", 10) + Warnf("Test log.Warnf %d", 10) + Warnw("Test log.Warnw", "value", 10) +} + +func TestLog(t *testing.T) { + cfg := Config{ + Environment: EnvironmentDevelopment, + Level: "debug", + Outputs: []string{"stderr"}, //[]string{"stdout", "test.log"} + } + + Init(cfg) + + Info("Test log.Info", " value is ", 10) + Infof("Test log.Infof %d", 10) + Infow("Test log.Infow", "value", 10) + Debugf("Test log.Debugf %d", 10) + Error("Test log.Error", " value is ", 10) + Errorf("Test log.Errorf %d", 10) + Errorw("Test log.Errorw", "value", 10) + Warnf("Test log.Warnf %d", 10) + Warnw("Test log.Warnw", "value", 10) +} diff --git a/merkletree/key.go b/merkletree/key.go new file mode 100644 index 00000000..1534f462 --- /dev/null +++ b/merkletree/key.go @@ -0,0 +1,201 @@ +package merkletree + +import ( + "math" + "math/big" + + "github.com/ethereum/go-ethereum/common" + poseidon "github.com/iden3/go-iden3-crypto/goldenposeidon" +) + +// Key stores key of the leaf +type Key [32]byte + +const ( + // HashPoseidonAllZeroes represents the poseidon hash for an input with all + // bits set to zero. + HashPoseidonAllZeroes = "0xc71603f33a1144ca7953db0ab48808f4c4055e3364a246c33c18a9786cb0b359" +) + +// keyEthAddr is the common code for all the keys related to ethereum addresses. +func keyEthAddr(ethAddr common.Address, leafType leafType, key1Capacity [4]uint64) ([]byte, error) { + ethAddrBI := new(big.Int).SetBytes(ethAddr.Bytes()) + ethAddrArr := scalar2fea(ethAddrBI) + + key1 := [8]uint64{ + ethAddrArr[0], + ethAddrArr[1], + ethAddrArr[2], + ethAddrArr[3], + ethAddrArr[4], + 0, + uint64(leafType), + 0, + } + + result, err := poseidon.Hash(key1, key1Capacity) + if err != nil { + return nil, err + } + + return h4ToFilledByteSlice(result[:]), nil +} + +func defaultCapIn() ([4]uint64, error) { + capIn, err := StringToh4(HashPoseidonAllZeroes) + if err != nil { + return [4]uint64{}, err + } + + return [4]uint64{capIn[0], capIn[1], capIn[2], capIn[3]}, nil +} + +// KeyEthAddrBalance returns the key of balance leaf: +// hk0: H([0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0]) +// key: H([ethAddr[0:4], ethAddr[4:8], ethAddr[8:12], ethAddr[12:16], ethAddr[16:20], 0, 0, 0], [hk0[0], hk0[1], hk0[2], hk0[3]]) +func KeyEthAddrBalance(ethAddr common.Address) ([]byte, error) { + capIn, err := defaultCapIn() + if err != nil { + return nil, err + } + + return keyEthAddr(ethAddr, LeafTypeBalance, capIn) +} + +// KeyEthAddrNonce returns the key of nonce leaf: +// hk0: H([0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0]) +// key: H([ethAddr[0:4], ethAddr[4:8], ethAddr[8:12], ethAddr[12:16], ethAddr[16:20], 0, 1, 0], [hk0[0], hk0[1], hk0[2], hk0[3]] +func KeyEthAddrNonce(ethAddr common.Address) ([]byte, error) { + capIn, err := defaultCapIn() + if err != nil { + return nil, err + } + + return keyEthAddr(ethAddr, LeafTypeNonce, capIn) +} + +// KeyContractCode returns the key of contract code leaf: +// hk0: H([0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0]) +// key: H([ethAddr[0:4], ethAddr[4:8], ethAddr[8:12], ethAddr[12:16], ethAddr[16:20], 0, 2, 0], [hk0[0], hk0[1], hk0[2], hk0[3]] +func KeyContractCode(ethAddr common.Address) ([]byte, error) { + capIn, err := defaultCapIn() + if err != nil { + return nil, err + } + + return keyEthAddr(ethAddr, LeafTypeCode, capIn) +} + +// KeyContractStorage returns the key of contract storage position leaf: +// hk0: H([stoPos[0:4], stoPos[4:8], stoPos[8:12], stoPos[12:16], stoPos[16:20], stoPos[20:24], stoPos[24:28], stoPos[28:32], [0, 0, 0, 0]) +// key: H([ethAddr[0:4], ethAddr[4:8], ethAddr[8:12], ethAddr[12:16], ethAddr[16:20], 0, 3, 0], [hk0[0], hk0[1], hk0[2], hk0[3]) +func KeyContractStorage(ethAddr common.Address, storagePos []byte) ([]byte, error) { + storageBI := new(big.Int).SetBytes(storagePos) + + storageArr := scalar2fea(storageBI) + + hk0, err := poseidon.Hash([8]uint64{ + storageArr[0], + storageArr[1], + storageArr[2], + storageArr[3], + storageArr[4], + storageArr[5], + storageArr[6], + storageArr[7], + }, [4]uint64{}) + if err != nil { + return nil, err + } + + return keyEthAddr(ethAddr, LeafTypeStorage, hk0) +} + +// HashContractBytecode computes the bytecode hash in order to add it to the +// state-tree. +func HashContractBytecode(code []byte) ([]uint64, error) { + const ( + bytecodeElementsHash = 8 + bytecodeBytesElement = 7 + + maxBytesToAdd = bytecodeElementsHash * bytecodeBytesElement + ) + + // add 0x01 + code = append(code, 0x01) // nolint:gomnd + + // add padding + for len(code)%(56) != 0 { // nolint:gomnd + code = append(code, 0x00) // nolint:gomnd + } + + code[len(code)-1] = code[len(code)-1] | 0x80 // nolint:gomnd + + numHashes := int(math.Ceil(float64(len(code)) / float64(maxBytesToAdd))) + + tmpHash := [4]uint64{} + var err error + + bytesPointer := 0 + for i := 0; i < numHashes; i++ { + elementsToHash := [12]uint64{} + + for j := 0; j < 4; j++ { + elementsToHash[j] = tmpHash[j] + } + + subsetBytecode := code[bytesPointer : int(math.Min(float64(len(code)-1), float64(bytesPointer+maxBytesToAdd)))+1] + bytesPointer += maxBytesToAdd + tmpElem := [7]byte{} + counter := 0 + index := 4 + for j := 0; j < maxBytesToAdd; j++ { + byteToAdd := []byte{0} + + if j < len(subsetBytecode) { + byteToAdd = subsetBytecode[j : j+1] + } + + tmpElem[bytecodeBytesElement-1-counter] = byteToAdd[0] + counter++ + + if counter == bytecodeBytesElement { + elementsToHash[index] = new(big.Int).SetBytes(tmpElem[:]).Uint64() + index++ + tmpElem = [7]byte{} + counter = 0 + } + } + tmpHash, err = poseidon.Hash([8]uint64{ + elementsToHash[4], + elementsToHash[5], + elementsToHash[6], + elementsToHash[7], + elementsToHash[8], + elementsToHash[9], + elementsToHash[10], + elementsToHash[11], + }, [4]uint64{ + elementsToHash[0], + elementsToHash[1], + elementsToHash[2], + elementsToHash[3], + }) + if err != nil { + return nil, err + } + } + return tmpHash[:], nil +} + +// KeyCodeLength returns the key of code length leaf: +// hk0: H([0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0]) +// key: H([ethAddr[0:4], ethAddr[4:8], ethAddr[8:12], ethAddr[12:16], ethAddr[16:20], 0, 4, 0], [hk0[0], hk0[1], hk0[2], hk0[3]] +func KeyCodeLength(ethAddr common.Address) ([]byte, error) { + capIn, err := defaultCapIn() + if err != nil { + return nil, err + } + + return keyEthAddr(ethAddr, LeafTypeSCLength, capIn) +} diff --git a/merkletree/leaf.go b/merkletree/leaf.go new file mode 100644 index 00000000..5321f69a --- /dev/null +++ b/merkletree/leaf.go @@ -0,0 +1,17 @@ +package merkletree + +// leafType specifies type of the leaf +type leafType uint8 + +const ( + // LeafTypeBalance specifies that leaf stores Balance + LeafTypeBalance leafType = 0 + // LeafTypeNonce specifies that leaf stores Nonce + LeafTypeNonce leafType = 1 + // LeafTypeCode specifies that leaf stores Code + LeafTypeCode leafType = 2 + // LeafTypeStorage specifies that leaf stores Storage Value + LeafTypeStorage leafType = 3 + // LeafTypeSCLength specifies that leaf stores Storage Value + LeafTypeSCLength leafType = 4 +) diff --git a/merkletree/split.go b/merkletree/split.go new file mode 100644 index 00000000..63fcae33 --- /dev/null +++ b/merkletree/split.go @@ -0,0 +1,98 @@ +package merkletree + +import ( + "encoding/binary" + "fmt" + "math/big" + "strings" + + "github.com/0xPolygon/cdk/hex" +) + +// maxBigIntLen is 256 bits (32 bytes) +const maxBigIntLen = 32 + +// wordLength is the number of bits of each ff limb +const wordLength = 64 + +// scalar2fea splits a *big.Int into array of 32bit uint64 values. +func scalar2fea(value *big.Int) []uint64 { + val := make([]uint64, 8) //nolint:gomnd + mask, _ := new(big.Int).SetString("FFFFFFFF", 16) //nolint:gomnd + val[0] = new(big.Int).And(value, mask).Uint64() + val[1] = new(big.Int).And(new(big.Int).Rsh(value, 32), mask).Uint64() //nolint:gomnd + val[2] = new(big.Int).And(new(big.Int).Rsh(value, 64), mask).Uint64() //nolint:gomnd + val[3] = new(big.Int).And(new(big.Int).Rsh(value, 96), mask).Uint64() //nolint:gomnd + val[4] = new(big.Int).And(new(big.Int).Rsh(value, 128), mask).Uint64() //nolint:gomnd + val[5] = new(big.Int).And(new(big.Int).Rsh(value, 160), mask).Uint64() //nolint:gomnd + val[6] = new(big.Int).And(new(big.Int).Rsh(value, 192), mask).Uint64() //nolint:gomnd + val[7] = new(big.Int).And(new(big.Int).Rsh(value, 224), mask).Uint64() //nolint:gomnd + return val +} + +// h4ToScalar converts array of 4 uint64 into a unique 256 bits scalar. +func h4ToScalar(h4 []uint64) *big.Int { + if len(h4) == 0 { + return new(big.Int) + } + result := new(big.Int).SetUint64(h4[0]) + + for i := 1; i < 4; i++ { + b2 := new(big.Int).SetUint64(h4[i]) + b2.Lsh(b2, uint(wordLength*i)) + result = result.Add(result, b2) + } + + return result +} + +// H4ToString converts array of 4 Scalars of 64 bits into an hex string. +func H4ToString(h4 []uint64) string { + sc := h4ToScalar(h4) + + return fmt.Sprintf("0x%064s", hex.EncodeToString(sc.Bytes())) +} + +// StringToh4 converts an hex string into array of 4 Scalars of 64 bits. +func StringToh4(str string) ([]uint64, error) { + if strings.HasPrefix(str, "0x") { // nolint + str = str[2:] + } + + bi, ok := new(big.Int).SetString(str, hex.Base) + if !ok { + return nil, fmt.Errorf("Could not convert %q into big int", str) + } + + return scalarToh4(bi), nil +} + +// scalarToh4 converts a *big.Int into an array of 4 uint64 +func scalarToh4(s *big.Int) []uint64 { + b := ScalarToFilledByteSlice(s) + + r := make([]uint64, 4) //nolint:gomnd + + f, _ := hex.DecodeHex("0xFFFFFFFFFFFFFFFF") + fbe := binary.BigEndian.Uint64(f) + + r[3] = binary.BigEndian.Uint64(b[0:8]) & fbe + r[2] = binary.BigEndian.Uint64(b[8:16]) & fbe + r[1] = binary.BigEndian.Uint64(b[16:24]) & fbe + r[0] = binary.BigEndian.Uint64(b[24:]) & fbe + + return r +} + +// ScalarToFilledByteSlice converts a *big.Int into an array of maxBigIntLen +// bytes. +func ScalarToFilledByteSlice(s *big.Int) []byte { + buf := make([]byte, maxBigIntLen) + return s.FillBytes(buf) +} + +// h4ToFilledByteSlice converts an array of 4 uint64 into an array of +// maxBigIntLen bytes. +func h4ToFilledByteSlice(h4 []uint64) []byte { + return ScalarToFilledByteSlice(h4ToScalar(h4)) +} diff --git a/proto/include/google/protobuf/empty.proto b/proto/include/google/protobuf/empty.proto new file mode 100644 index 00000000..22274621 --- /dev/null +++ b/proto/include/google/protobuf/empty.proto @@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "google.golang.org/protobuf/types/known/emptypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +message Empty {} diff --git a/proto/src/proto/aggregator/v1/aggregator.proto b/proto/src/proto/aggregator/v1/aggregator.proto new file mode 100644 index 00000000..3d5480e4 --- /dev/null +++ b/proto/src/proto/aggregator/v1/aggregator.proto @@ -0,0 +1,330 @@ +syntax = "proto3"; + +package aggregator.v1; + +option go_package = ""github.com/0xPolygon/cdk/proverclient/prover"; + +message Version { + string v0_0_1 = 1; +} + +// timestamps are represented in unix time in seconds + +/** + * Define all methods implementes by the gRPC + * Channel: prover receives aggregator messages and returns prover messages with the same id + */ +service AggregatorService { + rpc Channel(stream ProverMessage) returns (stream AggregatorMessage) {} +} + +message AggregatorMessage +{ + string id = 1; + oneof request + { + GetStatusRequest get_status_request = 2; + GenBatchProofRequest gen_batch_proof_request = 3; + GenAggregatedProofRequest gen_aggregated_proof_request = 4; + GenFinalProofRequest gen_final_proof_request = 5; + CancelRequest cancel_request = 6; + GetProofRequest get_proof_request = 7; + GenStatelessBatchProofRequest gen_stateless_batch_proof_request = 8; + } +} + +message ProverMessage +{ + string id = 1; + oneof response + { + GetStatusResponse get_status_response = 2; + GenBatchProofResponse gen_batch_proof_response = 3; + GenAggregatedProofResponse gen_aggregated_proof_response = 4; + GenFinalProofResponse gen_final_proof_response = 5; + CancelResponse cancel_response = 6; + GetProofResponse get_proof_response = 7; + } +} + +/////////////////// +// Request messages +/////////////////// + +/** + * @dev GetStatusRequest + */ +message GetStatusRequest {} + +/** + * @dev GenBatchProofRequest + * @param {input} - input prover + */ +message GenBatchProofRequest { + InputProver input = 1; +} + +message GenStatelessBatchProofRequest { + StatelessInputProver input = 1; +} + +/** + * @dev GenAggregatedProofRequest + * @param {recursive_proof_1} - proof json of the first batch to aggregate + * @param {recursive_proof_2} - proof json of the second batch to aggregate + */ +message GenAggregatedProofRequest { + string recursive_proof_1 = 1; + string recursive_proof_2 = 2; +} + +/** + * @dev GenFinalProofRequest + * @param {recursive_proof} - proof json of the batch or aggregated proof to finalise + * @param {aggregator_addr} - address of the aggregator + */ +message GenFinalProofRequest { + string recursive_proof = 1; + string aggregator_addr = 2; +} + +/** + * @dev CancelRequest + * @param {id} - identifier of the proof request to cancel + */ + message CancelRequest { + string id = 1; +} + +/** + * @dev Request GetProof + * @param {id} - proof identifier of the proof request + * @param {timeout} - time to wait until the service responds + */ +message GetProofRequest { + string id = 1; + uint64 timeout = 2; +} + +///////////////////// +// Responses messages +///////////////////// + +/** + * @dev Response GetStatus + * @param {status} - server status + * - BOOTING: being ready to compute proofs + * - COMPUTING: busy computing a proof + * - IDLE: waiting for a proof to compute + * - HALT: stop + * @param {last_computed_request_id} - last proof identifier that has been computed + * @param {last_computed_end_time} - last proof timestamp when it was finished + * @param {current_computing_request_id} - id of the proof that is being computed + * @param {current_computing_start_time} - timestamp when the proof that is being computed started + * @param {version_proto} - .proto verion + * @param {version_server} - server version + * @param {pending_request_queue_ids} - list of identifierss of proof requests that are in the pending queue + * @param {prover_name} - id of this prover server, normally specified via config.json, or UNSPECIFIED otherwise; it does not change if prover reboots + * @param {prover_id} - id of this prover instance or reboot; it changes if prover reboots; it is a UUID, automatically generated during the initialization + * @param {number_of_cores} - number of cores in the system where the prover is running + * @param {total_memory} - total memory in the system where the prover is running + * @param {free_memory} - free memory in the system where the prover is running + */ +message GetStatusResponse { + enum Status { + STATUS_UNSPECIFIED = 0; + STATUS_BOOTING = 1; + STATUS_COMPUTING = 2; + STATUS_IDLE = 3; + STATUS_HALT = 4; + } + Status status = 1; + string last_computed_request_id = 2; + uint64 last_computed_end_time = 3; + string current_computing_request_id = 4; + uint64 current_computing_start_time = 5; + string version_proto = 6; + string version_server = 7; + repeated string pending_request_queue_ids = 8; + string prover_name = 9; + string prover_id = 10; + uint64 number_of_cores = 11; + uint64 total_memory = 12; + uint64 free_memory = 13; + uint64 fork_id = 14; +} + +/** + * @dev Result + * - OK: succesfully completed + * - ERROR: request is not correct, i.e. input data is wrong + * - INTERNAL_ERROR: internal server error when delivering the response + */ +enum Result { + RESULT_UNSPECIFIED = 0; + RESULT_OK = 1; + RESULT_ERROR = 2; + RESULT_INTERNAL_ERROR = 3; +} + +/** + * @dev GenBatchProofResponse + * @param {id} - proof identifier, to be used in GetProofRequest() + * @param {result} - request result + */ +message GenBatchProofResponse { + string id = 1; + Result result = 2; +} + +/** + * @dev GenAggregatedProofResponse + * @param {id} - proof identifier, to be used in GetProofRequest() + * @param {result} - request result + */ +message GenAggregatedProofResponse { + string id = 1; + Result result = 2; +} + +/** + * @dev Response GenFinalProof + * @param {id} - proof identifier, to be used in GetProofRequest() + * @param {result} - request result + */ +message GenFinalProofResponse { + string id = 1; + Result result = 2; +} + +/** + * @dev CancelResponse + * @param {result} - request result + */ +message CancelResponse { + Result result = 1; +} + +/** + * @dev GetProofResponse + * @param {id} - proof identifier + * @param {final_proof} - groth16 proof + public circuit inputs + * @param {recursive_proof} - recursive proof json + * @param {result} - proof result + * - COMPLETED_OK: proof has been computed successfully and it is valid + * - ERROR: request error + * - COMPLETED_ERROR: proof has been computed successfully and it is not valid + * - PENDING: proof is being computed + * - INTERNAL_ERROR: server error during proof computation + * - CANCEL: proof has been cancelled + * @param {result_string} - extends result information + */ +message GetProofResponse { + enum Result { + RESULT_UNSPECIFIED = 0; + RESULT_COMPLETED_OK = 1; + RESULT_ERROR = 2; + RESULT_COMPLETED_ERROR = 3; + RESULT_PENDING = 4; + RESULT_INTERNAL_ERROR = 5; + RESULT_CANCEL = 6; + } + string id = 1; + oneof proof { + FinalProof final_proof = 2; + string recursive_proof =3; + } + Result result = 4; + string result_string = 5; +} + +/* + * @dev FinalProof + * @param {proof} - groth16 proof + * @param {public} - public circuit inputs +*/ +message FinalProof { + string proof = 1; + PublicInputsExtended public = 2; +} + +/* + * @dev PublicInputs + * @param {old_state_root} + * @param {old_acc_input_hash} + * @param {old_batch_num} + * @param {chain_id} + * @param {batch_l2_data} + * @param {global_exit_root} + * @param {sequencer_addr} + * @param {aggregator_addr} + */ +message PublicInputs { + bytes old_state_root = 1; + bytes old_acc_input_hash = 2; + uint64 old_batch_num = 3; + uint64 chain_id = 4; + uint64 fork_id = 5; + bytes batch_l2_data = 6; + bytes l1_info_root = 7; + uint64 timestamp_limit = 8; + string sequencer_addr = 9; + bytes forced_blockhash_l1 = 10; + string aggregator_addr = 12; + map l1_info_tree_data = 16; +} + +message StatelessPublicInputs { + bytes witness = 1; + bytes old_acc_input_hash = 2; + uint64 old_batch_num = 3; + uint64 chain_id = 4; + uint64 fork_id = 5; + bytes batch_l2_data = 6; + bytes l1_info_root = 7; + uint64 timestamp_limit = 8; + string sequencer_addr = 9; + bytes forced_blockhash_l1 = 10; + string aggregator_addr = 11; + map l1_info_tree_data = 12; +} + +// l1InfoTree leaf values +message L1Data { + bytes global_exit_root = 1; + bytes blockhash_l1 = 2; + uint32 min_timestamp = 3; + repeated bytes smt_proof = 4; +} + +/** + * @dev InputProver + * @param {public_inputs} - public inputs + * @param {db} - database containing all key-values in smt matching the old state root + * @param {contracts_bytecode} - key is the hash(contractBytecode), value is the bytecode itself + */ +message InputProver { + PublicInputs public_inputs = 1; + map db = 4; // For debug/testing purpposes only. Don't fill this on production + map contracts_bytecode = 5; // For debug/testing purpposes only. Don't fill this on production +} + +message StatelessInputProver { + StatelessPublicInputs public_inputs = 1; +} + +/** + * @dev PublicInputsExtended + * @param {public_inputs} - public inputs + * @param {new_state_root} - final state root. Used as a sanity check. + * @param {new_acc_input_hash} - final accumulate input hash. Used as a sanity check. + * @param {new_local_exit_root} - new local exit root. Used as a sanity check. + * @param {new_batch_num} - final num batch. Used as a sanity check. + */ +message PublicInputsExtended { + PublicInputs public_inputs = 1; + bytes new_state_root = 2; + bytes new_acc_input_hash = 3; + bytes new_local_exit_root = 4; + uint64 new_batch_num = 5; +} diff --git a/proto/src/proto/datastream/v1/datastream.proto b/proto/src/proto/datastream/v1/datastream.proto new file mode 100644 index 00000000..a93f0ab3 --- /dev/null +++ b/proto/src/proto/datastream/v1/datastream.proto @@ -0,0 +1,90 @@ +syntax = "proto3"; + +package datastream.v1; + +option go_package = "github.com/0xPolygon/cdk"state/datastream"; + +message BatchStart { + uint64 number = 1; + BatchType type = 2; + uint64 fork_id = 3; + uint64 chain_id = 4; + Debug debug = 5; +} + +message BatchEnd { + uint64 number = 1; + bytes local_exit_root = 2; + bytes state_root = 3; + Debug debug = 4; +} + +message L2Block { + uint64 number = 1; + uint64 batch_number = 2; + uint64 timestamp = 3; + uint32 delta_timestamp = 4; + uint64 min_timestamp = 5; + bytes l1_blockhash = 6; + uint32 l1_infotree_index = 7; + bytes hash = 8; + bytes state_root = 9; + bytes global_exit_root = 10; + bytes coinbase = 11; + uint64 block_gas_limit = 12; + bytes block_info_root = 13; + Debug debug = 14; +} + +message Transaction { + uint64 l2block_number = 1; + uint64 index = 2; + bool is_valid = 3; + bytes encoded = 4; + uint32 effective_gas_price_percentage = 5; + bytes im_state_root = 6; + Debug debug = 7; +} + +message UpdateGER { + uint64 batch_number = 1; + uint64 timestamp = 2; + bytes global_exit_root = 3; + bytes coinbase = 4; + uint64 fork_id = 5; + uint64 chain_id = 6; + bytes state_root = 7; + Debug debug = 8; +} + +message BookMark { + BookmarkType type = 1; + uint64 value = 2; +} + +message Debug { + string message = 1; +} + +enum BookmarkType { + BOOKMARK_TYPE_UNSPECIFIED = 0; + BOOKMARK_TYPE_BATCH = 1; + BOOKMARK_TYPE_L2_BLOCK = 2; +} + +enum EntryType { + ENTRY_TYPE_UNSPECIFIED = 0; + ENTRY_TYPE_BATCH_START = 1; + ENTRY_TYPE_L2_BLOCK = 2; + ENTRY_TYPE_TRANSACTION = 3; + ENTRY_TYPE_BATCH_END = 4; + ENTRY_TYPE_UPDATE_GER = 5; +} + +enum BatchType { + BATCH_TYPE_UNSPECIFIED = 0; + BATCH_TYPE_REGULAR = 1; + BATCH_TYPE_FORCED = 2; + BATCH_TYPE_INJECTED = 3; + BATCH_TYPE_INVALID = 4; +} diff --git a/reorgdetector/reorgdetector.go b/reorgdetector/reorgdetector.go new file mode 100644 index 00000000..06d15e85 --- /dev/null +++ b/reorgdetector/reorgdetector.go @@ -0,0 +1,246 @@ +package reorgdetector + +import ( + "context" + "errors" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" +) + +// TODO: consider the case where blocks can disappear, current implementation assumes that if there is a reorg, +// the client will have at least as many blocks as it had before the reorg, however this may not be the case for L2 + +const ( + waitPeriodBlockRemover = time.Second * 20 + waitPeriodBlockAdder = time.Second * 2 // should be smaller than block time of the tracked chain +) + +var ( + ErrNotSubscribed = errors.New("id not found in subscriptions") + ErrInvalidBlockHash = errors.New("the block hash does not match with the expected block hash") +) + +type Subscription struct { + FirstReorgedBlock chan uint64 + ReorgProcessed chan bool + mu sync.Mutex + pendingReorgsToBeProcessed *sync.WaitGroup +} + +type ReorgDetector struct { + ethClient *ethclient.Client + mu sync.Mutex + unfinalisedBlocks map[uint64]common.Hash + trackedBlocks map[string]map[uint64]common.Hash // TODO: needs persistance! needs to be able to iterate in order! + // the channel is used to notify first invalid block + subscriptions map[string]*Subscription +} + +func New(ctx context.Context) (*ReorgDetector, error) { + r := &ReorgDetector{} + lastFinalisedBlock, err := r.ethClient.HeaderByNumber(ctx, big.NewInt(int64(rpc.FinalizedBlockNumber))) + if err != nil { + return nil, err + } + r.unfinalisedBlocks[lastFinalisedBlock.Number.Uint64()] = lastFinalisedBlock.Hash() + err = r.cleanStoredSubsBeforeStart(ctx, lastFinalisedBlock.Number.Uint64()) + if err != nil { + return nil, err + } + return r, nil +} + +func (r *ReorgDetector) Start(ctx context.Context) { + var lastFinalisedBlock uint64 + for lastFinalisedBlock = range r.unfinalisedBlocks { + } + go r.removeFinalisedBlocks(ctx) + go r.addUnfinalisedBlocks(ctx, lastFinalisedBlock+1) +} + +func (r *ReorgDetector) Subscribe(id string) *Subscription { + if sub, ok := r.subscriptions[id]; ok { + return sub + } + sub := &Subscription{ + FirstReorgedBlock: make(chan uint64), + ReorgProcessed: make(chan bool), + } + r.subscriptions[id] = sub + r.trackedBlocks[id] = make(map[uint64]common.Hash) + return sub +} + +func (r *ReorgDetector) AddBlockToTrack(ctx context.Context, id string, blockNum uint64, blockHash common.Hash) error { + if sub, ok := r.subscriptions[id]; !ok { + return ErrNotSubscribed + } else { + // In case there are reorgs being processed, wait + // Note that this also makes any addition to trackedBlocks[id] safe + sub.mu.Lock() + defer sub.mu.Unlock() + sub.pendingReorgsToBeProcessed.Wait() + } + if actualHash, ok := r.unfinalisedBlocks[blockNum]; ok { + if actualHash == blockHash { + r.trackedBlocks[id][blockNum] = blockHash + return nil + } else { + return ErrInvalidBlockHash + } + } else { + // block not found in local storage + lastFinalisedBlock, err := r.ethClient.HeaderByNumber(ctx, big.NewInt(int64(rpc.FinalizedBlockNumber))) + if err != nil { + return err + } + if lastFinalisedBlock.Number.Uint64() >= blockNum { + // block already finalised, no need to track + return nil + } else { + // ReorgDetector has not added the requested block yet, adding it + r.trackedBlocks[id][blockNum] = blockHash + return nil + } + } +} + +func (r *ReorgDetector) cleanStoredSubsBeforeStart(ctx context.Context, latestFinalisedBlock uint64) error { + for id := range r.trackedBlocks { + sub := &Subscription{ + FirstReorgedBlock: make(chan uint64), + ReorgProcessed: make(chan bool), + } + r.subscriptions[id] = sub + if err := r.cleanStoredSubBeforeStart(ctx, id, latestFinalisedBlock); err != nil { + return err + } + } + return nil +} + +func (r *ReorgDetector) cleanStoredSubBeforeStart(ctx context.Context, id string, latestFinalisedBlock uint64) error { + blocks := r.trackedBlocks[id] + var lastTrackedBlock uint64 // TODO: get the greatest block num tracked + for expectedBlockNum, expectedBlockHash := range blocks { // should iterate in order + actualBlock, err := r.ethClient.HeaderByNumber(ctx, big.NewInt(int64(expectedBlockNum))) + if err != nil { + return err + } + if actualBlock.Hash() != expectedBlockHash { + r.subscriptions[id].pendingReorgsToBeProcessed.Add(1) + go r.notifyReorgToSubscription(id, expectedBlockNum, lastTrackedBlock) + return nil + } else if expectedBlockNum < latestFinalisedBlock { + delete(blocks, expectedBlockNum) + } + } + return nil +} + +func (r *ReorgDetector) removeFinalisedBlocks(ctx context.Context) { + for { + lastFinalisedBlock, err := r.ethClient.HeaderByNumber(ctx, big.NewInt(int64(rpc.FinalizedBlockNumber))) + if err != nil { + // TODO: handle error + return + } + for i := lastFinalisedBlock.Number.Uint64(); i >= 0; i-- { + if _, ok := r.unfinalisedBlocks[i]; ok { + r.mu.Lock() + delete(r.unfinalisedBlocks, i) + r.mu.Unlock() + } else { + break + } + for id, blocks := range r.trackedBlocks { + r.subscriptions[id].mu.Lock() + delete(blocks, i) + r.subscriptions[id].mu.Unlock() + } + } + time.Sleep(waitPeriodBlockRemover) + } +} + +func (r *ReorgDetector) addUnfinalisedBlocks(ctx context.Context, initBlock uint64) { + var ( + firstBlockReorged uint64 + err error + ) + currentBlock := initBlock + lastBlockFromClient := currentBlock - 1 + for { + for currentBlock > lastBlockFromClient { + lastBlockFromClient, err = r.ethClient.BlockNumber(ctx) + if err != nil { + // TODO: handle error + return + } + time.Sleep(waitPeriodBlockAdder) + } + block, err := r.ethClient.HeaderByNumber(ctx, big.NewInt(int64(currentBlock))) + if err != nil { + // TODO: handle error + return + } + prevBlockHash, ok := r.unfinalisedBlocks[currentBlock-1] + if !ok || block.ParentHash == prevBlockHash { + // previous block is correct or there is no previous block, add current block + r.mu.Lock() + r.unfinalisedBlocks[currentBlock] = block.Hash() + r.mu.Unlock() + if firstBlockReorged > 0 { + r.notifyReorg(currentBlock, firstBlockReorged) + } + currentBlock++ + firstBlockReorged = 0 + } else { + // previous block is reorged: + // 1. add a pending reorg to be processed for all the subscribers (so they don't add more blocks) + // 2. remove block + for _, sub := range r.subscriptions { + sub.mu.Lock() + sub.pendingReorgsToBeProcessed.Add(1) + sub.mu.Unlock() + } + r.mu.Lock() + delete(r.unfinalisedBlocks, currentBlock-1) + r.mu.Unlock() + currentBlock-- + if firstBlockReorged == 0 { + firstBlockReorged = currentBlock + } + } + } +} + +func (r *ReorgDetector) notifyReorg(fromBlock, toBlock uint64) { + for id := range r.subscriptions { + go r.notifyReorgToSubscription(id, fromBlock, toBlock) + } +} + +func (r *ReorgDetector) notifyReorgToSubscription(id string, fromBlock, toBlock uint64) { + blocks := r.trackedBlocks[id] + sub := r.subscriptions[id] + var found bool + for i := fromBlock; i <= toBlock; i++ { + if _, ok := blocks[i]; ok { + if !found { + // notify about the first reorged block that was tracked + // and wait for the receiver to process + found = true + sub.FirstReorgedBlock <- i + <-sub.ReorgProcessed + } + delete(blocks, i) + } + } + sub.pendingReorgsToBeProcessed.Done() +} diff --git a/sequencesender/config.go b/sequencesender/config.go new file mode 100644 index 00000000..58fa01d8 --- /dev/null +++ b/sequencesender/config.go @@ -0,0 +1,77 @@ +package sequencesender + +import ( + "github.com/0xPolygon/cdk/config/types" + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygonHermez/zkevm-ethtx-manager/ethtxmanager" + "github.com/ethereum/go-ethereum/common" +) + +// Config represents the configuration of a sequence sender +type Config struct { + // IsValidiumMode has the value true if the sequence sender is running in validium mode. + IsValidiumMode bool `mapstructure:"IsValidiumMode"` + // WaitPeriodSendSequence is the time the sequencer waits until + // trying to send a sequence to L1 + WaitPeriodSendSequence types.Duration `mapstructure:"WaitPeriodSendSequence"` + // LastBatchVirtualizationTimeMaxWaitPeriod is time since sequences should be sent + LastBatchVirtualizationTimeMaxWaitPeriod types.Duration `mapstructure:"LastBatchVirtualizationTimeMaxWaitPeriod"` + // L1BlockTimestampMargin is the time difference (margin) that must exists between last L1 block and last L2 block in the sequence before + // to send the sequence to L1. If the difference is lower than this value then sequencesender will wait until the difference is equal or greater + L1BlockTimestampMargin types.Duration `mapstructure:"L1BlockTimestampMargin"` + // MaxTxSizeForL1 is the maximum size a single transaction can have. This field has + // non-trivial consequences: larger transactions than 128KB are significantly harder and + // more expensive to propagate; larger transactions also take more resources + // to validate whether they fit into the pool or not. + MaxTxSizeForL1 uint64 `mapstructure:"MaxTxSizeForL1"` + // SenderAddress defines which private key the eth tx manager needs to use + // to sign the L1 txs + SenderAddress common.Address + // L2Coinbase defines which address is going to receive the fees + L2Coinbase common.Address `mapstructure:"L2Coinbase"` + // PrivateKey defines all the key store files that are going + // to be read in order to provide the private keys to sign the L1 txs + PrivateKey types.KeystoreFileConfig `mapstructure:"PrivateKey"` + // Batch number where there is a forkid change (fork upgrade) + ForkUpgradeBatchNumber uint64 + // GasOffset is the amount of gas to be added to the gas estimation in order + // to provide an amount that is higher than the estimated one. This is used + // to avoid the TX getting reverted in case something has changed in the network + // state after the estimation which can cause the TX to require more gas to be + // executed. + // + // ex: + // gas estimation: 1000 + // gas offset: 100 + // final gas: 1100 + GasOffset uint64 `mapstructure:"GasOffset"` + + // SequencesTxFileName is the file name to store sequences sent to L1 + SequencesTxFileName string + + // WaitPeriodPurgeTxFile is the time to wait before purging from file the finished sent L1 tx + WaitPeriodPurgeTxFile types.Duration `mapstructure:"WaitPeriodPurgeTxFile"` + + // MaxPendingTx is the maximum number of pending transactions (those that are not in a final state) + MaxPendingTx uint64 + + // StreamClient is the config for the stream client + StreamClient StreamClientCfg `mapstructure:"StreamClient"` + + // EthTxManager is the config for the ethtxmanager + EthTxManager ethtxmanager.Config `mapstructure:"EthTxManager"` + + // Log is the log configuration + Log log.Config `mapstructure:"Log"` + + // MaxBatchesForL1 is the maximum amount of batches to be sequenced in a single L1 tx + MaxBatchesForL1 uint64 `mapstructure:"MaxBatchesForL1"` +} + +// StreamClientCfg contains the data streamer's configuration properties +type StreamClientCfg struct { + // Datastream server to connect + Server string `mapstructure:"Server"` + // Log is the log configuration + Log log.Config `mapstructure:"Log"` +} diff --git a/sequencesender/sequencesender.go b/sequencesender/sequencesender.go new file mode 100644 index 00000000..f8cd4b5e --- /dev/null +++ b/sequencesender/sequencesender.go @@ -0,0 +1,1184 @@ +package sequencesender + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "math" + "math/big" + "os" + "strings" + "sync" + "time" + + "github.com/0xPolygon/cdk/aggregator/ethmantypes" + "github.com/0xPolygon/cdk/dataavailability" + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/state" + "github.com/0xPolygon/cdk/state/datastream" + "github.com/0xPolygonHermez/zkevm-data-streamer/datastreamer" + "github.com/0xPolygonHermez/zkevm-ethtx-manager/ethtxmanager" + ethtxlog "github.com/0xPolygonHermez/zkevm-ethtx-manager/log" + "github.com/ethereum/go-ethereum/common" + "google.golang.org/protobuf/proto" +) + +var ( + // ErrOversizedData when transaction input data is greater than a limit (DOS protection) + ErrOversizedData = errors.New("oversized data") +) + +// SequenceSender represents a sequence sender +type SequenceSender struct { + cfg Config + ethTxManager *ethtxmanager.Client + etherman *etherman.Client + currentNonce uint64 + latestVirtualBatch uint64 // Latest virtualized batch obtained from L1 + latestVirtualTime time.Time // Latest virtual batch timestamp + latestSentToL1Batch uint64 // Latest batch sent to L1 + wipBatch uint64 // Work in progress batch + sequenceList []uint64 // Sequence of batch number to be send to L1 + sequenceData map[uint64]*sequenceData // All the batch data indexed by batch number + mutexSequence sync.Mutex // Mutex to access sequenceData and sequenceList + ethTransactions map[common.Hash]*ethTxData // All the eth tx sent to L1 indexed by hash + ethTxData map[common.Hash][]byte // Tx data send to or received from L1 + mutexEthTx sync.Mutex // Mutex to access ethTransactions + sequencesTxFile *os.File // Persistence of sent transactions + validStream bool // Not valid while receiving data before the desired batch + fromStreamBatch uint64 // Initial batch to connect to the streaming + latestStreamBatch uint64 // Latest batch received by the streaming + seqSendingStopped bool // If there is a critical error + streamClient *datastreamer.StreamClient + da *dataavailability.DataAvailability +} + +type sequenceData struct { + batchClosed bool + batch *ethmantypes.Sequence + batchRaw *state.BatchRawV2 +} + +type ethTxData struct { + Nonce uint64 `json:"nonce"` + Status string `json:"status"` + SentL1Timestamp time.Time `json:"sentL1Timestamp"` + StatusTimestamp time.Time `json:"statusTimestamp"` + FromBatch uint64 `json:"fromBatch"` + ToBatch uint64 `json:"toBatch"` + MinedAtBlock big.Int `json:"minedAtBlock"` + OnMonitor bool `json:"onMonitor"` + To common.Address `json:"to"` + StateHistory []string `json:"stateHistory"` + Txs map[common.Hash]ethTxAdditionalData `json:"txs"` +} + +type ethTxAdditionalData struct { + GasPrice *big.Int `json:"gasPrice,omitempty"` + RevertMessage string `json:"revertMessage,omitempty"` +} + +// New inits sequence sender +func New(cfg Config, etherman *etherman.Client, da *dataavailability.DataAvailability) (*SequenceSender, error) { + // Create sequencesender + s := SequenceSender{ + cfg: cfg, + etherman: etherman, + ethTransactions: make(map[common.Hash]*ethTxData), + ethTxData: make(map[common.Hash][]byte), + sequenceData: make(map[uint64]*sequenceData), + validStream: false, + latestStreamBatch: 0, + seqSendingStopped: false, + da: da, + } + + // Restore pending sent sequences + err := s.loadSentSequencesTransactions() + if err != nil { + log.Fatalf("[SeqSender] error restoring sent sequences from file", err) + return nil, err + } + + // Create ethtxmanager client + cfg.EthTxManager.Log = ethtxlog.Config{ + Environment: ethtxlog.LogEnvironment(cfg.Log.Environment), + Level: cfg.Log.Level, + Outputs: cfg.Log.Outputs, + } + s.ethTxManager, err = ethtxmanager.New(cfg.EthTxManager) + if err != nil { + log.Fatalf("[SeqSender] error creating ethtxmanager client: %v", err) + return nil, err + } + + // Create datastream client + s.streamClient, err = datastreamer.NewClient(s.cfg.StreamClient.Server, 1) + if err != nil { + log.Fatalf("[SeqSender] failed to create stream client, error: %v", err) + } else { + log.Infof("[SeqSender] new stream client") + } + // Set func to handle the streaming + s.streamClient.SetProcessEntryFunc(s.handleReceivedDataStream) + + return &s, nil +} + +// Start starts the sequence sender +func (s *SequenceSender) Start(ctx context.Context) { + // Start ethtxmanager client + go s.ethTxManager.Start() + + // Get current nonce + var err error + s.currentNonce, err = s.etherman.CurrentNonce(ctx, s.cfg.L2Coinbase) + if err != nil { + log.Fatalf("[SeqSender] failed to get current nonce from %v, error: %v", s.cfg.L2Coinbase, err) + } else { + log.Infof("[SeqSender] current nonce for %v is %d", s.cfg.L2Coinbase, s.currentNonce) + } + + // Get latest virtual state batch from L1 + err = s.updateLatestVirtualBatch() + if err != nil { + log.Fatalf("[SeqSender] error getting latest sequenced batch, error: %v", err) + } + + // Sync all monitored sent L1 tx + err = s.syncAllEthTxResults(ctx) + if err != nil { + log.Fatalf("[SeqSender] failed to sync monitored tx results, error: %v", err) + } + + // Start datastream client + err = s.streamClient.Start() + if err != nil { + log.Fatalf("[SeqSender] failed to start stream client, error: %v", err) + } + + // Set starting point of the streaming + s.fromStreamBatch = s.latestVirtualBatch + + bookmark := &datastream.BookMark{ + Type: datastream.BookmarkType_BOOKMARK_TYPE_BATCH, + Value: s.fromStreamBatch, + } + + marshalledBookmark, err := proto.Marshal(bookmark) + if err != nil { + log.Fatalf("[SeqSender] failed to marshal bookmark, error: %v", err) + } + + log.Infof("[SeqSender] stream client from bookmark %v", bookmark) + + // Current batch to sequence + s.wipBatch = s.latestVirtualBatch + 1 + s.latestSentToL1Batch = s.latestVirtualBatch + + // Start sequence sending + go s.sequenceSending(ctx) + + // Start receiving the streaming + err = s.streamClient.ExecCommandStartBookmark(marshalledBookmark) + if err != nil { + log.Fatalf("[SeqSender] failed to connect to the streaming: %v", err) + } +} + +// sequenceSending starts loop to check if there are sequences to send and sends them if it's convenient +func (s *SequenceSender) sequenceSending(ctx context.Context) { + for { + s.tryToSendSequence(ctx) + time.Sleep(s.cfg.WaitPeriodSendSequence.Duration) + } +} + +// purgeSequences purges batches from memory structures +func (s *SequenceSender) purgeSequences() { + // If sequence sending is stopped, do not purge + if s.seqSendingStopped { + return + } + + // Purge the information of batches that are already virtualized + s.mutexSequence.Lock() + truncateUntil := 0 + toPurge := make([]uint64, 0) + for i := 0; i < len(s.sequenceList); i++ { + batchNumber := s.sequenceList[i] + if batchNumber <= s.latestVirtualBatch { + truncateUntil = i + 1 + toPurge = append(toPurge, batchNumber) + } + } + + if len(toPurge) > 0 { + s.sequenceList = s.sequenceList[truncateUntil:] + + var firstPurged uint64 + var lastPurged uint64 + for i := 0; i < len(toPurge); i++ { + if i == 0 { + firstPurged = toPurge[i] + } + if i == len(toPurge)-1 { + lastPurged = toPurge[i] + } + delete(s.sequenceData, toPurge[i]) + } + log.Infof("[SeqSender] batches purged count: %d, fromBatch: %d, toBatch: %d", len(toPurge), firstPurged, lastPurged) + } + s.mutexSequence.Unlock() +} + +// purgeEthTx purges transactions from memory structures +func (s *SequenceSender) purgeEthTx(ctx context.Context) { + // If sequence sending is stopped, do not purge + if s.seqSendingStopped { + return + } + + // Purge old transactions that are finalized + s.mutexEthTx.Lock() + timePurge := time.Now().Add(-s.cfg.WaitPeriodPurgeTxFile.Duration) + toPurge := make([]common.Hash, 0) + for hash, data := range s.ethTransactions { + if !data.StatusTimestamp.Before(timePurge) { + continue + } + + if !data.OnMonitor || data.Status == ethtxmanager.MonitoredTxStatusFinalized.String() { + toPurge = append(toPurge, hash) + + // Remove from tx monitor + if data.OnMonitor { + err := s.ethTxManager.Remove(ctx, hash) + if err != nil { + log.Warnf("[SeqSender] error removing monitor tx %v from ethtxmanager: %v", hash, err) + } else { + log.Infof("[SeqSender] removed monitor tx %v from ethtxmanager", hash) + } + } + } + } + + if len(toPurge) > 0 { + var firstPurged uint64 = math.MaxUint64 + var lastPurged uint64 + for i := 0; i < len(toPurge); i++ { + if s.ethTransactions[toPurge[i]].Nonce < firstPurged { + firstPurged = s.ethTransactions[toPurge[i]].Nonce + } + if s.ethTransactions[toPurge[i]].Nonce > lastPurged { + lastPurged = s.ethTransactions[toPurge[i]].Nonce + } + delete(s.ethTransactions, toPurge[i]) + delete(s.ethTxData, toPurge[i]) + } + log.Infof("[SeqSender] txs purged count: %d, fromNonce: %d, toNonce: %d", len(toPurge), firstPurged, lastPurged) + } + s.mutexEthTx.Unlock() +} + +// syncEthTxResults syncs results from L1 for transactions in the memory structure +func (s *SequenceSender) syncEthTxResults(ctx context.Context) (uint64, error) { + s.mutexEthTx.Lock() + var txPending uint64 + var txSync uint64 + for hash, data := range s.ethTransactions { + if data.Status == ethtxmanager.MonitoredTxStatusFinalized.String() { + continue + } + + _ = s.getResultAndUpdateEthTx(ctx, hash) + txSync++ + txStatus := s.ethTransactions[hash].Status + // Count if it is not in a final state + if s.ethTransactions[hash].OnMonitor && + txStatus != ethtxmanager.MonitoredTxStatusFailed.String() && + txStatus != ethtxmanager.MonitoredTxStatusSafe.String() && + txStatus != ethtxmanager.MonitoredTxStatusFinalized.String() { + txPending++ + } + } + s.mutexEthTx.Unlock() + + // Save updated sequences transactions + err := s.saveSentSequencesTransactions(ctx) + if err != nil { + log.Errorf("[SeqSender] error saving tx sequence, error: %v", err) + } + + log.Infof("[SeqSender] %d tx results synchronized (%d in pending state)", txSync, txPending) + return txPending, nil +} + +// syncAllEthTxResults syncs all tx results from L1 +func (s *SequenceSender) syncAllEthTxResults(ctx context.Context) error { + // Get all results + results, err := s.ethTxManager.ResultsByStatus(ctx, nil) + if err != nil { + log.Warnf("[SeqSender] error getting results for all tx: %v", err) + return err + } + + // Check and update tx status + numResults := len(results) + s.mutexEthTx.Lock() + for _, result := range results { + txSequence, exists := s.ethTransactions[result.ID] + if !exists { + log.Infof("[SeqSender] transaction %v missing in memory structure. Adding it", result.ID) + // No info: from/to batch and the sent timestamp + s.ethTransactions[result.ID] = ðTxData{ + SentL1Timestamp: time.Time{}, + StatusTimestamp: time.Now(), + OnMonitor: true, + Status: "*missing", + } + txSequence = s.ethTransactions[result.ID] + } + + s.updateEthTxResult(txSequence, result) + } + s.mutexEthTx.Unlock() + + // Save updated sequences transactions + err = s.saveSentSequencesTransactions(ctx) + if err != nil { + log.Errorf("[SeqSender] error saving tx sequence, error: %v", err) + } + + log.Infof("[SeqSender] %d tx results synchronized", numResults) + return nil +} + +// copyTxData copies tx data in the internal structure +func (s *SequenceSender) copyTxData(txHash common.Hash, txData []byte, txsResults map[common.Hash]ethtxmanager.TxResult) { + s.ethTxData[txHash] = make([]byte, len(txData)) + copy(s.ethTxData[txHash], txData) + + s.ethTransactions[txHash].Txs = make(map[common.Hash]ethTxAdditionalData, 0) + for hash, result := range txsResults { + var gasPrice *big.Int + if result.Tx != nil { + gasPrice = result.Tx.GasPrice() + } + + add := ethTxAdditionalData{ + GasPrice: gasPrice, + RevertMessage: result.RevertMessage, + } + s.ethTransactions[txHash].Txs[hash] = add + } +} + +// updateEthTxResult handles updating transaction state +func (s *SequenceSender) updateEthTxResult(txData *ethTxData, txResult ethtxmanager.MonitoredTxResult) { + if txData.Status != txResult.Status.String() { + log.Infof("[SeqSender] update transaction %v to state %s", txResult.ID, txResult.Status.String()) + txData.StatusTimestamp = time.Now() + stTrans := txData.StatusTimestamp.Format("2006-01-02T15:04:05.000-07:00") + ", " + txData.Status + ", " + txResult.Status.String() + txData.Status = txResult.Status.String() + txData.StateHistory = append(txData.StateHistory, stTrans) + + // Manage according to the state + statusConsolidated := txData.Status == ethtxmanager.MonitoredTxStatusSafe.String() || txData.Status == ethtxmanager.MonitoredTxStatusFinalized.String() + if txData.Status == ethtxmanager.MonitoredTxStatusFailed.String() { + s.logFatalf("[SeqSender] transaction %v result failed!") + } else if statusConsolidated && txData.ToBatch >= s.latestVirtualBatch { + s.latestVirtualTime = txData.StatusTimestamp + } + } + + // Update info received from L1 + txData.Nonce = txResult.Nonce + if txResult.To != nil { + txData.To = *txResult.To + } + if txResult.MinedAtBlockNumber != nil { + txData.MinedAtBlock = *txResult.MinedAtBlockNumber + } + s.copyTxData(txResult.ID, txResult.Data, txResult.Txs) +} + +// getResultAndUpdateEthTx updates the tx status from the ethTxManager +func (s *SequenceSender) getResultAndUpdateEthTx(ctx context.Context, txHash common.Hash) error { + txData, exists := s.ethTransactions[txHash] + if !exists { + log.Errorf("[SeqSender] transaction %v not found in memory", txHash) + return errors.New("transaction not found in memory structure") + } + + txResult, err := s.ethTxManager.Result(ctx, txHash) + if err == ethtxmanager.ErrNotFound { + log.Infof("[SeqSender] transaction %v does not exist in ethtxmanager. Marking it", txHash) + txData.OnMonitor = false + // Resend tx + errSend := s.sendTx(ctx, true, &txHash, nil, 0, 0, nil) + if errSend == nil { + txData.OnMonitor = false + } + } else if err != nil { + log.Errorf("[SeqSender] error getting result for tx %v: %v", txHash, err) + return err + } else { + s.updateEthTxResult(txData, txResult) + } + + return nil +} + +// tryToSendSequence checks if there is a sequence and it's worth it to send to L1 +func (s *SequenceSender) tryToSendSequence(ctx context.Context) { + // Update latest virtual batch + log.Infof("[SeqSender] updating virtual batch") + err := s.updateLatestVirtualBatch() + if err != nil { + return + } + + // Update state of transactions + log.Infof("[SeqSender] updating tx results") + countPending, err := s.syncEthTxResults(ctx) + if err != nil { + return + } + + // Check if the sequence sending is stopped + if s.seqSendingStopped { + log.Warnf("[SeqSender] sending is stopped!") + return + } + + // Check if reached the maximum number of pending transactions + if countPending >= s.cfg.MaxPendingTx { + log.Infof("[SeqSender] max number of pending txs (%d) reached. Waiting for some to be completed", countPending) + return + } + + // Check if should send sequence to L1 + log.Infof("[SeqSender] getting sequences to send") + sequences, err := s.getSequencesToSend() + if err != nil || len(sequences) == 0 { + if err != nil { + log.Errorf("[SeqSender] error getting sequences: %v", err) + } + return + } + + // Send sequences to L1 + sequenceCount := len(sequences) + firstSequence := sequences[0] + lastSequence := sequences[sequenceCount-1] + lastL2BlockTimestamp := lastSequence.LastL2BLockTimestamp + + log.Infof("[SeqSender] sending sequences to L1. From batch %d to batch %d", firstSequence.BatchNumber, lastSequence.BatchNumber) + printSequences(sequences) + + // Wait until last L1 block timestamp is L1BlockTimestampMargin seconds above the timestamp of the last L2 block in the sequence + timeMargin := int64(s.cfg.L1BlockTimestampMargin.Seconds()) + for { + // Get header of the last L1 block + lastL1BlockHeader, err := s.etherman.GetLatestBlockHeader(ctx) + if err != nil { + log.Errorf("[SeqSender] failed to get last L1 block timestamp, err: %v", err) + return + } + + elapsed, waitTime := s.marginTimeElapsed(lastL2BlockTimestamp, lastL1BlockHeader.Time, timeMargin) + + if !elapsed { + log.Infof("[SeqSender] waiting at least %d seconds to send sequences, time difference between last L1 block %d (ts: %d) and last L2 block %d (ts: %d) in the sequence is lower than %d seconds", + waitTime, lastL1BlockHeader.Number, lastL1BlockHeader.Time, lastSequence.BatchNumber, lastL2BlockTimestamp, timeMargin) + time.Sleep(time.Duration(waitTime) * time.Second) + } else { + log.Infof("[SeqSender] continuing, time difference between last L1 block %d (ts: %d) and last L2 block %d (ts: %d) in the sequence is greater than %d seconds", + lastL1BlockHeader.Number, lastL1BlockHeader.Time, lastSequence.BatchNumber, lastL2BlockTimestamp, timeMargin) + break + } + } + + // Sanity check: Wait also until current time is L1BlockTimestampMargin seconds above the timestamp of the last L2 block in the sequence + for { + currentTime := uint64(time.Now().Unix()) + + elapsed, waitTime := s.marginTimeElapsed(lastL2BlockTimestamp, currentTime, timeMargin) + + // Wait if the time difference is less than L1BlockTimestampMargin + if !elapsed { + log.Infof("[SeqSender] waiting at least %d seconds to send sequences, time difference between now (ts: %d) and last L2 block %d (ts: %d) in the sequence is lower than %d seconds", + waitTime, currentTime, lastSequence.BatchNumber, lastL2BlockTimestamp, timeMargin) + time.Sleep(time.Duration(waitTime) * time.Second) + } else { + log.Infof("[SeqSender]sending sequences now, time difference between now (ts: %d) and last L2 block %d (ts: %d) in the sequence is also greater than %d seconds", + currentTime, lastSequence.BatchNumber, lastL2BlockTimestamp, timeMargin) + break + } + } + + // Send sequences to L1 + log.Infof("[SeqSender] sending sequences to L1. From batch %d to batch %d", firstSequence.BatchNumber, lastSequence.BatchNumber) + printSequences(sequences) + + // Post sequences to DA backend + var dataAvailabilityMessage []byte + if s.cfg.IsValidiumMode { + dataAvailabilityMessage, err = s.da.PostSequence(ctx, sequences) + if err != nil { + log.Error("error posting sequences to the data availability protocol: ", err) + return + } + } + + // Build sequence data + tx, err := s.etherman.BuildSequenceBatchesTx(s.cfg.SenderAddress, sequences, lastSequence.LastL2BLockTimestamp, firstSequence.BatchNumber-1, s.cfg.L2Coinbase, dataAvailabilityMessage) + if err != nil { + log.Errorf("[SeqSender] error estimating new sequenceBatches to add to ethtxmanager: ", err) + return + } + + // Add sequence tx + err = s.sendTx(ctx, false, nil, tx.To(), firstSequence.BatchNumber, lastSequence.BatchNumber, tx.Data()) + if err != nil { + return + } + + // Purge sequences data from memory + s.purgeSequences() +} + +// sendTx adds transaction to the ethTxManager to send it to L1 +func (s *SequenceSender) sendTx(ctx context.Context, resend bool, txOldHash *common.Hash, to *common.Address, fromBatch uint64, toBatch uint64, data []byte) error { + // Params if new tx to send or resend a previous tx + var paramTo *common.Address + var paramNonce *uint64 + var paramData []byte + var valueFromBatch uint64 + var valueToBatch uint64 + var valueToAddress common.Address + + if !resend { + paramTo = to + paramNonce = &s.currentNonce + paramData = data + valueFromBatch = fromBatch + valueToBatch = toBatch + } else { + if txOldHash == nil { + log.Errorf("[SeqSender] trying to resend a tx with nil hash") + return errors.New("resend tx with nil hash monitor id") + } + paramTo = &s.ethTransactions[*txOldHash].To + paramNonce = &s.ethTransactions[*txOldHash].Nonce + paramData = s.ethTxData[*txOldHash] + valueFromBatch = s.ethTransactions[*txOldHash].FromBatch + valueToBatch = s.ethTransactions[*txOldHash].ToBatch + } + if paramTo != nil { + valueToAddress = *paramTo + } + + // Add sequence tx + txHash, err := s.ethTxManager.Add(ctx, paramTo, paramNonce, big.NewInt(0), paramData, s.cfg.GasOffset, nil) + if err != nil { + log.Errorf("[SeqSender] error adding sequence to ethtxmanager: %v", err) + return err + } + if !resend { + s.currentNonce++ + } + + // Add new eth tx + txData := ethTxData{ + SentL1Timestamp: time.Now(), + StatusTimestamp: time.Now(), + Status: "*new", + FromBatch: valueFromBatch, + ToBatch: valueToBatch, + OnMonitor: true, + To: valueToAddress, + } + + // Add tx to internal structure + s.mutexEthTx.Lock() + s.ethTransactions[txHash] = &txData + txResults := make(map[common.Hash]ethtxmanager.TxResult, 0) + s.copyTxData(txHash, paramData, txResults) + _ = s.getResultAndUpdateEthTx(ctx, txHash) + if !resend { + s.latestSentToL1Batch = valueToBatch + } else { + s.ethTransactions[*txOldHash].Status = "*resent" + } + s.mutexEthTx.Unlock() + + // Save sent sequences + err = s.saveSentSequencesTransactions(ctx) + if err != nil { + log.Errorf("[SeqSender] error saving tx sequence sent, error: %v", err) + } + return nil +} + +// getSequencesToSend generates sequences to be sent to L1. Empty array means there are no sequences to send or it's not worth sending +func (s *SequenceSender) getSequencesToSend() ([]ethmantypes.Sequence, error) { + // Add sequences until too big for a single L1 tx or last batch is reached + s.mutexSequence.Lock() + defer s.mutexSequence.Unlock() + var prevCoinbase common.Address + sequences := []ethmantypes.Sequence{} + for i := 0; i < len(s.sequenceList); i++ { + batchNumber := s.sequenceList[i] + if batchNumber <= s.latestVirtualBatch || batchNumber <= s.latestSentToL1Batch { + continue + } + + // Check if the next batch belongs to a new forkid, in this case we need to stop sequencing as we need to + // wait the upgrade of forkid is completed and s.cfg.NumBatchForkIdUpgrade is disabled (=0) again + if (s.cfg.ForkUpgradeBatchNumber != 0) && (batchNumber == (s.cfg.ForkUpgradeBatchNumber + 1)) { + return nil, fmt.Errorf("aborting sequencing process as we reached the batch %d where a new forkid is applied (upgrade)", s.cfg.ForkUpgradeBatchNumber+1) + } + + // Check if batch is closed + if !s.sequenceData[batchNumber].batchClosed { + // Reached current wip batch + break + } + + // New potential batch to add to the sequence + batch := *s.sequenceData[batchNumber].batch + + // If the coinbase changes, the sequence ends here + if len(sequences) > 0 && batch.LastCoinbase != prevCoinbase { + log.Infof("[SeqSender] batch with different coinbase (batch %v, sequence %v), sequence will be sent to this point", prevCoinbase, batch.LastCoinbase) + return sequences, nil + } + prevCoinbase = batch.LastCoinbase + + // Add new sequence + sequences = append(sequences, batch) + + if s.cfg.IsValidiumMode { + if len(sequences) == int(s.cfg.MaxBatchesForL1) { + log.Infof( + "[SeqSender] sequence should be sent to L1, because MaxBatchesForL1 (%d) has been reached", + s.cfg.MaxBatchesForL1, + ) + return sequences, nil + } + } else { + firstSequence := sequences[0] + lastSequence := sequences[len(sequences)-1] + + // Check if can be sent + tx, err := s.etherman.BuildSequenceBatchesTx(s.cfg.SenderAddress, sequences, lastSequence.LastL2BLockTimestamp, firstSequence.BatchNumber-1, s.cfg.L2Coinbase, nil) + if err == nil && tx.Size() > s.cfg.MaxTxSizeForL1 { + log.Infof("[SeqSender] oversized Data on TX oldHash %s (txSize %d > %d)", tx.Hash(), tx.Size(), s.cfg.MaxTxSizeForL1) + err = ErrOversizedData + } + + if err != nil { + log.Infof("[SeqSender] handling estimate gas send sequence error: %v", err) + sequences, err = s.handleEstimateGasSendSequenceErr(sequences, batchNumber, err) + if sequences != nil { + // Handling the error gracefully, re-processing the sequence as a sanity check + lastSequence = sequences[len(sequences)-1] + _, err = s.etherman.BuildSequenceBatchesTx(s.cfg.SenderAddress, sequences, lastSequence.LastL2BLockTimestamp, firstSequence.BatchNumber-1, s.cfg.L2Coinbase, nil) + return sequences, err + } + return sequences, err + } + } + + // Check if the current batch is the last before a change to a new forkid, in this case we need to close and send the sequence to L1 + if (s.cfg.ForkUpgradeBatchNumber != 0) && (batchNumber == (s.cfg.ForkUpgradeBatchNumber)) { + log.Infof("[SeqSender] sequence should be sent to L1, as we have reached the batch %d from which a new forkid is applied (upgrade)", s.cfg.ForkUpgradeBatchNumber) + return sequences, nil + } + } + + // Reached the latest batch. Decide if it's worth to send the sequence, or wait for new batches + if len(sequences) == 0 { + log.Infof("[SeqSender] no batches to be sequenced") + return nil, nil + } + + if s.latestVirtualTime.Before(time.Now().Add(-s.cfg.LastBatchVirtualizationTimeMaxWaitPeriod.Duration)) { + log.Infof("[SeqSender] sequence should be sent, too much time without sending anything to L1") + return sequences, nil + } + + log.Infof("[SeqSender] not enough time has passed since last batch was virtualized and the sequence could be bigger") + return nil, nil +} + +// handleEstimateGasSendSequenceErr handles an error on the estimate gas. Results: (nil,nil)=requires waiting, (nil,error)=no handled gracefully, (seq,nil) handled gracefully +func (s *SequenceSender) handleEstimateGasSendSequenceErr(sequences []ethmantypes.Sequence, currentBatchNumToSequence uint64, err error) ([]ethmantypes.Sequence, error) { + // Insufficient allowance + if errors.Is(err, etherman.ErrInsufficientAllowance) { + return nil, err + } + if isDataForEthTxTooBig(err) { + // Remove the latest item and send the sequences + log.Infof("Done building sequences, selected batches to %d. Batch %d caused the L1 tx to be too big: %v", currentBatchNumToSequence-1, currentBatchNumToSequence, err) + } else { + // Remove the latest item and send the sequences + log.Infof("Done building sequences, selected batches to %d. Batch %d excluded due to unknown error: %v", currentBatchNumToSequence, currentBatchNumToSequence+1, err) + } + + if len(sequences) > 1 { + sequences = sequences[:len(sequences)-1] + } else { + sequences = nil + } + return sequences, nil +} + +// isDataForEthTxTooBig checks if tx oversize error +func isDataForEthTxTooBig(err error) bool { + return errors.Is(err, etherman.ErrGasRequiredExceedsAllowance) || + errors.Is(err, ErrOversizedData) || + errors.Is(err, etherman.ErrContentLengthTooLarge) +} + +// loadSentSequencesTransactions loads the file into the memory structure +func (s *SequenceSender) loadSentSequencesTransactions() error { + // Check if file exists + if _, err := os.Stat(s.cfg.SequencesTxFileName); os.IsNotExist(err) { + log.Infof("[SeqSender] file not found %s: %v", s.cfg.SequencesTxFileName, err) + return nil + } else if err != nil { + log.Errorf("[SeqSender] error opening file %s: %v", s.cfg.SequencesTxFileName, err) + return err + } + + // Read file + data, err := os.ReadFile(s.cfg.SequencesTxFileName) + if err != nil { + log.Errorf("[SeqSender] error reading file %s: %v", s.cfg.SequencesTxFileName, err) + return err + } + + // Restore memory structure + s.mutexEthTx.Lock() + err = json.Unmarshal(data, &s.ethTransactions) + s.mutexEthTx.Unlock() + if err != nil { + log.Errorf("[SeqSender] error decoding data from %s: %v", s.cfg.SequencesTxFileName, err) + return err + } + + return nil +} + +// saveSentSequencesTransactions saves memory structure into persistent file +func (s *SequenceSender) saveSentSequencesTransactions(ctx context.Context) error { + var err error + + // Purge tx + s.purgeEthTx(ctx) + + // Ceate file + fileName := s.cfg.SequencesTxFileName[0:strings.IndexRune(s.cfg.SequencesTxFileName, '.')] + ".tmp" + s.sequencesTxFile, err = os.Create(fileName) + if err != nil { + log.Errorf("[SeqSender] error creating file %s: %v", fileName, err) + return err + } + defer s.sequencesTxFile.Close() + + // Write data JSON encoded + encoder := json.NewEncoder(s.sequencesTxFile) + encoder.SetIndent("", " ") + s.mutexEthTx.Lock() + err = encoder.Encode(s.ethTransactions) + s.mutexEthTx.Unlock() + if err != nil { + log.Errorf("[SeqSender] error writing file %s: %v", fileName, err) + return err + } + + // Rename the new file + err = os.Rename(fileName, s.cfg.SequencesTxFileName) + if err != nil { + log.Errorf("[SeqSender] error renaming file %s to %s: %v", fileName, s.cfg.SequencesTxFileName, err) + return err + } + + return nil +} + +// handleReceivedDataStream manages the events received by the streaming +func (s *SequenceSender) handleReceivedDataStream(e *datastreamer.FileEntry, c *datastreamer.StreamClient, ss *datastreamer.StreamServer) error { + dsType := datastream.EntryType(e.Type) + + switch dsType { + case datastream.EntryType_ENTRY_TYPE_L2_BLOCK: + // Handle stream entry: L2Block + l2Block := &datastream.L2Block{} + err := proto.Unmarshal(e.Data, l2Block) + if err != nil { + log.Errorf("[SeqSender] error unmarshalling L2Block: %v", err) + return err + } + + // Already virtualized + if l2Block.BatchNumber <= s.fromStreamBatch { + if l2Block.BatchNumber != s.latestStreamBatch { + log.Infof("[SeqSender] skipped! batch already virtualized, number %d", l2Block.BatchNumber) + } + } else if !s.validStream && l2Block.BatchNumber == s.fromStreamBatch+1 { + // Initial case after startup + s.addNewSequenceBatch(l2Block) + s.validStream = true + } + + // Latest stream batch + s.latestStreamBatch = l2Block.BatchNumber + if !s.validStream { + return nil + } + + // Handle whether it's only a new block or also a new batch + if l2Block.BatchNumber > s.wipBatch { + // New batch in the sequence + // Close current batch + err := s.closeSequenceBatch() + if err != nil { + log.Fatalf("[SeqSender] error closing wip batch") + return err + } + + // Create new sequential batch + s.addNewSequenceBatch(l2Block) + } + + // Add L2 block + s.addNewBatchL2Block(l2Block) + + case datastream.EntryType_ENTRY_TYPE_TRANSACTION: + // Handle stream entry: Transaction + if !s.validStream { + return nil + } + + l2Tx := &datastream.Transaction{} + err := proto.Unmarshal(e.Data, l2Tx) + if err != nil { + log.Errorf("[SeqSender] error unmarshalling Transaction: %v", err) + return err + } + + // Add tx data + s.addNewBlockTx(l2Tx) + + case datastream.EntryType_ENTRY_TYPE_BATCH_START: + // Handle stream entry: BatchStart + if !s.validStream { + return nil + } + + batch := &datastream.BatchStart{} + err := proto.Unmarshal(e.Data, batch) + if err != nil { + log.Errorf("[SeqSender] error unmarshalling BatchStart: %v", err) + return err + } + + // Add batch start data + s.addInfoSequenceBatchStart(batch) + + case datastream.EntryType_ENTRY_TYPE_BATCH_END: + // Handle stream entry: BatchEnd + if !s.validStream { + return nil + } + + batch := &datastream.BatchEnd{} + err := proto.Unmarshal(e.Data, batch) + if err != nil { + log.Errorf("[SeqSender] error unmarshalling BatchEnd: %v", err) + return err + } + + // Add batch end data + s.addInfoSequenceBatchEnd(batch) + } + + return nil +} + +// closeSequenceBatch closes the current batch +func (s *SequenceSender) closeSequenceBatch() error { + s.mutexSequence.Lock() + log.Infof("[SeqSender] closing batch %d", s.wipBatch) + + data := s.sequenceData[s.wipBatch] + if data != nil { + data.batchClosed = true + + var err error + data.batch.BatchL2Data, err = state.EncodeBatchV2(data.batchRaw) + if err != nil { + log.Errorf("[SeqSender] error closing and encoding the batch %d: %v", s.wipBatch, err) + return err + } + } + + s.mutexSequence.Unlock() + return nil +} + +// addNewSequenceBatch adds a new batch to the sequence +func (s *SequenceSender) addNewSequenceBatch(l2Block *datastream.L2Block) { + s.mutexSequence.Lock() + log.Infof("[SeqSender] ...new batch, number %d", l2Block.BatchNumber) + + if l2Block.BatchNumber > s.wipBatch+1 { + s.logFatalf("[SeqSender] new batch number (%d) is not consecutive to the current one (%d)", l2Block.BatchNumber, s.wipBatch) + } else if l2Block.BatchNumber < s.wipBatch { + s.logFatalf("[SeqSender] new batch number (%d) is lower than the current one (%d)", l2Block.BatchNumber, s.wipBatch) + } + + // Create sequence + sequence := ethmantypes.Sequence{ + GlobalExitRoot: common.BytesToHash(l2Block.GlobalExitRoot), + LastL2BLockTimestamp: l2Block.Timestamp, + BatchNumber: l2Block.BatchNumber, + LastCoinbase: common.BytesToAddress(l2Block.Coinbase), + } + + // Add to the list + s.sequenceList = append(s.sequenceList, l2Block.BatchNumber) + + // Create initial data + batchRaw := state.BatchRawV2{} + data := sequenceData{ + batchClosed: false, + batch: &sequence, + batchRaw: &batchRaw, + } + s.sequenceData[l2Block.BatchNumber] = &data + + // Update wip batch + s.wipBatch = l2Block.BatchNumber + s.mutexSequence.Unlock() +} + +// addInfoSequenceBatchStart adds info from the batch start +func (s *SequenceSender) addInfoSequenceBatchStart(batch *datastream.BatchStart) { + s.mutexSequence.Lock() + log.Infof("[SeqSender] batch %d (%s) Start: type %d forkId %d chainId %d", batch.Number, datastream.BatchType_name[int32(batch.Type)], batch.Type, batch.ForkId, batch.ChainId) + + // Current batch + data := s.sequenceData[s.wipBatch] + if data != nil { + wipBatch := data.batch + if wipBatch.BatchNumber+1 != batch.Number { + s.logFatalf("[SeqSender] batch start number (%d) does not match the current consecutive one (%d)", batch.Number, wipBatch.BatchNumber) + } + } + + s.mutexSequence.Unlock() +} + +// addInfoSequenceBatchEnd adds info from the batch end +func (s *SequenceSender) addInfoSequenceBatchEnd(batch *datastream.BatchEnd) { + s.mutexSequence.Lock() + + // Current batch + data := s.sequenceData[s.wipBatch] + if data != nil { + wipBatch := data.batch + if wipBatch.BatchNumber == batch.Number { + wipBatch.StateRoot = common.BytesToHash(batch.StateRoot) + } else { + s.logFatalf("[SeqSender] batch end number (%d) does not match the current one (%d)", batch.Number, wipBatch.BatchNumber) + } + } + + s.mutexSequence.Unlock() +} + +// addNewBatchL2Block adds a new L2 block to the work in progress batch +func (s *SequenceSender) addNewBatchL2Block(l2Block *datastream.L2Block) { + s.mutexSequence.Lock() + log.Infof("[SeqSender] .....new L2 block, number %d (batch %d)", l2Block.Number, l2Block.BatchNumber) + + // Current batch + data := s.sequenceData[s.wipBatch] + if data != nil { + wipBatchRaw := data.batchRaw + data.batch.LastL2BLockTimestamp = l2Block.Timestamp + // Sanity check: should be the same coinbase within the batch + if common.BytesToAddress(l2Block.Coinbase) != data.batch.LastCoinbase { + s.logFatalf("[SeqSender] coinbase changed within the batch! (Previous %v, Current %v)", data.batch.LastCoinbase, common.BytesToAddress(l2Block.Coinbase)) + } + data.batch.LastCoinbase = common.BytesToAddress(l2Block.Coinbase) + data.batch.StateRoot = common.BytesToHash(l2Block.StateRoot) + + // New L2 block raw + newBlockRaw := state.L2BlockRaw{} + + // Add L2 block + wipBatchRaw.Blocks = append(wipBatchRaw.Blocks, newBlockRaw) + + // Update batch timestamp + data.batch.LastL2BLockTimestamp = l2Block.Timestamp + + // Get current L2 block + _, blockRaw := s.getWipL2Block() + if blockRaw == nil { + log.Debugf("[SeqSender] wip block %d not found!") + return + } + + // Fill in data + blockRaw.DeltaTimestamp = l2Block.DeltaTimestamp + blockRaw.IndexL1InfoTree = l2Block.L1InfotreeIndex + } + + s.mutexSequence.Unlock() +} + +// addNewBlockTx adds a new Tx to the current L2 block +func (s *SequenceSender) addNewBlockTx(l2Tx *datastream.Transaction) { + s.mutexSequence.Lock() + log.Debugf("[SeqSender] ........new tx, length %d EGP %d SR %x..", len(l2Tx.Encoded), l2Tx.EffectiveGasPricePercentage, l2Tx.ImStateRoot[:8]) + + // Current L2 block + _, blockRaw := s.getWipL2Block() + + // New Tx raw + tx, err := state.DecodeTx(common.Bytes2Hex(l2Tx.Encoded)) + if err != nil { + log.Fatalf("[SeqSender] error decoding tx!") + return + } + + l2TxRaw := state.L2TxRaw{ + EfficiencyPercentage: uint8(l2Tx.EffectiveGasPricePercentage), + TxAlreadyEncoded: false, + Tx: tx, + } + + // Add Tx + blockRaw.Transactions = append(blockRaw.Transactions, l2TxRaw) + s.mutexSequence.Unlock() +} + +// getWipL2Block returns index of the array and pointer to the current L2 block (helper func) +func (s *SequenceSender) getWipL2Block() (uint64, *state.L2BlockRaw) { + // Current batch + var wipBatchRaw *state.BatchRawV2 + if s.sequenceData[s.wipBatch] != nil { + wipBatchRaw = s.sequenceData[s.wipBatch].batchRaw + } + + // Current wip block + if len(wipBatchRaw.Blocks) > 0 { + blockIndex := uint64(len(wipBatchRaw.Blocks)) - 1 + return blockIndex, &wipBatchRaw.Blocks[blockIndex] + } else { + return 0, nil + } +} + +// updateLatestVirtualBatch queries the value in L1 and updates the latest virtual batch field +func (s *SequenceSender) updateLatestVirtualBatch() error { + // Get latest virtual state batch from L1 + var err error + + s.latestVirtualBatch, err = s.etherman.GetLatestBatchNumber() + if err != nil { + log.Errorf("[SeqSender] error getting latest virtual batch, error: %v", err) + return errors.New("fail to get latest virtual batch") + } else { + log.Infof("[SeqSender] latest virtual batch is %d", s.latestVirtualBatch) + } + return nil +} + +// marginTimeElapsed checks if the time between currentTime and l2BlockTimestamp is greater than timeMargin. +// If it's greater returns true, otherwise it returns false and the waitTime needed to achieve this timeMargin +func (s *SequenceSender) marginTimeElapsed(l2BlockTimestamp uint64, currentTime uint64, timeMargin int64) (bool, int64) { + // Check the time difference between L2 block and currentTime + var timeDiff int64 + if l2BlockTimestamp >= currentTime { + //L2 block timestamp is above currentTime, negative timeDiff. We do in this way to avoid uint64 overflow + timeDiff = int64(-(l2BlockTimestamp - currentTime)) + } else { + timeDiff = int64(currentTime - l2BlockTimestamp) + } + + // Check if the time difference is less than timeMargin (L1BlockTimestampMargin) + if timeDiff < timeMargin { + var waitTime int64 + if timeDiff < 0 { //L2 block timestamp is above currentTime + waitTime = timeMargin + (-timeDiff) + } else { + waitTime = timeMargin - timeDiff + } + return false, waitTime + } else { // timeDiff is greater than timeMargin + return true, 0 + } +} + +// logFatalf logs error, activates flag to stop sequencing, and remains in an infinite loop +func (s *SequenceSender) logFatalf(template string, args ...interface{}) { + s.seqSendingStopped = true + log.Errorf(template, args...) + log.Errorf("[SeqSender] sequence sending stopped.") + for { + time.Sleep(1 * time.Second) + } +} + +// printSequences prints data from slice of type sequences +func printSequences(sequences []ethmantypes.Sequence) { + for i, seq := range sequences { + log.Debugf("[SeqSender] // sequence(%d): batch: %d, ts: %v, lenData: %d, GER: %x..", i, seq.BatchNumber, seq.LastL2BLockTimestamp, len(seq.BatchL2Data), seq.GlobalExitRoot[:8]) + } +} + +// printBatch prints data from batch raw V2 +func printBatch(raw *state.BatchRawV2, showBlock bool, showTx bool) { + // Total amount of L2 tx in the batch + totalL2Txs := 0 + for k := 0; k < len(raw.Blocks); k++ { + totalL2Txs += len(raw.Blocks[k].Transactions) + } + + log.Debugf("[SeqSender] // #blocks: %d, #L2txs: %d", len(raw.Blocks), totalL2Txs) + + // Blocks info + if showBlock { + numBlocks := len(raw.Blocks) + var firstBlock *state.L2BlockRaw + var lastBlock *state.L2BlockRaw + if numBlocks > 0 { + firstBlock = &raw.Blocks[0] + } + if numBlocks > 1 { + lastBlock = &raw.Blocks[numBlocks-1] + } + if firstBlock != nil { + log.Debugf("[SeqSender] // block first (indL1info: %d, delta-timestamp: %d, #L2txs: %d)", firstBlock.IndexL1InfoTree, firstBlock.DeltaTimestamp, len(firstBlock.Transactions)) + // Tx info + if showTx { + for iTx, tx := range firstBlock.Transactions { + v, r, s := tx.Tx.RawSignatureValues() + log.Debugf("[SeqSender] // tx(%d) effPct: %d, encoded: %t, v: %v, r: %v, s: %v", iTx, tx.EfficiencyPercentage, tx.TxAlreadyEncoded, v, r, s) + } + } + } + if lastBlock != nil { + log.Debugf("[SeqSender] // block last (indL1info: %d, delta-timestamp: %d, #L2txs: %d)", lastBlock.DeltaTimestamp, lastBlock.DeltaTimestamp, len(lastBlock.Transactions)) + } + } +} diff --git a/sequencesender/sequencesender_test.go b/sequencesender/sequencesender_test.go new file mode 100644 index 00000000..ca0d33e7 --- /dev/null +++ b/sequencesender/sequencesender_test.go @@ -0,0 +1,65 @@ +package sequencesender + +import ( + "testing" + + "github.com/0xPolygon/cdk/log" + "github.com/0xPolygon/cdk/state" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +const ( + txStreamEncoded1 = "f86508843b9aca0082520894617b3a3528f9cdd6630fd3301b9c8911f7bf063d0a808207f5a0579b72a1c1ffdd845fba45317540982109298e2ec8d67ddf2cdaf22e80903677a01831e9a01291c7ea246742a5b5a543ca6938bfc3f6958c22be06fad99274e4ac" + txStreamEncoded2 = "f86509843b9aca0082520894617b3a3528f9cdd6630fd3301b9c8911f7bf063d0a808207f5a0908a522075e09485166ffa7630cd2b7013897fa1f1238013677d6f0a86efb3d2a0068b12435fcdc8ee254f3b1df8c5b29ed691eeee6065704f061130935976ca99" +) + +func TestStreamTx(t *testing.T) { + tx1, err := state.DecodeTx(txStreamEncoded1) + require.NoError(t, err) + tx2, err := state.DecodeTx(txStreamEncoded2) + require.NoError(t, err) + + txTest := state.L2TxRaw{ + EfficiencyPercentage: 129, + TxAlreadyEncoded: false, + Tx: tx1, + } + txTestEncoded := make([]byte, 0) + txTestEncoded, err = txTest.Encode(txTestEncoded) + require.NoError(t, err) + log.Debugf("%s", common.Bytes2Hex(txTestEncoded)) + + batch := state.BatchRawV2{ + Blocks: []state.L2BlockRaw{ + { + ChangeL2BlockHeader: state.ChangeL2BlockHeader{ + DeltaTimestamp: 3633752, + IndexL1InfoTree: 0, + }, + Transactions: []state.L2TxRaw{ + { + EfficiencyPercentage: 129, + TxAlreadyEncoded: false, + Tx: tx1, + }, + { + EfficiencyPercentage: 97, + TxAlreadyEncoded: false, + Tx: tx2, + }, + }, + }, + }, + } + + printBatch(&batch, true, true) + + encodedBatch, err := state.EncodeBatchV2(&batch) + require.NoError(t, err) + + decodedBatch, err := state.DecodeBatchV2(encodedBatch) + require.NoError(t, err) + + printBatch(decodedBatch, true, true) +} diff --git a/state/batch.go b/state/batch.go new file mode 100644 index 00000000..4975a961 --- /dev/null +++ b/state/batch.go @@ -0,0 +1,33 @@ +package state + +import ( + "time" + + "github.com/0xPolygon/cdk/state/datastream" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// Batch struct +type Batch struct { + BatchNumber uint64 + Coinbase common.Address + BatchL2Data []byte + StateRoot common.Hash + LocalExitRoot common.Hash + AccInputHash common.Hash + L1InfoTreeIndex uint32 + L1InfoRoot common.Hash + // Timestamp (<=incaberry) -> batch time + // (>incaberry) -> minTimestamp used in batch creation, real timestamp is in virtual_batch.batch_timestamp + Timestamp time.Time + Transactions []types.Transaction + GlobalExitRoot common.Hash + ForcedBatchNum *uint64 + Resources BatchResources + // WIP: if WIP == true is a openBatch + WIP bool + ChainID uint64 + ForkID uint64 + Type datastream.BatchType +} diff --git a/state/config.go b/state/config.go new file mode 100644 index 00000000..e5a65e8b --- /dev/null +++ b/state/config.go @@ -0,0 +1,13 @@ +package state + +import ( + "github.com/0xPolygon/cdk/aggregator/db" +) + +// Config is state config +type Config struct { + // ChainID is the L2 ChainID provided by the Network Config + ChainID uint64 + // DB is the database configuration + DB db.Config `mapstructure:"DB"` +} diff --git a/state/datastream.go b/state/datastream.go new file mode 100644 index 00000000..7687dba7 --- /dev/null +++ b/state/datastream.go @@ -0,0 +1,12 @@ +package state + +import ( + "github.com/0xPolygonHermez/zkevm-data-streamer/datastreamer" +) + +const ( + // StreamTypeSequencer represents a Sequencer stream + StreamTypeSequencer datastreamer.StreamType = 1 + // EntryTypeBookMark represents a bookmark entry + EntryTypeBookMark datastreamer.EntryType = datastreamer.EtBookmark +) diff --git a/state/datastream/datastream.pb.go b/state/datastream/datastream.pb.go new file mode 100644 index 00000000..e600a4fd --- /dev/null +++ b/state/datastream/datastream.pb.go @@ -0,0 +1,1069 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v5.27.0 +// source: datastream.proto + +package datastream + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type BookmarkType int32 + +const ( + BookmarkType_BOOKMARK_TYPE_UNSPECIFIED BookmarkType = 0 + BookmarkType_BOOKMARK_TYPE_BATCH BookmarkType = 1 + BookmarkType_BOOKMARK_TYPE_L2_BLOCK BookmarkType = 2 +) + +// Enum value maps for BookmarkType. +var ( + BookmarkType_name = map[int32]string{ + 0: "BOOKMARK_TYPE_UNSPECIFIED", + 1: "BOOKMARK_TYPE_BATCH", + 2: "BOOKMARK_TYPE_L2_BLOCK", + } + BookmarkType_value = map[string]int32{ + "BOOKMARK_TYPE_UNSPECIFIED": 0, + "BOOKMARK_TYPE_BATCH": 1, + "BOOKMARK_TYPE_L2_BLOCK": 2, + } +) + +func (x BookmarkType) Enum() *BookmarkType { + p := new(BookmarkType) + *p = x + return p +} + +func (x BookmarkType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (BookmarkType) Descriptor() protoreflect.EnumDescriptor { + return file_datastream_proto_enumTypes[0].Descriptor() +} + +func (BookmarkType) Type() protoreflect.EnumType { + return &file_datastream_proto_enumTypes[0] +} + +func (x BookmarkType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use BookmarkType.Descriptor instead. +func (BookmarkType) EnumDescriptor() ([]byte, []int) { + return file_datastream_proto_rawDescGZIP(), []int{0} +} + +type EntryType int32 + +const ( + EntryType_ENTRY_TYPE_UNSPECIFIED EntryType = 0 + EntryType_ENTRY_TYPE_BATCH_START EntryType = 1 + EntryType_ENTRY_TYPE_L2_BLOCK EntryType = 2 + EntryType_ENTRY_TYPE_TRANSACTION EntryType = 3 + EntryType_ENTRY_TYPE_BATCH_END EntryType = 4 + EntryType_ENTRY_TYPE_UPDATE_GER EntryType = 5 +) + +// Enum value maps for EntryType. +var ( + EntryType_name = map[int32]string{ + 0: "ENTRY_TYPE_UNSPECIFIED", + 1: "ENTRY_TYPE_BATCH_START", + 2: "ENTRY_TYPE_L2_BLOCK", + 3: "ENTRY_TYPE_TRANSACTION", + 4: "ENTRY_TYPE_BATCH_END", + 5: "ENTRY_TYPE_UPDATE_GER", + } + EntryType_value = map[string]int32{ + "ENTRY_TYPE_UNSPECIFIED": 0, + "ENTRY_TYPE_BATCH_START": 1, + "ENTRY_TYPE_L2_BLOCK": 2, + "ENTRY_TYPE_TRANSACTION": 3, + "ENTRY_TYPE_BATCH_END": 4, + "ENTRY_TYPE_UPDATE_GER": 5, + } +) + +func (x EntryType) Enum() *EntryType { + p := new(EntryType) + *p = x + return p +} + +func (x EntryType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (EntryType) Descriptor() protoreflect.EnumDescriptor { + return file_datastream_proto_enumTypes[1].Descriptor() +} + +func (EntryType) Type() protoreflect.EnumType { + return &file_datastream_proto_enumTypes[1] +} + +func (x EntryType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use EntryType.Descriptor instead. +func (EntryType) EnumDescriptor() ([]byte, []int) { + return file_datastream_proto_rawDescGZIP(), []int{1} +} + +type BatchType int32 + +const ( + BatchType_BATCH_TYPE_UNSPECIFIED BatchType = 0 + BatchType_BATCH_TYPE_REGULAR BatchType = 1 + BatchType_BATCH_TYPE_FORCED BatchType = 2 + BatchType_BATCH_TYPE_INJECTED BatchType = 3 + BatchType_BATCH_TYPE_INVALID BatchType = 4 +) + +// Enum value maps for BatchType. +var ( + BatchType_name = map[int32]string{ + 0: "BATCH_TYPE_UNSPECIFIED", + 1: "BATCH_TYPE_REGULAR", + 2: "BATCH_TYPE_FORCED", + 3: "BATCH_TYPE_INJECTED", + 4: "BATCH_TYPE_INVALID", + } + BatchType_value = map[string]int32{ + "BATCH_TYPE_UNSPECIFIED": 0, + "BATCH_TYPE_REGULAR": 1, + "BATCH_TYPE_FORCED": 2, + "BATCH_TYPE_INJECTED": 3, + "BATCH_TYPE_INVALID": 4, + } +) + +func (x BatchType) Enum() *BatchType { + p := new(BatchType) + *p = x + return p +} + +func (x BatchType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (BatchType) Descriptor() protoreflect.EnumDescriptor { + return file_datastream_proto_enumTypes[2].Descriptor() +} + +func (BatchType) Type() protoreflect.EnumType { + return &file_datastream_proto_enumTypes[2] +} + +func (x BatchType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use BatchType.Descriptor instead. +func (BatchType) EnumDescriptor() ([]byte, []int) { + return file_datastream_proto_rawDescGZIP(), []int{2} +} + +type BatchStart struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Number uint64 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"` + Type BatchType `protobuf:"varint,2,opt,name=type,proto3,enum=datastream.v1.BatchType" json:"type,omitempty"` + ForkId uint64 `protobuf:"varint,3,opt,name=fork_id,json=forkId,proto3" json:"fork_id,omitempty"` + ChainId uint64 `protobuf:"varint,4,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + Debug *Debug `protobuf:"bytes,5,opt,name=debug,proto3" json:"debug,omitempty"` +} + +func (x *BatchStart) Reset() { + *x = BatchStart{} + if protoimpl.UnsafeEnabled { + mi := &file_datastream_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BatchStart) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BatchStart) ProtoMessage() {} + +func (x *BatchStart) ProtoReflect() protoreflect.Message { + mi := &file_datastream_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BatchStart.ProtoReflect.Descriptor instead. +func (*BatchStart) Descriptor() ([]byte, []int) { + return file_datastream_proto_rawDescGZIP(), []int{0} +} + +func (x *BatchStart) GetNumber() uint64 { + if x != nil { + return x.Number + } + return 0 +} + +func (x *BatchStart) GetType() BatchType { + if x != nil { + return x.Type + } + return BatchType_BATCH_TYPE_UNSPECIFIED +} + +func (x *BatchStart) GetForkId() uint64 { + if x != nil { + return x.ForkId + } + return 0 +} + +func (x *BatchStart) GetChainId() uint64 { + if x != nil { + return x.ChainId + } + return 0 +} + +func (x *BatchStart) GetDebug() *Debug { + if x != nil { + return x.Debug + } + return nil +} + +type BatchEnd struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Number uint64 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"` + LocalExitRoot []byte `protobuf:"bytes,2,opt,name=local_exit_root,json=localExitRoot,proto3" json:"local_exit_root,omitempty"` + StateRoot []byte `protobuf:"bytes,3,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty"` + Debug *Debug `protobuf:"bytes,4,opt,name=debug,proto3" json:"debug,omitempty"` +} + +func (x *BatchEnd) Reset() { + *x = BatchEnd{} + if protoimpl.UnsafeEnabled { + mi := &file_datastream_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BatchEnd) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BatchEnd) ProtoMessage() {} + +func (x *BatchEnd) ProtoReflect() protoreflect.Message { + mi := &file_datastream_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BatchEnd.ProtoReflect.Descriptor instead. +func (*BatchEnd) Descriptor() ([]byte, []int) { + return file_datastream_proto_rawDescGZIP(), []int{1} +} + +func (x *BatchEnd) GetNumber() uint64 { + if x != nil { + return x.Number + } + return 0 +} + +func (x *BatchEnd) GetLocalExitRoot() []byte { + if x != nil { + return x.LocalExitRoot + } + return nil +} + +func (x *BatchEnd) GetStateRoot() []byte { + if x != nil { + return x.StateRoot + } + return nil +} + +func (x *BatchEnd) GetDebug() *Debug { + if x != nil { + return x.Debug + } + return nil +} + +type L2Block struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Number uint64 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"` + BatchNumber uint64 `protobuf:"varint,2,opt,name=batch_number,json=batchNumber,proto3" json:"batch_number,omitempty"` + Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + DeltaTimestamp uint32 `protobuf:"varint,4,opt,name=delta_timestamp,json=deltaTimestamp,proto3" json:"delta_timestamp,omitempty"` + MinTimestamp uint64 `protobuf:"varint,5,opt,name=min_timestamp,json=minTimestamp,proto3" json:"min_timestamp,omitempty"` + L1Blockhash []byte `protobuf:"bytes,6,opt,name=l1_blockhash,json=l1Blockhash,proto3" json:"l1_blockhash,omitempty"` + L1InfotreeIndex uint32 `protobuf:"varint,7,opt,name=l1_infotree_index,json=l1InfotreeIndex,proto3" json:"l1_infotree_index,omitempty"` + Hash []byte `protobuf:"bytes,8,opt,name=hash,proto3" json:"hash,omitempty"` + StateRoot []byte `protobuf:"bytes,9,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty"` + GlobalExitRoot []byte `protobuf:"bytes,10,opt,name=global_exit_root,json=globalExitRoot,proto3" json:"global_exit_root,omitempty"` + Coinbase []byte `protobuf:"bytes,11,opt,name=coinbase,proto3" json:"coinbase,omitempty"` + BlockGasLimit uint64 `protobuf:"varint,12,opt,name=block_gas_limit,json=blockGasLimit,proto3" json:"block_gas_limit,omitempty"` + BlockInfoRoot []byte `protobuf:"bytes,13,opt,name=block_info_root,json=blockInfoRoot,proto3" json:"block_info_root,omitempty"` + Debug *Debug `protobuf:"bytes,14,opt,name=debug,proto3" json:"debug,omitempty"` +} + +func (x *L2Block) Reset() { + *x = L2Block{} + if protoimpl.UnsafeEnabled { + mi := &file_datastream_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *L2Block) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*L2Block) ProtoMessage() {} + +func (x *L2Block) ProtoReflect() protoreflect.Message { + mi := &file_datastream_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use L2Block.ProtoReflect.Descriptor instead. +func (*L2Block) Descriptor() ([]byte, []int) { + return file_datastream_proto_rawDescGZIP(), []int{2} +} + +func (x *L2Block) GetNumber() uint64 { + if x != nil { + return x.Number + } + return 0 +} + +func (x *L2Block) GetBatchNumber() uint64 { + if x != nil { + return x.BatchNumber + } + return 0 +} + +func (x *L2Block) GetTimestamp() uint64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *L2Block) GetDeltaTimestamp() uint32 { + if x != nil { + return x.DeltaTimestamp + } + return 0 +} + +func (x *L2Block) GetMinTimestamp() uint64 { + if x != nil { + return x.MinTimestamp + } + return 0 +} + +func (x *L2Block) GetL1Blockhash() []byte { + if x != nil { + return x.L1Blockhash + } + return nil +} + +func (x *L2Block) GetL1InfotreeIndex() uint32 { + if x != nil { + return x.L1InfotreeIndex + } + return 0 +} + +func (x *L2Block) GetHash() []byte { + if x != nil { + return x.Hash + } + return nil +} + +func (x *L2Block) GetStateRoot() []byte { + if x != nil { + return x.StateRoot + } + return nil +} + +func (x *L2Block) GetGlobalExitRoot() []byte { + if x != nil { + return x.GlobalExitRoot + } + return nil +} + +func (x *L2Block) GetCoinbase() []byte { + if x != nil { + return x.Coinbase + } + return nil +} + +func (x *L2Block) GetBlockGasLimit() uint64 { + if x != nil { + return x.BlockGasLimit + } + return 0 +} + +func (x *L2Block) GetBlockInfoRoot() []byte { + if x != nil { + return x.BlockInfoRoot + } + return nil +} + +func (x *L2Block) GetDebug() *Debug { + if x != nil { + return x.Debug + } + return nil +} + +type Transaction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + L2BlockNumber uint64 `protobuf:"varint,1,opt,name=l2block_number,json=l2blockNumber,proto3" json:"l2block_number,omitempty"` + Index uint64 `protobuf:"varint,2,opt,name=index,proto3" json:"index,omitempty"` + IsValid bool `protobuf:"varint,3,opt,name=is_valid,json=isValid,proto3" json:"is_valid,omitempty"` + Encoded []byte `protobuf:"bytes,4,opt,name=encoded,proto3" json:"encoded,omitempty"` + EffectiveGasPricePercentage uint32 `protobuf:"varint,5,opt,name=effective_gas_price_percentage,json=effectiveGasPricePercentage,proto3" json:"effective_gas_price_percentage,omitempty"` + ImStateRoot []byte `protobuf:"bytes,6,opt,name=im_state_root,json=imStateRoot,proto3" json:"im_state_root,omitempty"` + Debug *Debug `protobuf:"bytes,7,opt,name=debug,proto3" json:"debug,omitempty"` +} + +func (x *Transaction) Reset() { + *x = Transaction{} + if protoimpl.UnsafeEnabled { + mi := &file_datastream_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Transaction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Transaction) ProtoMessage() {} + +func (x *Transaction) ProtoReflect() protoreflect.Message { + mi := &file_datastream_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Transaction.ProtoReflect.Descriptor instead. +func (*Transaction) Descriptor() ([]byte, []int) { + return file_datastream_proto_rawDescGZIP(), []int{3} +} + +func (x *Transaction) GetL2BlockNumber() uint64 { + if x != nil { + return x.L2BlockNumber + } + return 0 +} + +func (x *Transaction) GetIndex() uint64 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *Transaction) GetIsValid() bool { + if x != nil { + return x.IsValid + } + return false +} + +func (x *Transaction) GetEncoded() []byte { + if x != nil { + return x.Encoded + } + return nil +} + +func (x *Transaction) GetEffectiveGasPricePercentage() uint32 { + if x != nil { + return x.EffectiveGasPricePercentage + } + return 0 +} + +func (x *Transaction) GetImStateRoot() []byte { + if x != nil { + return x.ImStateRoot + } + return nil +} + +func (x *Transaction) GetDebug() *Debug { + if x != nil { + return x.Debug + } + return nil +} + +type UpdateGER struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BatchNumber uint64 `protobuf:"varint,1,opt,name=batch_number,json=batchNumber,proto3" json:"batch_number,omitempty"` + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + GlobalExitRoot []byte `protobuf:"bytes,3,opt,name=global_exit_root,json=globalExitRoot,proto3" json:"global_exit_root,omitempty"` + Coinbase []byte `protobuf:"bytes,4,opt,name=coinbase,proto3" json:"coinbase,omitempty"` + ForkId uint64 `protobuf:"varint,5,opt,name=fork_id,json=forkId,proto3" json:"fork_id,omitempty"` + ChainId uint64 `protobuf:"varint,6,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + StateRoot []byte `protobuf:"bytes,7,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty"` + Debug *Debug `protobuf:"bytes,8,opt,name=debug,proto3" json:"debug,omitempty"` +} + +func (x *UpdateGER) Reset() { + *x = UpdateGER{} + if protoimpl.UnsafeEnabled { + mi := &file_datastream_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateGER) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateGER) ProtoMessage() {} + +func (x *UpdateGER) ProtoReflect() protoreflect.Message { + mi := &file_datastream_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateGER.ProtoReflect.Descriptor instead. +func (*UpdateGER) Descriptor() ([]byte, []int) { + return file_datastream_proto_rawDescGZIP(), []int{4} +} + +func (x *UpdateGER) GetBatchNumber() uint64 { + if x != nil { + return x.BatchNumber + } + return 0 +} + +func (x *UpdateGER) GetTimestamp() uint64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *UpdateGER) GetGlobalExitRoot() []byte { + if x != nil { + return x.GlobalExitRoot + } + return nil +} + +func (x *UpdateGER) GetCoinbase() []byte { + if x != nil { + return x.Coinbase + } + return nil +} + +func (x *UpdateGER) GetForkId() uint64 { + if x != nil { + return x.ForkId + } + return 0 +} + +func (x *UpdateGER) GetChainId() uint64 { + if x != nil { + return x.ChainId + } + return 0 +} + +func (x *UpdateGER) GetStateRoot() []byte { + if x != nil { + return x.StateRoot + } + return nil +} + +func (x *UpdateGER) GetDebug() *Debug { + if x != nil { + return x.Debug + } + return nil +} + +type BookMark struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type BookmarkType `protobuf:"varint,1,opt,name=type,proto3,enum=datastream.v1.BookmarkType" json:"type,omitempty"` + Value uint64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *BookMark) Reset() { + *x = BookMark{} + if protoimpl.UnsafeEnabled { + mi := &file_datastream_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BookMark) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BookMark) ProtoMessage() {} + +func (x *BookMark) ProtoReflect() protoreflect.Message { + mi := &file_datastream_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BookMark.ProtoReflect.Descriptor instead. +func (*BookMark) Descriptor() ([]byte, []int) { + return file_datastream_proto_rawDescGZIP(), []int{5} +} + +func (x *BookMark) GetType() BookmarkType { + if x != nil { + return x.Type + } + return BookmarkType_BOOKMARK_TYPE_UNSPECIFIED +} + +func (x *BookMark) GetValue() uint64 { + if x != nil { + return x.Value + } + return 0 +} + +type Debug struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *Debug) Reset() { + *x = Debug{} + if protoimpl.UnsafeEnabled { + mi := &file_datastream_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Debug) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Debug) ProtoMessage() {} + +func (x *Debug) ProtoReflect() protoreflect.Message { + mi := &file_datastream_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Debug.ProtoReflect.Descriptor instead. +func (*Debug) Descriptor() ([]byte, []int) { + return file_datastream_proto_rawDescGZIP(), []int{6} +} + +func (x *Debug) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_datastream_proto protoreflect.FileDescriptor + +var file_datastream_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x76, + 0x31, 0x22, 0xb2, 0x01, 0x0a, 0x0a, 0x42, 0x61, 0x74, 0x63, 0x68, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, + 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x05, 0x64, 0x65, + 0x62, 0x75, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x64, 0x61, 0x74, 0x61, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x52, + 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x22, 0x95, 0x01, 0x0a, 0x08, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x45, 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x6c, + 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x45, 0x78, 0x69, 0x74, 0x52, + 0x6f, 0x6f, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, + 0x6f, 0x74, 0x12, 0x2a, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x22, 0xf4, + 0x03, 0x0a, 0x07, 0x4c, 0x32, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x61, 0x74, 0x63, 0x68, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x64, 0x65, + 0x6c, 0x74, 0x61, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x0d, + 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x31, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6c, 0x31, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x68, 0x61, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x31, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x74, + 0x72, 0x65, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0f, 0x6c, 0x31, 0x49, 0x6e, 0x66, 0x6f, 0x74, 0x72, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x68, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, + 0x6f, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x6f, 0x6f, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x65, 0x78, + 0x69, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x45, 0x78, 0x69, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x47, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, + 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x2a, 0x0a, 0x05, 0x64, 0x65, 0x62, + 0x75, 0x67, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x73, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x52, 0x05, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x22, 0x94, 0x02, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x32, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6c, + 0x32, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x18, 0x0a, + 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x1e, 0x65, 0x66, 0x66, 0x65, 0x63, + 0x74, 0x69, 0x76, 0x65, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x70, + 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x1b, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, + 0x63, 0x65, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x0d, + 0x69, 0x6d, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x69, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, + 0x12, 0x2a, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x65, 0x62, 0x75, 0x67, 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x22, 0x91, 0x02, 0x0a, + 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x45, 0x52, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x61, + 0x74, 0x63, 0x68, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0b, 0x62, 0x61, 0x74, 0x63, 0x68, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x45, 0x78, 0x69, + 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, + 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, + 0x6f, 0x6f, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x2a, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, + 0x22, 0x51, 0x0a, 0x08, 0x42, 0x6f, 0x6f, 0x6b, 0x4d, 0x61, 0x72, 0x6b, 0x12, 0x2f, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x64, 0x61, 0x74, + 0x61, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, + 0x61, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0x21, 0x0a, 0x05, 0x44, 0x65, 0x62, 0x75, 0x67, 0x12, 0x18, 0x0a, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2a, 0x62, 0x0a, 0x0c, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, + 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x42, 0x4f, 0x4f, 0x4b, 0x4d, 0x41, + 0x52, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x42, 0x4f, 0x4f, 0x4b, 0x4d, 0x41, 0x52, + 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x41, 0x54, 0x43, 0x48, 0x10, 0x01, 0x12, 0x1a, + 0x0a, 0x16, 0x42, 0x4f, 0x4f, 0x4b, 0x4d, 0x41, 0x52, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x4c, 0x32, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x10, 0x02, 0x2a, 0xad, 0x01, 0x0a, 0x09, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x4e, 0x54, 0x52, + 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x01, + 0x12, 0x17, 0x0a, 0x13, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4c, + 0x32, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x4e, 0x54, + 0x52, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x41, 0x43, 0x54, + 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x45, 0x4e, 0x44, 0x10, 0x04, 0x12, + 0x19, 0x0a, 0x15, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x50, + 0x44, 0x41, 0x54, 0x45, 0x5f, 0x47, 0x45, 0x52, 0x10, 0x05, 0x2a, 0x87, 0x01, 0x0a, 0x09, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x41, 0x54, 0x43, + 0x48, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x52, 0x45, 0x47, 0x55, 0x4c, 0x41, 0x52, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, + 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x4f, 0x52, 0x43, 0x45, + 0x44, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x49, 0x4e, 0x4a, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, + 0x42, 0x41, 0x54, 0x43, 0x48, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, + 0x49, 0x44, 0x10, 0x04, 0x42, 0x3e, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x30, 0x78, 0x50, 0x6f, 0x6c, 0x79, 0x67, 0x6f, 0x6e, 0x48, 0x65, 0x72, 0x6d, + 0x65, 0x7a, 0x2f, 0x7a, 0x6b, 0x65, 0x76, 0x6d, 0x2d, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x6f, 0x72, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_datastream_proto_rawDescOnce sync.Once + file_datastream_proto_rawDescData = file_datastream_proto_rawDesc +) + +func file_datastream_proto_rawDescGZIP() []byte { + file_datastream_proto_rawDescOnce.Do(func() { + file_datastream_proto_rawDescData = protoimpl.X.CompressGZIP(file_datastream_proto_rawDescData) + }) + return file_datastream_proto_rawDescData +} + +var file_datastream_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_datastream_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_datastream_proto_goTypes = []interface{}{ + (BookmarkType)(0), // 0: datastream.v1.BookmarkType + (EntryType)(0), // 1: datastream.v1.EntryType + (BatchType)(0), // 2: datastream.v1.BatchType + (*BatchStart)(nil), // 3: datastream.v1.BatchStart + (*BatchEnd)(nil), // 4: datastream.v1.BatchEnd + (*L2Block)(nil), // 5: datastream.v1.L2Block + (*Transaction)(nil), // 6: datastream.v1.Transaction + (*UpdateGER)(nil), // 7: datastream.v1.UpdateGER + (*BookMark)(nil), // 8: datastream.v1.BookMark + (*Debug)(nil), // 9: datastream.v1.Debug +} +var file_datastream_proto_depIdxs = []int32{ + 2, // 0: datastream.v1.BatchStart.type:type_name -> datastream.v1.BatchType + 9, // 1: datastream.v1.BatchStart.debug:type_name -> datastream.v1.Debug + 9, // 2: datastream.v1.BatchEnd.debug:type_name -> datastream.v1.Debug + 9, // 3: datastream.v1.L2Block.debug:type_name -> datastream.v1.Debug + 9, // 4: datastream.v1.Transaction.debug:type_name -> datastream.v1.Debug + 9, // 5: datastream.v1.UpdateGER.debug:type_name -> datastream.v1.Debug + 0, // 6: datastream.v1.BookMark.type:type_name -> datastream.v1.BookmarkType + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_datastream_proto_init() } +func file_datastream_proto_init() { + if File_datastream_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_datastream_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BatchStart); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_datastream_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BatchEnd); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_datastream_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*L2Block); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_datastream_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Transaction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_datastream_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateGER); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_datastream_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BookMark); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_datastream_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Debug); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_datastream_proto_rawDesc, + NumEnums: 3, + NumMessages: 7, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_datastream_proto_goTypes, + DependencyIndexes: file_datastream_proto_depIdxs, + EnumInfos: file_datastream_proto_enumTypes, + MessageInfos: file_datastream_proto_msgTypes, + }.Build() + File_datastream_proto = out.File + file_datastream_proto_rawDesc = nil + file_datastream_proto_goTypes = nil + file_datastream_proto_depIdxs = nil +} diff --git a/state/encoding_batch_v2.go b/state/encoding_batch_v2.go new file mode 100644 index 00000000..8e3561bd --- /dev/null +++ b/state/encoding_batch_v2.go @@ -0,0 +1,373 @@ +/* +This file provide functions to work with ETROG batches: +- EncodeBatchV2 (equivalent to EncodeTransactions) +- DecodeBatchV2 (equivalent to DecodeTxs) +- DecodeForcedBatchV2 + +Also provide a builder class to create batches (BatchV2Encoder): + This method doesnt check anything, so is more flexible but you need to know what you are doing + - `builder := NewBatchV2Encoder()` : Create a new `BatchV2Encoder`` + - You can call to `AddBlockHeader` or `AddTransaction` to add a block header or a transaction as you wish + - You can call to `GetResult` to get the batch data + + +// batch data format: +// 0xb | 1 | changeL2Block +// --------- L2 block Header --------------------------------- +// 0x73e6af6f | 4 | deltaTimestamp +// 0x00000012 | 4 | indexL1InfoTree +// -------- Transaction --------------------------------------- +// 0x00...0x00 | n | transaction RLP coded +// 0x00...0x00 | 32 | R +// 0x00...0x00 | 32 | S +// 0x00 | 32 | V +// 0x00 | 1 | efficiencyPercentage +// Repeat Transaction or changeL2Block +// Note: RLP codification: https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ + +/ forced batch data format: +// -------- Transaction --------------------------------------- +// 0x00...0x00 | n | transaction RLP coded +// 0x00...0x00 | 32 | R +// 0x00...0x00 | 32 | S +// 0x00 | 32 | V +// 0x00 | 1 | efficiencyPercentage +// Repeat Transaction +// +// Usage: +// There are 2 ways of use this module, direct calls or a builder class: +// 1) Direct calls: +// - EncodeBatchV2: Encode a batch of transactions +// - DecodeBatchV2: Decode a batch of transactions +// +// 2) Builder class: +// This method doesnt check anything, so is more flexible but you need to know what you are doing +// - builder := NewBatchV2Encoder(): Create a new BatchV2Encoder +// - You can call to `AddBlockHeader` or `AddTransaction` to add a block header or a transaction as you wish +// - You can call to `GetResult` to get the batch data + +*/ + +package state + +import ( + "encoding/binary" + "errors" + "fmt" + "strconv" + + "github.com/0xPolygon/cdk/hex" + "github.com/0xPolygon/cdk/log" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" +) + +// ChangeL2BlockHeader is the header of a L2 block. +type ChangeL2BlockHeader struct { + DeltaTimestamp uint32 + IndexL1InfoTree uint32 +} + +// L2BlockRaw is the raw representation of a L2 block. +type L2BlockRaw struct { + BlockNumber uint64 + ChangeL2BlockHeader + Transactions []L2TxRaw +} + +// BatchRawV2 is the representation of a batch of transactions. +type BatchRawV2 struct { + Blocks []L2BlockRaw +} + +// ForcedBatchRawV2 is the representation of a forced batch of transactions. +type ForcedBatchRawV2 struct { + Transactions []L2TxRaw +} + +// L2TxRaw is the raw representation of a L2 transaction inside a L2 block. +type L2TxRaw struct { + EfficiencyPercentage uint8 // valid always + TxAlreadyEncoded bool // If true the tx is already encoded (data field is used) + Tx *types.Transaction // valid if TxAlreadyEncoded == false + Data []byte // valid if TxAlreadyEncoded == true +} + +const ( + changeL2Block = uint8(0x0b) + sizeUInt32 = 4 +) + +var ( + // ErrBatchV2DontStartWithChangeL2Block is returned when the batch start directly with a trsansaction (without a changeL2Block) + ErrBatchV2DontStartWithChangeL2Block = errors.New("batch v2 must start with changeL2Block before Tx (suspect a V1 Batch or a ForcedBatch?))") + // ErrInvalidBatchV2 is returned when the batch is invalid. + ErrInvalidBatchV2 = errors.New("invalid batch v2") + // ErrInvalidRLP is returned when the rlp is invalid. + ErrInvalidRLP = errors.New("invalid rlp codification") +) + +func (b *BatchRawV2) String() string { + res := "" + nTxs := 0 + for i, block := range b.Blocks { + res += fmt.Sprintf("Block[%d/%d]: deltaTimestamp: %d, indexL1InfoTree: %d nTxs: %d\n", i, len(b.Blocks), + block.DeltaTimestamp, block.IndexL1InfoTree, len(block.Transactions)) + nTxs += len(block.Transactions) + } + res = fmt.Sprintf("BATCHv2, nBlocks: %d nTxs:%d \n", len(b.Blocks), nTxs) + res + return res +} + +// EncodeBatchV2 encodes a batch of transactions into a byte slice. +func EncodeBatchV2(batch *BatchRawV2) ([]byte, error) { + if batch == nil { + return nil, fmt.Errorf("batch is nil: %w", ErrInvalidBatchV2) + } + if len(batch.Blocks) == 0 { + return nil, fmt.Errorf("a batch need minimum a L2Block: %w", ErrInvalidBatchV2) + } + + encoder := NewBatchV2Encoder() + for _, block := range batch.Blocks { + encoder.AddBlockHeader(block.ChangeL2BlockHeader) + err := encoder.AddTransactions(block.Transactions) + if err != nil { + return nil, fmt.Errorf("can't encode tx: %w", err) + } + } + return encoder.GetResult(), nil +} + +// BatchV2Encoder is a builder of the batchl2data used by EncodeBatchV2 +type BatchV2Encoder struct { + batchData []byte +} + +// NewBatchV2Encoder creates a new BatchV2Encoder. +func NewBatchV2Encoder() *BatchV2Encoder { + return &BatchV2Encoder{} +} + +// AddBlockHeader adds a block header to the batch. +func (b *BatchV2Encoder) AddBlockHeader(l2BlockHeader ChangeL2BlockHeader) { + b.batchData = l2BlockHeader.Encode(b.batchData) +} + +// AddTransactions adds a set of transactions to the batch. +func (b *BatchV2Encoder) AddTransactions(transactions []L2TxRaw) error { + for i := range transactions { + tx := transactions[i] + err := b.AddTransaction(&tx) + if err != nil { + return fmt.Errorf("can't encode tx: %w", err) + } + } + return nil +} + +// AddTransaction adds a transaction to the batch. +func (b *BatchV2Encoder) AddTransaction(transaction *L2TxRaw) error { + var err error + b.batchData, err = transaction.Encode(b.batchData) + if err != nil { + return fmt.Errorf("can't encode tx: %w", err) + } + return nil +} + +// GetResult returns the batch data. +func (b *BatchV2Encoder) GetResult() []byte { + return b.batchData +} + +// Encode encodes a batch of l2blocks header into a byte slice. +func (c ChangeL2BlockHeader) Encode(batchData []byte) []byte { + batchData = append(batchData, changeL2Block) + batchData = append(batchData, encodeUint32(c.DeltaTimestamp)...) + batchData = append(batchData, encodeUint32(c.IndexL1InfoTree)...) + return batchData +} + +// Encode encodes a transaction into a byte slice. +func (tx L2TxRaw) Encode(batchData []byte) ([]byte, error) { + if tx.TxAlreadyEncoded { + batchData = append(batchData, tx.Data...) + } else { + rlpTx, err := prepareRLPTxData(tx.Tx) + if err != nil { + return nil, fmt.Errorf("can't encode tx to RLP: %w", err) + } + batchData = append(batchData, rlpTx...) + } + batchData = append(batchData, tx.EfficiencyPercentage) + return batchData, nil +} + +// DecodeBatchV2 decodes a batch of transactions from a byte slice. +func DecodeBatchV2(txsData []byte) (*BatchRawV2, error) { + // The transactions is not RLP encoded. Is the raw bytes in this form: 1 byte for the transaction type (always 0b for changeL2Block) + 4 bytes for deltaTimestamp + for bytes for indexL1InfoTree + var err error + var blocks []L2BlockRaw + var currentBlock *L2BlockRaw + pos := int(0) + for pos < len(txsData) { + switch txsData[pos] { + case changeL2Block: + if currentBlock != nil { + blocks = append(blocks, *currentBlock) + } + pos, currentBlock, err = decodeBlockHeader(txsData, pos+1) + if err != nil { + return nil, fmt.Errorf("pos: %d can't decode new BlockHeader: %w", pos, err) + } + // by RLP definition a tx never starts with a 0x0b. So, if is not a changeL2Block + // is a tx + default: + if currentBlock == nil { + _, _, err := DecodeTxRLP(txsData, pos) + if err == nil { + // There is no changeL2Block but have a valid RLP transaction + return nil, ErrBatchV2DontStartWithChangeL2Block + } else { + // No changeL2Block and no valid RLP transaction + return nil, fmt.Errorf("no ChangeL2Block neither valid Tx, batch malformed : %w", ErrInvalidBatchV2) + } + } + var tx *L2TxRaw + pos, tx, err = DecodeTxRLP(txsData, pos) + if err != nil { + return nil, fmt.Errorf("can't decode transactions: %w", err) + } + + currentBlock.Transactions = append(currentBlock.Transactions, *tx) + } + } + if currentBlock != nil { + blocks = append(blocks, *currentBlock) + } + return &BatchRawV2{blocks}, nil +} + +// DecodeForcedBatchV2 decodes a forced batch V2 (Etrog) +// Is forbidden changeL2Block, so are just the set of transactions +func DecodeForcedBatchV2(txsData []byte) (*ForcedBatchRawV2, error) { + txs, _, efficiencyPercentages, err := DecodeTxs(txsData, FORKID_ETROG) + if err != nil { + return nil, err + } + // Sanity check, this should never happen + if len(efficiencyPercentages) != len(txs) { + return nil, fmt.Errorf("error decoding len(efficiencyPercentages) != len(txs). len(efficiencyPercentages)=%d, len(txs)=%d : %w", len(efficiencyPercentages), len(txs), ErrInvalidRLP) + } + forcedBatch := ForcedBatchRawV2{} + for i := range txs { + tx := txs[i] + forcedBatch.Transactions = append(forcedBatch.Transactions, L2TxRaw{ + Tx: tx, + EfficiencyPercentage: efficiencyPercentages[i], + }) + } + return &forcedBatch, nil +} + +// decodeBlockHeader decodes a block header from a byte slice. +// +// Extract: 4 bytes for deltaTimestamp + 4 bytes for indexL1InfoTree +func decodeBlockHeader(txsData []byte, pos int) (int, *L2BlockRaw, error) { + var err error + currentBlock := &L2BlockRaw{} + pos, currentBlock.DeltaTimestamp, err = decodeUint32(txsData, pos) + if err != nil { + return 0, nil, fmt.Errorf("can't get deltaTimestamp: %w", err) + } + pos, currentBlock.IndexL1InfoTree, err = decodeUint32(txsData, pos) + if err != nil { + return 0, nil, fmt.Errorf("can't get leafIndex: %w", err) + } + + return pos, currentBlock, nil +} + +// DecodeTxRLP decodes a transaction from a byte slice. +func DecodeTxRLP(txsData []byte, offset int) (int, *L2TxRaw, error) { + var err error + length, err := decodeRLPListLengthFromOffset(txsData, offset) + if err != nil { + return 0, nil, fmt.Errorf("can't get RLP length (offset=%d): %w", offset, err) + } + endPos := uint64(offset) + length + rLength + sLength + vLength + EfficiencyPercentageByteLength + if endPos > uint64(len(txsData)) { + return 0, nil, fmt.Errorf("can't get tx because not enough data (endPos=%d lenData=%d): %w", + endPos, len(txsData), ErrInvalidBatchV2) + } + fullDataTx := txsData[offset:endPos] + dataStart := uint64(offset) + length + txInfo := txsData[offset:dataStart] + rData := txsData[dataStart : dataStart+rLength] + sData := txsData[dataStart+rLength : dataStart+rLength+sLength] + vData := txsData[dataStart+rLength+sLength : dataStart+rLength+sLength+vLength] + efficiencyPercentage := txsData[dataStart+rLength+sLength+vLength] + var rlpFields [][]byte + err = rlp.DecodeBytes(txInfo, &rlpFields) + if err != nil { + log.Error("error decoding tx Bytes: ", err, ". fullDataTx: ", hex.EncodeToString(fullDataTx), "\n tx: ", hex.EncodeToString(txInfo), "\n Txs received: ", hex.EncodeToString(txsData)) + return 0, nil, err + } + legacyTx, err := RlpFieldsToLegacyTx(rlpFields, vData, rData, sData) + if err != nil { + log.Debug("error creating tx from rlp fields: ", err, ". fullDataTx: ", hex.EncodeToString(fullDataTx), "\n tx: ", hex.EncodeToString(txInfo), "\n Txs received: ", hex.EncodeToString(txsData)) + return 0, nil, err + } + + l2Tx := &L2TxRaw{ + Tx: types.NewTx(legacyTx), + EfficiencyPercentage: efficiencyPercentage, + } + + return int(endPos), l2Tx, err +} + +// It returns the length of data from the param offset +// ex: +// 0xc0 -> empty data -> 1 byte because it include the 0xc0 +func decodeRLPListLengthFromOffset(txsData []byte, offset int) (uint64, error) { + txDataLength := uint64(len(txsData)) + num := uint64(txsData[offset]) + if num < c0 { // c0 -> is a empty data + log.Debugf("error num < c0 : %d, %d", num, c0) + return 0, fmt.Errorf("first byte of tx (%x) is < 0xc0: %w", num, ErrInvalidRLP) + } + length := num - c0 + if length > shortRlp { // If rlp is bigger than length 55 + // n is the length of the rlp data without the header (1 byte) for example "0xf7" + pos64 := uint64(offset) + lengthInByteOfSize := num - f7 + if (pos64 + headerByteLength + lengthInByteOfSize) > txDataLength { + log.Debug("error not enough data: ") + return 0, fmt.Errorf("not enough data to get length: %w", ErrInvalidRLP) + } + + n, err := strconv.ParseUint(hex.EncodeToString(txsData[pos64+1:pos64+1+lengthInByteOfSize]), hex.Base, hex.BitSize64) // +1 is the header. For example 0xf7 + if err != nil { + log.Debug("error parsing length: ", err) + return 0, fmt.Errorf("error parsing length value: %w", err) + } + // TODO: RLP specifications says length = n ??? that is wrong?? + length = n + num - f7 // num - f7 is the header. For example 0xf7 + } + return length + headerByteLength, nil +} + +func encodeUint32(value uint32) []byte { + data := make([]byte, sizeUInt32) + binary.BigEndian.PutUint32(data, value) + return data +} + +func decodeUint32(txsData []byte, pos int) (int, uint32, error) { + if len(txsData)-pos < sizeUInt32 { + return 0, 0, fmt.Errorf("can't get u32 because not enough data: %w", ErrInvalidBatchV2) + } + return pos + sizeUInt32, binary.BigEndian.Uint32(txsData[pos : pos+sizeUInt32]), nil +} diff --git a/state/encoding_batch_v2_test.go b/state/encoding_batch_v2_test.go new file mode 100644 index 00000000..30b16d23 --- /dev/null +++ b/state/encoding_batch_v2_test.go @@ -0,0 +1,245 @@ +package state + +import ( + "testing" + + "github.com/0xPolygon/cdk/hex" + "github.com/0xPolygon/cdk/log" + "github.com/stretchr/testify/require" +) + +const ( + // changeL2Block + deltaTimeStamp + indexL1InfoTree + codedL2BlockHeader = "0b73e6af6f00000000" + // 2 x [ tx coded in RLP + r,s,v,efficiencyPercentage] + codedRLP2Txs1 = "ee02843b9aca00830186a0944d5cf5032b2a844602278b01199ed191a86c93ff88016345785d8a0000808203e88080bff0e780ba7db409339fd3f71969fa2cbf1b8535f6c725a1499d3318d3ef9c2b6340ddfab84add2c188f9efddb99771db1fe621c981846394ea4f035c85bcdd51bffee03843b9aca00830186a0944d5cf5032b2a844602278b01199ed191a86c93ff88016345785d8a0000808203e880805b346aa02230b22e62f73608de9ff39a162a6c24be9822209c770e3685b92d0756d5316ef954eefc58b068231ccea001fb7ac763ebe03afd009ad71cab36861e1bff" + // 2 x [ tx coded in RLP + r,s,v,efficiencyPercentage] + codedRLP2Txs2 = "ee80843b9aca00830186a0944d5cf5032b2a844602278b01199ed191a86c93ff88016345785d8a0000808203e880801cee7e01dc62f69a12c3510c6d64de04ee6346d84b6a017f3e786c7d87f963e75d8cc91fa983cd6d9cf55fff80d73bd26cd333b0f098acc1e58edb1fd484ad731bffee01843b9aca00830186a0944d5cf5032b2a844602278b01199ed191a86c93ff88016345785d8a0000808203e880803ee20a0764440b016c4a2ee4e7e4eb3a5a97f1e6a6c9f40bf5ecf50f95ff636d63878ddb3e997e519826c7bb26fb7c5950a208e1ec722a9f1c568c4e479b40341cff" + codedL2Block1 = codedL2BlockHeader + codedRLP2Txs1 + codedL2Block2 = codedL2BlockHeader + codedRLP2Txs2 + // Batch 420.000 (Incaberry) from Testnet + realBatchIncaberry = "ee8307c4848402faf08082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a000808205a28080e8c76f8b8ec579362a4ef92dc1c8c372ad4ef6372a20903b3997408743e86239394ad6decc3bc080960b6c62ad78bc09913cba88fd98d595457b3462ed1494b91cffee8307c4858402faf08082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a000808205a28080ed0de9758ff75ae777821e45178da0163c719341188220050cc4ad33048cd9cb272951662ae72269cf611528d591fcf682c8bad4402d98dbac4abc1b2be1ca431cffee8307c4868402faf08082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a000808205a280807c94882ecf48d65b6240e7355c32e7d1a56366fd9571471cb664463ad2afecdd564d24abbea5b38b74dda029cdac3109f199f5e3e683acfbe43e7f27fe23b60b1cffee8307c4878402faf08082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a000808205a280801b5e85cc1b402403a625610d4319558632cffd2b14a15bc031b9ba644ecc48a332bcc608e894b9ede61220767558e1d9e02780b53dbdd9bcc01de0ab2b1742951bffee8307c4888402faf08082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a000808205a2808089eee14afeead54c815953a328ec52d441128e71d08ff75b4e5cd23db6fa67e774ca24e8878368eee5ad4562340edebcfb595395d40f8a5b0301e19ced92af5f1cffee8307c4898402faf08082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a000808205a280807b672107c41caf91cff9061241686dd37e8d1e013d81f7f383b76afa93b7ff85413d4fc4c7e9613340b8fc29aefd0c42a3db6d75340b1bec0b895d324bcfa02e1cffee8307c48a8402faf08082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a000808205a28080efadeca94da405cf44881670bc8b2464d006af41f20517e82339c72d73543c5c4e1e546eea07b4b751e3e2f909bd4026f742684c923bf666985f9a5a1cd91cde1bffee8307c48b8402faf08082520894417a7ba2d8d0060ae6c54fd098590db854b9c1d58609184e72a000808205a2808092ac34e2d6a38c7df5df96c78f9d837daaa7f74352d8c42fe671ef8ba6565ae350648c7e736a0017bf90370e766720c410441f6506765c70fad91ce046c1fad61bfff86c8206838402faf08082803194828f7ceca102de66a6ed4f4b6abee0bd1bd4f9dc80b844095ea7b3000000000000000000000000e907ec70b4efbb28efbf6f4ffb3ae0d34012eaa00000000000000000000000000000000000000000000000011a8297a4dca080008205a28080579cfefee3fa664c8b59190de80454da9642b7647a46b929c9fcc89105b2d5575d28665bef2bb1052db0d36ec1e92bc7503efaa74798fe3630b8867318c20d4e1cff" + realBatchConvertedEtrog = codedL2BlockHeader + realBatchIncaberry +) + +func TestDecodeEmptyBatchV2(t *testing.T) { + batchL2Data, err := hex.DecodeString("") + require.NoError(t, err) + + batch, err := DecodeBatchV2(batchL2Data) + require.NoError(t, err) + require.Equal(t, 0, len(batch.Blocks)) +} + +func TestDecodeBatches(t *testing.T) { + type testCase struct { + name string + batchL2Data string + expectedError error + } + testCases := []testCase{ + { + name: "batch dont start with 0x0b (changeL2Block)", + batchL2Data: "0c", + expectedError: ErrInvalidBatchV2, + }, + { + name: "batch no enough data to decode block.deltaTimestamp", + batchL2Data: "0b010203", + expectedError: ErrInvalidBatchV2, + }, + { + name: "batch no enough data to decode block.index", + batchL2Data: "0b01020304010203", + expectedError: ErrInvalidBatchV2, + }, + { + name: "batch no enough data to decode block.index", + batchL2Data: "0b01020304010203", + expectedError: ErrInvalidBatchV2, + }, + { + name: "valid batch no trx, just L2Block", + batchL2Data: "0b0102030401020304", + expectedError: nil, + }, + { + name: "invalid batch bad RLP codification", + batchL2Data: "0b" + "01020304" + "01020304" + "7f", + expectedError: ErrInvalidRLP, + }, + { + name: "1 block + 2 txs", + batchL2Data: "0b" + "73e6af6f" + "00000000" + codedRLP2Txs1 + codedRLP2Txs2, + expectedError: nil, + }, + { + name: "1 block + 1 txs", + batchL2Data: "0b" + "73e6af6f" + "00000000" + codedRLP2Txs1, + expectedError: nil, + }, + { + name: "1 block + 1 txs, missiging efficiencyPercentage", + batchL2Data: "0b" + "73e6af6f" + "00000000" + codedRLP2Txs1[0:len(codedRLP2Txs1)-2], + expectedError: ErrInvalidBatchV2, + }, + { + name: "real batch converted to etrog", + batchL2Data: realBatchConvertedEtrog, + expectedError: nil, + }, + { + name: "pass a V1 batch(incaberry) must fail", + batchL2Data: realBatchIncaberry, + expectedError: ErrBatchV2DontStartWithChangeL2Block, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + log.Debug("************************ ", tc.name, " ************************") + data, err := hex.DecodeString(tc.batchL2Data) + require.NoError(t, err) + _, err = DecodeBatchV2(data) + if err != nil { + log.Debugf("[%s] %v", tc.name, err) + } + if tc.expectedError != nil { + require.ErrorIs(t, err, tc.expectedError) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestDecodeBatchV2(t *testing.T) { + batchL2Data, err := hex.DecodeString(codedL2Block1) + require.NoError(t, err) + batchL2Data2, err := hex.DecodeString(codedL2Block2) + require.NoError(t, err) + batch := append(batchL2Data, batchL2Data2...) + decodedBatch, err := DecodeBatchV2(batch) + require.NoError(t, err) + require.Equal(t, 2, len(decodedBatch.Blocks)) + require.Equal(t, uint32(0x73e6af6f), decodedBatch.Blocks[0].DeltaTimestamp) + require.Equal(t, uint32(0x00000000), decodedBatch.Blocks[0].IndexL1InfoTree) +} + +func TestDecodeRLPLength(t *testing.T) { + type testCase struct { + name string + data string + expectedError error + expectedResult uint64 + } + testCases := []testCase{ + { + name: "must start >= 0xc0", + data: "bf", + expectedError: ErrInvalidRLP, + }, + { + name: "shortRLP: c0 -> len=0", + data: "c0", + expectedResult: 1, + }, + { + name: "shortRLP: c1 -> len=1", + data: "c1", + expectedResult: 2, // 1 byte header + 1 byte of data + }, + { + name: "shortRLP: byte>0xf7", + data: "f7", + expectedResult: 56, // 1 byte header + 55 bytes of data + }, + { + name: "longRLP: f8: 1 extra byte, missing data", + data: "f8", + expectedError: ErrInvalidRLP, + }, + { + name: "longRLP: f8:size is stored in next byte ->0x01 (code add the length of bytes of the size??)", + data: "f8" + "01", + expectedResult: 3, // 2 bytes of header + 1 byte of data + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + log.Debug("************************ ", tc.name, " ************************") + data, err := hex.DecodeString(tc.data) + require.NoError(t, err) + length, err := decodeRLPListLengthFromOffset(data, 0) + if err != nil { + log.Debugf("[%s] %v", tc.name, err) + } + if tc.expectedError != nil { + require.ErrorIs(t, err, tc.expectedError) + } else { + require.NoError(t, err) + require.Equal(t, tc.expectedResult, length) + } + }) + } +} + +func TestEncodeBatchV2(t *testing.T) { + block1 := L2BlockRaw{ + ChangeL2BlockHeader: ChangeL2BlockHeader{ + DeltaTimestamp: 123, + IndexL1InfoTree: 456, + }, + Transactions: []L2TxRaw{}, + } + block2 := L2BlockRaw{ + ChangeL2BlockHeader: ChangeL2BlockHeader{ + DeltaTimestamp: 789, + IndexL1InfoTree: 101112, + }, + Transactions: []L2TxRaw{}, + } + blocks := []L2BlockRaw{block1, block2} + + expectedBatchData := []byte{ + 0xb, 0x0, 0x0, 0x0, 0x7b, 0x0, 0x0, 0x1, 0xc8, 0xb, 0x0, 0x0, 0x3, 0x15, 0x0, 0x1, 0x8a, 0xf8, + } + + batchData, err := EncodeBatchV2(&BatchRawV2{Blocks: blocks}) + require.NoError(t, err) + require.Equal(t, expectedBatchData, batchData) +} + +func TestDecodeEncodeBatchV2(t *testing.T) { + batchL2Data, err := hex.DecodeString(codedL2Block1 + codedL2Block2) + require.NoError(t, err) + decodedBatch, err := DecodeBatchV2(batchL2Data) + require.NoError(t, err) + require.Equal(t, 2, len(decodedBatch.Blocks)) + encoded, err := EncodeBatchV2(decodedBatch) + require.NoError(t, err) + require.Equal(t, batchL2Data, encoded) +} + +func TestEncodeEmptyBatchV2Fails(t *testing.T) { + l2Batch := BatchRawV2{} + _, err := EncodeBatchV2(&l2Batch) + require.ErrorIs(t, err, ErrInvalidBatchV2) + _, err = EncodeBatchV2(nil) + require.ErrorIs(t, err, ErrInvalidBatchV2) +} + +func TestDecodeForcedBatchV2(t *testing.T) { + batchL2Data, err := hex.DecodeString(codedRLP2Txs1) + require.NoError(t, err) + decodedBatch, err := DecodeForcedBatchV2(batchL2Data) + require.NoError(t, err) + require.Equal(t, 2, len(decodedBatch.Transactions)) +} + +func TestDecodeForcedBatchV2WithRegularBatch(t *testing.T) { + batchL2Data, err := hex.DecodeString(codedL2Block1) + require.NoError(t, err) + _, err = DecodeForcedBatchV2(batchL2Data) + require.Error(t, err) +} diff --git a/state/errors.go b/state/errors.go new file mode 100644 index 00000000..5a394a24 --- /dev/null +++ b/state/errors.go @@ -0,0 +1,68 @@ +package state + +import ( + "errors" + "fmt" +) + +var ( + // ErrInvalidBatchHeader indicates the batch header is invalid + ErrInvalidBatchHeader = errors.New("invalid batch header") + // ErrUnexpectedBatch indicates that the batch is unexpected + ErrUnexpectedBatch = errors.New("unexpected batch") + // ErrStateNotSynchronized indicates the state database may be empty + ErrStateNotSynchronized = errors.New("state not synchronized") + // ErrNotFound indicates an object has not been found for the search criteria used + ErrNotFound = errors.New("object not found") + // ErrNilDBTransaction indicates the db transaction has not been properly initialized + ErrNilDBTransaction = errors.New("database transaction not properly initialized") + // ErrAlreadyInitializedDBTransaction indicates the db transaction was already initialized + ErrAlreadyInitializedDBTransaction = errors.New("database transaction already initialized") + // ErrNotEnoughIntrinsicGas indicates the gas is not enough to cover the intrinsic gas cost + ErrNotEnoughIntrinsicGas = fmt.Errorf("not enough gas supplied for intrinsic gas costs") + // ErrParsingExecutorTrace indicates an error occurred while parsing the executor trace + ErrParsingExecutorTrace = fmt.Errorf("error while parsing executor trace") + // ErrInvalidBatchNumber indicates the provided batch number is not the latest in db + ErrInvalidBatchNumber = errors.New("provided batch number is not latest") + // ErrLastBatchShouldBeClosed indicates that last batch needs to be closed before adding a new one + ErrLastBatchShouldBeClosed = errors.New("last batch needs to be closed before adding a new one") + // ErrBatchAlreadyClosed indicates that batch is already closed + ErrBatchAlreadyClosed = errors.New("batch is already closed") + // ErrClosingBatchWithoutTxs indicates that the batch attempted to close does not have txs. + ErrClosingBatchWithoutTxs = errors.New("can not close a batch without transactions") + // ErrTimestampGE indicates that timestamp needs to be greater or equal + ErrTimestampGE = errors.New("timestamp needs to be greater or equal") + // ErrDBTxNil indicates that the method requires a dbTx that is not nil + ErrDBTxNil = errors.New("the method requires a dbTx that is not nil") + // ErrExistingTxGreaterThanProcessedTx indicates that we have more txs stored + // in db than the txs we want to process. + ErrExistingTxGreaterThanProcessedTx = errors.New("there are more transactions in the database than in the processed transaction set") + // ErrOutOfOrderProcessedTx indicates the the processed transactions of an + // ongoing batch are not in the same order as the transactions stored in the + // database for the same batch. + ErrOutOfOrderProcessedTx = errors.New("the processed transactions are not in the same order as the stored transactions") + // ErrInsufficientFundsForTransfer is returned if the transaction sender doesn't + // have enough funds for transfer(topmost call only). + ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer") + // ErrExecutorNil indicates that the method requires an executor that is not nil + ErrExecutorNil = errors.New("the method requires an executor that is not nil") + // ErrStateTreeNil indicates that the method requires a state tree that is not nil + ErrStateTreeNil = errors.New("the method requires a state tree that is not nil") + // ErrUnsupportedDuration is returned if the provided unit for a time + // interval is not supported by our conversion mechanism. + ErrUnsupportedDuration = errors.New("unsupported time duration") + // ErrInvalidData is the error when the raw txs is unexpected + ErrInvalidData = errors.New("invalid data") + // ErrInvalidBlockRange returned when the selected block range is invalid, generally + // because the toBlock is bigger than the fromBlock + ErrInvalidBlockRange = errors.New("invalid block range") + // ErrMaxLogsCountLimitExceeded returned when the number of logs is bigger than the + // configured limit + ErrMaxLogsCountLimitExceeded = errors.New("query returned more than %v results") + // ErrMaxLogsBlockRangeLimitExceeded returned when the range between block number range + // to filter logs is bigger than the configured limit + ErrMaxLogsBlockRangeLimitExceeded = errors.New("logs are limited to a %v block range") + // ErrMaxNativeBlockHashBlockRangeLimitExceeded returned when the range between block number range + // to filter native block hashes is bigger than the configured limit + ErrMaxNativeBlockHashBlockRangeLimitExceeded = errors.New("native block hashes are limited to a %v block range") +) diff --git a/state/forkid.go b/state/forkid.go new file mode 100644 index 00000000..27617313 --- /dev/null +++ b/state/forkid.go @@ -0,0 +1,12 @@ +package state + +const ( + // FORKID_BLUEBERRY is the fork id 4 + FORKID_BLUEBERRY = 4 + // FORKID_DRAGONFRUIT is the fork id 5 + FORKID_DRAGONFRUIT = 5 + // FORKID_INCABERRY is the fork id 6 + FORKID_INCABERRY = 6 + // FORKID_ETROG is the fork id 7 + FORKID_ETROG = 7 +) diff --git a/state/genesis.go b/state/genesis.go new file mode 100644 index 00000000..368a56eb --- /dev/null +++ b/state/genesis.go @@ -0,0 +1,26 @@ +package state + +import ( + "github.com/ethereum/go-ethereum/common" +) + +// Genesis contains the information to populate state on creation +type Genesis struct { + // BlockNumber is the block number where the polygonZKEVM smc was deployed on L1 + BlockNumber uint64 + // Root hash of the genesis block + Root common.Hash + // Actions is the data to populate into the state trie + Actions []*GenesisAction +} + +// GenesisAction represents one of the values set on the SMT during genesis. +type GenesisAction struct { + Address string `json:"address"` + Type int `json:"type"` + StoragePosition string `json:"storagePosition"` + Bytecode string `json:"bytecode"` + Key string `json:"key"` + Value string `json:"value"` + Root string `json:"root"` +} diff --git a/state/helper.go b/state/helper.go new file mode 100644 index 00000000..efb1b1df --- /dev/null +++ b/state/helper.go @@ -0,0 +1,188 @@ +package state + +import ( + "fmt" + "math/big" + "strconv" + + "github.com/0xPolygon/cdk/hex" + "github.com/0xPolygon/cdk/log" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" +) + +const ( + double = 2 + ether155V = 27 + etherPre155V = 35 + // MaxEffectivePercentage is the maximum value that can be used as effective percentage + MaxEffectivePercentage = uint8(255) + // Decoding constants + headerByteLength uint64 = 1 + sLength uint64 = 32 + rLength uint64 = 32 + vLength uint64 = 1 + c0 uint64 = 192 // 192 is c0. This value is defined by the rlp protocol + ff uint64 = 255 // max value of rlp header + shortRlp uint64 = 55 // length of the short rlp codification + f7 uint64 = 247 // 192 + 55 = c0 + shortRlp + + // EfficiencyPercentageByteLength is the length of the effective percentage in bytes + EfficiencyPercentageByteLength uint64 = 1 +) + +func prepareRLPTxData(tx *types.Transaction) ([]byte, error) { + v, r, s := tx.RawSignatureValues() + sign := 1 - (v.Uint64() & 1) + + nonce, gasPrice, gas, to, value, data, chainID := tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), tx.ChainId() + + rlpFieldsToEncode := []interface{}{ + nonce, + gasPrice, + gas, + to, + value, + data, + } + + if !IsPreEIP155Tx(tx) { + rlpFieldsToEncode = append(rlpFieldsToEncode, chainID) + rlpFieldsToEncode = append(rlpFieldsToEncode, uint(0)) + rlpFieldsToEncode = append(rlpFieldsToEncode, uint(0)) + } + + txCodedRlp, err := rlp.EncodeToBytes(rlpFieldsToEncode) + if err != nil { + return nil, err + } + + newV := new(big.Int).Add(big.NewInt(ether155V), big.NewInt(int64(sign))) + newRPadded := fmt.Sprintf("%064s", r.Text(hex.Base)) + newSPadded := fmt.Sprintf("%064s", s.Text(hex.Base)) + newVPadded := fmt.Sprintf("%02s", newV.Text(hex.Base)) + txData, err := hex.DecodeString(hex.EncodeToString(txCodedRlp) + newRPadded + newSPadded + newVPadded) + if err != nil { + return nil, err + } + return txData, nil +} + +// DecodeTxs extracts Transactions for its encoded form +func DecodeTxs(txsData []byte, forkID uint64) ([]*types.Transaction, []byte, []uint8, error) { + // Process coded txs + var pos uint64 + var txs []*types.Transaction + var efficiencyPercentages []uint8 + txDataLength := uint64(len(txsData)) + if txDataLength == 0 { + return txs, txsData, nil, nil + } + for pos < txDataLength { + num, err := strconv.ParseUint(hex.EncodeToString(txsData[pos:pos+1]), hex.Base, hex.BitSize64) + if err != nil { + log.Debug("error parsing header length: ", err) + return []*types.Transaction{}, txsData, []uint8{}, err + } + // First byte is the length and must be ignored + if num < c0 { + log.Debugf("error num < c0 : %d, %d", num, c0) + return []*types.Transaction{}, txsData, []uint8{}, ErrInvalidData + } + length := num - c0 + if length > shortRlp { // If rlp is bigger than length 55 + // n is the length of the rlp data without the header (1 byte) for example "0xf7" + if (pos + 1 + num - f7) > txDataLength { + log.Debug("error parsing length: ", err) + return []*types.Transaction{}, txsData, []uint8{}, err + } + n, err := strconv.ParseUint(hex.EncodeToString(txsData[pos+1:pos+1+num-f7]), hex.Base, hex.BitSize64) // +1 is the header. For example 0xf7 + if err != nil { + log.Debug("error parsing length: ", err) + return []*types.Transaction{}, txsData, []uint8{}, err + } + if n+num < f7 { + log.Debug("error n + num < f7: ", err) + return []*types.Transaction{}, txsData, []uint8{}, ErrInvalidData + } + length = n + num - f7 // num - f7 is the header. For example 0xf7 + } + + endPos := pos + length + rLength + sLength + vLength + headerByteLength + + if forkID >= FORKID_DRAGONFRUIT { + endPos += EfficiencyPercentageByteLength + } + + if endPos > txDataLength { + err := fmt.Errorf("endPos %d is bigger than txDataLength %d", endPos, txDataLength) + log.Debug("error parsing header: ", err) + return []*types.Transaction{}, txsData, []uint8{}, ErrInvalidData + } + + if endPos < pos { + err := fmt.Errorf("endPos %d is smaller than pos %d", endPos, pos) + log.Debug("error parsing header: ", err) + return []*types.Transaction{}, txsData, []uint8{}, ErrInvalidData + } + + if endPos < pos { + err := fmt.Errorf("endPos %d is smaller than pos %d", endPos, pos) + log.Debug("error parsing header: ", err) + return []*types.Transaction{}, txsData, []uint8{}, ErrInvalidData + } + + fullDataTx := txsData[pos:endPos] + dataStart := pos + length + headerByteLength + txInfo := txsData[pos:dataStart] + rData := txsData[dataStart : dataStart+rLength] + sData := txsData[dataStart+rLength : dataStart+rLength+sLength] + vData := txsData[dataStart+rLength+sLength : dataStart+rLength+sLength+vLength] + + if forkID >= FORKID_DRAGONFRUIT { + efficiencyPercentage := txsData[dataStart+rLength+sLength+vLength : endPos] + efficiencyPercentages = append(efficiencyPercentages, efficiencyPercentage[0]) + } + + pos = endPos + + // Decode rlpFields + var rlpFields [][]byte + err = rlp.DecodeBytes(txInfo, &rlpFields) + if err != nil { + log.Error("error decoding tx Bytes: ", err, ". fullDataTx: ", hex.EncodeToString(fullDataTx), "\n tx: ", hex.EncodeToString(txInfo), "\n Txs received: ", hex.EncodeToString(txsData)) + return []*types.Transaction{}, txsData, []uint8{}, ErrInvalidData + } + + legacyTx, err := RlpFieldsToLegacyTx(rlpFields, vData, rData, sData) + if err != nil { + log.Debug("error creating tx from rlp fields: ", err, ". fullDataTx: ", hex.EncodeToString(fullDataTx), "\n tx: ", hex.EncodeToString(txInfo), "\n Txs received: ", hex.EncodeToString(txsData)) + return []*types.Transaction{}, txsData, []uint8{}, err + } + + tx := types.NewTx(legacyTx) + txs = append(txs, tx) + } + return txs, txsData, efficiencyPercentages, nil +} + +// DecodeTx decodes a string rlp tx representation into a types.Transaction instance +func DecodeTx(encodedTx string) (*types.Transaction, error) { + b, err := hex.DecodeHex(encodedTx) + if err != nil { + return nil, err + } + + tx := new(types.Transaction) + if err := tx.UnmarshalBinary(b); err != nil { + return nil, err + } + return tx, nil +} + +// IsPreEIP155Tx checks if the tx is a tx that has a chainID as zero and +// V field is either 27 or 28 +func IsPreEIP155Tx(tx *types.Transaction) bool { + v, _, _ := tx.RawSignatureValues() + return tx.ChainId().Uint64() == 0 && (v.Uint64() == 27 || v.Uint64() == 28) +} diff --git a/state/interfaces.go b/state/interfaces.go new file mode 100644 index 00000000..a0c38337 --- /dev/null +++ b/state/interfaces.go @@ -0,0 +1,30 @@ +package state + +import ( + "context" + + "github.com/jackc/pgconn" + "github.com/jackc/pgx/v4" +) + +type storage interface { + Exec(ctx context.Context, sql string, arguments ...interface{}) (commandTag pgconn.CommandTag, err error) + Query(ctx context.Context, sql string, args ...interface{}) (pgx.Rows, error) + QueryRow(ctx context.Context, sql string, args ...interface{}) pgx.Row + Begin(ctx context.Context) (pgx.Tx, error) + AddSequence(ctx context.Context, sequence Sequence, dbTx pgx.Tx) error + CheckProofContainsCompleteSequences(ctx context.Context, proof *Proof, dbTx pgx.Tx) (bool, error) + GetProofReadyToVerify(ctx context.Context, lastVerfiedBatchNumber uint64, dbTx pgx.Tx) (*Proof, error) + GetProofsToAggregate(ctx context.Context, dbTx pgx.Tx) (*Proof, *Proof, error) + AddGeneratedProof(ctx context.Context, proof *Proof, dbTx pgx.Tx) error + UpdateGeneratedProof(ctx context.Context, proof *Proof, dbTx pgx.Tx) error + DeleteGeneratedProofs(ctx context.Context, batchNumber uint64, batchNumberFinal uint64, dbTx pgx.Tx) error + DeleteUngeneratedProofs(ctx context.Context, dbTx pgx.Tx) error + CleanupGeneratedProofs(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error + CleanupLockedProofs(ctx context.Context, duration string, dbTx pgx.Tx) (int64, error) + CheckProofExistsForBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (bool, error) + AddBatch(ctx context.Context, batch *Batch, datastream []byte, dbTx pgx.Tx) error + GetBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*Batch, []byte, error) + DeleteBatchesOlderThanBatchNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error + DeleteBatchesNewerThanBatchNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error +} diff --git a/state/pgstatestorage/batch.go b/state/pgstatestorage/batch.go new file mode 100644 index 00000000..ab8414d1 --- /dev/null +++ b/state/pgstatestorage/batch.go @@ -0,0 +1,46 @@ +package pgstatestorage + +import ( + "context" + + "github.com/0xPolygon/cdk/state" + "github.com/ethereum/go-ethereum/common" + "github.com/jackc/pgx/v4" +) + +// AddBatch stores a batch +func (p *PostgresStorage) AddBatch(ctx context.Context, batch *state.Batch, datastream []byte, dbTx pgx.Tx) error { + const addInputHashSQL = "INSERT INTO aggregator.batch (batch_num, batch, datastream) VALUES ($1, $2, $3) ON CONFLICT (batch_num) DO UPDATE SET batch = $2, datastream = $3" + e := p.getExecQuerier(dbTx) + _, err := e.Exec(ctx, addInputHashSQL, batch.BatchNumber, &batch, common.Bytes2Hex(datastream)) + return err +} + +// GetBatch gets a batch by a given batch number +func (p *PostgresStorage) GetBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.Batch, []byte, error) { + const getInputHashSQL = "SELECT batch, datastream FROM aggregator.batch WHERE batch_num = $1" + e := p.getExecQuerier(dbTx) + var batch *state.Batch + var streamStr string + err := e.QueryRow(ctx, getInputHashSQL, batchNumber).Scan(&batch, &streamStr) + if err != nil { + return nil, nil, err + } + return batch, common.Hex2Bytes(streamStr), nil +} + +// DeleteBatchesOlderThanBatchNumber deletes batches previous to the given batch number +func (p *PostgresStorage) DeleteBatchesOlderThanBatchNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error { + const deleteBatchesSQL = "DELETE FROM aggregator.batch WHERE batch_num < $1" + e := p.getExecQuerier(dbTx) + _, err := e.Exec(ctx, deleteBatchesSQL, batchNumber) + return err +} + +// DeleteBatchesNewerThanBatchNumber deletes batches previous to the given batch number +func (p *PostgresStorage) DeleteBatchesNewerThanBatchNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error { + const deleteBatchesSQL = "DELETE FROM aggregator.batch WHERE batch_num > $1" + e := p.getExecQuerier(dbTx) + _, err := e.Exec(ctx, deleteBatchesSQL, batchNumber) + return err +} diff --git a/state/pgstatestorage/interfaces.go b/state/pgstatestorage/interfaces.go new file mode 100644 index 00000000..e5f7402b --- /dev/null +++ b/state/pgstatestorage/interfaces.go @@ -0,0 +1,14 @@ +package pgstatestorage + +import ( + "context" + + "github.com/jackc/pgconn" + "github.com/jackc/pgx/v4" +) + +type ExecQuerier interface { + Exec(ctx context.Context, sql string, arguments ...interface{}) (commandTag pgconn.CommandTag, err error) + Query(ctx context.Context, sql string, args ...interface{}) (pgx.Rows, error) + QueryRow(ctx context.Context, sql string, args ...interface{}) pgx.Row +} diff --git a/state/pgstatestorage/pgstatestorage.go b/state/pgstatestorage/pgstatestorage.go new file mode 100644 index 00000000..7e294c6b --- /dev/null +++ b/state/pgstatestorage/pgstatestorage.go @@ -0,0 +1,29 @@ +package pgstatestorage + +import ( + "github.com/0xPolygon/cdk/state" + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" +) + +// PostgresStorage implements the Storage interface +type PostgresStorage struct { + cfg state.Config + *pgxpool.Pool +} + +// NewPostgresStorage creates a new StateDB +func NewPostgresStorage(cfg state.Config, db *pgxpool.Pool) *PostgresStorage { + return &PostgresStorage{ + cfg, + db, + } +} + +// getExecQuerier determines which execQuerier to use, dbTx or the main pgxpool +func (p *PostgresStorage) getExecQuerier(dbTx pgx.Tx) ExecQuerier { + if dbTx != nil { + return dbTx + } + return p +} diff --git a/state/pgstatestorage/proof.go b/state/pgstatestorage/proof.go new file mode 100644 index 00000000..98668a44 --- /dev/null +++ b/state/pgstatestorage/proof.go @@ -0,0 +1,227 @@ +package pgstatestorage + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/0xPolygon/cdk/state" + "github.com/jackc/pgx/v4" +) + +// CheckProofExistsForBatch checks if the batch is already included in any proof +func (p *PostgresStorage) CheckProofExistsForBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (bool, error) { + const checkProofExistsForBatchSQL = ` + SELECT EXISTS (SELECT 1 FROM aggregator.proof p WHERE $1 >= p.batch_num AND $1 <= p.batch_num_final) + ` + e := p.getExecQuerier(dbTx) + var exists bool + err := e.QueryRow(ctx, checkProofExistsForBatchSQL, batchNumber).Scan(&exists) + if err != nil && !errors.Is(err, pgx.ErrNoRows) { + return exists, err + } + return exists, nil +} + +// CheckProofContainsCompleteSequences checks if a recursive proof contains complete sequences +func (p *PostgresStorage) CheckProofContainsCompleteSequences(ctx context.Context, proof *state.Proof, dbTx pgx.Tx) (bool, error) { + const getProofContainsCompleteSequencesSQL = ` + SELECT EXISTS (SELECT 1 FROM aggregator.sequence s1 WHERE s1.from_batch_num = $1) AND + EXISTS (SELECT 1 FROM aggregator.sequence s2 WHERE s2.to_batch_num = $2) + ` + e := p.getExecQuerier(dbTx) + var exists bool + err := e.QueryRow(ctx, getProofContainsCompleteSequencesSQL, proof.BatchNumber, proof.BatchNumberFinal).Scan(&exists) + if err != nil && !errors.Is(err, pgx.ErrNoRows) { + return exists, err + } + return exists, nil +} + +// GetProofReadyToVerify return the proof that is ready to verify +func (p *PostgresStorage) GetProofReadyToVerify(ctx context.Context, lastVerfiedBatchNumber uint64, dbTx pgx.Tx) (*state.Proof, error) { + const getProofReadyToVerifySQL = ` + SELECT + p.batch_num, + p.batch_num_final, + p.proof, + p.proof_id, + p.input_prover, + p.prover, + p.prover_id, + p.generating_since, + p.created_at, + p.updated_at + FROM aggregator.proof p + WHERE batch_num = $1 AND generating_since IS NULL AND + EXISTS (SELECT 1 FROM aggregator.sequence s1 WHERE s1.from_batch_num = p.batch_num) AND + EXISTS (SELECT 1 FROM aggregator.sequence s2 WHERE s2.to_batch_num = p.batch_num_final) + ` + + var proof *state.Proof = &state.Proof{} + + e := p.getExecQuerier(dbTx) + row := e.QueryRow(ctx, getProofReadyToVerifySQL, lastVerfiedBatchNumber+1) + err := row.Scan(&proof.BatchNumber, &proof.BatchNumberFinal, &proof.Proof, &proof.ProofID, &proof.InputProver, &proof.Prover, &proof.ProverID, &proof.GeneratingSince, &proof.CreatedAt, &proof.UpdatedAt) + + if errors.Is(err, pgx.ErrNoRows) { + return nil, state.ErrNotFound + } else if err != nil { + return nil, err + } + + return proof, err +} + +// GetProofsToAggregate return the next to proof that it is possible to aggregate +func (p *PostgresStorage) GetProofsToAggregate(ctx context.Context, dbTx pgx.Tx) (*state.Proof, *state.Proof, error) { + var ( + proof1 *state.Proof = &state.Proof{} + proof2 *state.Proof = &state.Proof{} + ) + + // TODO: add comments to explain the query + const getProofsToAggregateSQL = ` + SELECT + p1.batch_num as p1_batch_num, + p1.batch_num_final as p1_batch_num_final, + p1.proof as p1_proof, + p1.proof_id as p1_proof_id, + p1.input_prover as p1_input_prover, + p1.prover as p1_prover, + p1.prover_id as p1_prover_id, + p1.generating_since as p1_generating_since, + p1.created_at as p1_created_at, + p1.updated_at as p1_updated_at, + p2.batch_num as p2_batch_num, + p2.batch_num_final as p2_batch_num_final, + p2.proof as p2_proof, + p2.proof_id as p2_proof_id, + p2.input_prover as p2_input_prover, + p2.prover as p2_prover, + p2.prover_id as p2_prover_id, + p2.generating_since as p2_generating_since, + p2.created_at as p2_created_at, + p2.updated_at as p2_updated_at + FROM aggregator.proof p1 INNER JOIN aggregator.proof p2 ON p1.batch_num_final = p2.batch_num - 1 + WHERE p1.generating_since IS NULL AND p2.generating_since IS NULL AND + p1.proof IS NOT NULL AND p2.proof IS NOT NULL AND + ( + EXISTS ( + SELECT 1 FROM aggregator.sequence s + WHERE p1.batch_num >= s.from_batch_num AND p1.batch_num <= s.to_batch_num AND + p1.batch_num_final >= s.from_batch_num AND p1.batch_num_final <= s.to_batch_num AND + p2.batch_num >= s.from_batch_num AND p2.batch_num <= s.to_batch_num AND + p2.batch_num_final >= s.from_batch_num AND p2.batch_num_final <= s.to_batch_num + ) + OR + ( + EXISTS ( SELECT 1 FROM aggregator.sequence s WHERE p1.batch_num = s.from_batch_num) AND + EXISTS ( SELECT 1 FROM aggregator.sequence s WHERE p1.batch_num_final = s.to_batch_num) AND + EXISTS ( SELECT 1 FROM aggregator.sequence s WHERE p2.batch_num = s.from_batch_num) AND + EXISTS ( SELECT 1 FROM aggregator.sequence s WHERE p2.batch_num_final = s.to_batch_num) + ) + ) + ORDER BY p1.batch_num ASC + LIMIT 1 + ` + + e := p.getExecQuerier(dbTx) + row := e.QueryRow(ctx, getProofsToAggregateSQL) + err := row.Scan( + &proof1.BatchNumber, &proof1.BatchNumberFinal, &proof1.Proof, &proof1.ProofID, &proof1.InputProver, &proof1.Prover, &proof1.ProverID, &proof1.GeneratingSince, &proof1.CreatedAt, &proof1.UpdatedAt, + &proof2.BatchNumber, &proof2.BatchNumberFinal, &proof2.Proof, &proof2.ProofID, &proof2.InputProver, &proof2.Prover, &proof2.ProverID, &proof2.GeneratingSince, &proof2.CreatedAt, &proof2.UpdatedAt) + + if errors.Is(err, pgx.ErrNoRows) { + return nil, nil, state.ErrNotFound + } else if err != nil { + return nil, nil, err + } + + return proof1, proof2, err +} + +// AddGeneratedProof adds a generated proof to the storage +func (p *PostgresStorage) AddGeneratedProof(ctx context.Context, proof *state.Proof, dbTx pgx.Tx) error { + const addGeneratedProofSQL = "INSERT INTO aggregator.proof (batch_num, batch_num_final, proof, proof_id, input_prover, prover, prover_id, generating_since, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)" + e := p.getExecQuerier(dbTx) + now := time.Now().UTC().Round(time.Microsecond) + _, err := e.Exec(ctx, addGeneratedProofSQL, proof.BatchNumber, proof.BatchNumberFinal, proof.Proof, proof.ProofID, proof.InputProver, proof.Prover, proof.ProverID, proof.GeneratingSince, now, now) + return err +} + +// UpdateGeneratedProof updates a generated proof in the storage +func (p *PostgresStorage) UpdateGeneratedProof(ctx context.Context, proof *state.Proof, dbTx pgx.Tx) error { + const addGeneratedProofSQL = "UPDATE aggregator.proof SET proof = $3, proof_id = $4, input_prover = $5, prover = $6, prover_id = $7, generating_since = $8, updated_at = $9 WHERE batch_num = $1 AND batch_num_final = $2" + e := p.getExecQuerier(dbTx) + now := time.Now().UTC().Round(time.Microsecond) + _, err := e.Exec(ctx, addGeneratedProofSQL, proof.BatchNumber, proof.BatchNumberFinal, proof.Proof, proof.ProofID, proof.InputProver, proof.Prover, proof.ProverID, proof.GeneratingSince, now) + return err +} + +// DeleteGeneratedProofs deletes from the storage the generated proofs falling +// inside the batch numbers range. +func (p *PostgresStorage) DeleteGeneratedProofs(ctx context.Context, batchNumber uint64, batchNumberFinal uint64, dbTx pgx.Tx) error { + const deleteGeneratedProofSQL = "DELETE FROM aggregator.proof WHERE batch_num >= $1 AND batch_num_final <= $2" + e := p.getExecQuerier(dbTx) + _, err := e.Exec(ctx, deleteGeneratedProofSQL, batchNumber, batchNumberFinal) + return err +} + +// CleanupGeneratedProofs deletes from the storage the generated proofs up to +// the specified batch number included. +func (p *PostgresStorage) CleanupGeneratedProofs(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) error { + const deleteGeneratedProofSQL = "DELETE FROM aggregator.proof WHERE batch_num_final <= $1" + e := p.getExecQuerier(dbTx) + _, err := e.Exec(ctx, deleteGeneratedProofSQL, batchNumber) + return err +} + +// CleanupLockedProofs deletes from the storage the proofs locked in generating +// state for more than the provided threshold. +func (p *PostgresStorage) CleanupLockedProofs(ctx context.Context, duration string, dbTx pgx.Tx) (int64, error) { + interval, err := toPostgresInterval(duration) + if err != nil { + return 0, err + } + sql := fmt.Sprintf("DELETE FROM aggregator.proof WHERE generating_since < (NOW() - interval '%s')", interval) + e := p.getExecQuerier(dbTx) + ct, err := e.Exec(ctx, sql) + if err != nil { + return 0, err + } + return ct.RowsAffected(), nil +} + +// DeleteUngeneratedProofs deletes ungenerated proofs. +// This method is meant to be use during aggregator boot-up sequence +func (p *PostgresStorage) DeleteUngeneratedProofs(ctx context.Context, dbTx pgx.Tx) error { + const deleteUngeneratedProofsSQL = "DELETE FROM aggregator.proof WHERE generating_since IS NOT NULL" + e := p.getExecQuerier(dbTx) + _, err := e.Exec(ctx, deleteUngeneratedProofsSQL) + return err +} + +func toPostgresInterval(duration string) (string, error) { + unit := duration[len(duration)-1] + var pgUnit string + + switch unit { + case 's': + pgUnit = "second" + case 'm': + pgUnit = "minute" + case 'h': + pgUnit = "hour" + default: + return "", state.ErrUnsupportedDuration + } + + isMoreThanOne := duration[0] != '1' || len(duration) > 2 //nolint:gomnd + if isMoreThanOne { + pgUnit = pgUnit + "s" + } + + return fmt.Sprintf("%s %s", duration[:len(duration)-1], pgUnit), nil +} diff --git a/state/pgstatestorage/sequence.go b/state/pgstatestorage/sequence.go new file mode 100644 index 00000000..12b19f7e --- /dev/null +++ b/state/pgstatestorage/sequence.go @@ -0,0 +1,17 @@ +package pgstatestorage + +import ( + "context" + + "github.com/0xPolygon/cdk/state" + "github.com/jackc/pgx/v4" +) + +// AddSequence stores the sequence information to allow the aggregator verify sequences. +func (p *PostgresStorage) AddSequence(ctx context.Context, sequence state.Sequence, dbTx pgx.Tx) error { + const addSequenceSQL = "INSERT INTO aggregator.sequence (from_batch_num, to_batch_num) VALUES($1, $2) ON CONFLICT (from_batch_num) DO UPDATE SET to_batch_num = $2" + + e := p.getExecQuerier(dbTx) + _, err := e.Exec(ctx, addSequenceSQL, sequence.FromBatchNumber, sequence.ToBatchNumber) + return err +} diff --git a/state/state.go b/state/state.go new file mode 100644 index 00000000..c9235ce4 --- /dev/null +++ b/state/state.go @@ -0,0 +1,40 @@ +package state + +import ( + "context" + + "github.com/ethereum/go-ethereum/common" + "github.com/jackc/pgx/v4" +) + +var ( + // ZeroHash is the hash 0x0000000000000000000000000000000000000000000000000000000000000000 + ZeroHash = common.Hash{} + // ZeroAddress is the address 0x0000000000000000000000000000000000000000 + ZeroAddress = common.Address{} +) + +// State is an implementation of the state +type State struct { + cfg Config + storage +} + +// NewState creates a new State +func NewState(cfg Config, storage storage) *State { + state := &State{ + cfg: cfg, + storage: storage, + } + + return state +} + +// BeginStateTransaction starts a state transaction +func (s *State) BeginStateTransaction(ctx context.Context) (pgx.Tx, error) { + tx, err := s.Begin(ctx) + if err != nil { + return nil, err + } + return tx, nil +} diff --git a/state/transaction.go b/state/transaction.go new file mode 100644 index 00000000..14ec84a6 --- /dev/null +++ b/state/transaction.go @@ -0,0 +1,88 @@ +package state + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// GetSender gets the sender from the transaction's signature +func GetSender(tx *types.Transaction) (common.Address, error) { + signer := types.NewEIP155Signer(tx.ChainId()) + sender, err := signer.Sender(tx) + if err != nil { + return common.Address{}, err + } + return sender, nil +} + +// RlpFieldsToLegacyTx parses the rlp fields slice into a type.LegacyTx +// in this specific order: +// +// required fields: +// [0] Nonce uint64 +// [1] GasPrice *big.Int +// [2] Gas uint64 +// [3] To *common.Address +// [4] Value *big.Int +// [5] Data []byte +// +// optional fields: +// [6] V *big.Int +// [7] R *big.Int +// [8] S *big.Int +func RlpFieldsToLegacyTx(fields [][]byte, v, r, s []byte) (tx *types.LegacyTx, err error) { + const ( + fieldsSizeWithoutChainID = 6 + fieldsSizeWithChainID = 7 + ) + + if len(fields) < fieldsSizeWithoutChainID { + return nil, types.ErrTxTypeNotSupported + } + + nonce := big.NewInt(0).SetBytes(fields[0]).Uint64() + gasPrice := big.NewInt(0).SetBytes(fields[1]) + gas := big.NewInt(0).SetBytes(fields[2]).Uint64() + var to *common.Address + + if fields[3] != nil && len(fields[3]) != 0 { + tmp := common.BytesToAddress(fields[3]) + to = &tmp + } + value := big.NewInt(0).SetBytes(fields[4]) + data := fields[5] + + txV := big.NewInt(0).SetBytes(v) + if len(fields) >= fieldsSizeWithChainID { + chainID := big.NewInt(0).SetBytes(fields[6]) + + // a = chainId * 2 + // b = v - 27 + // c = a + 35 + // v = b + c + // + // same as: + // v = v-27+chainId*2+35 + a := new(big.Int).Mul(chainID, big.NewInt(double)) + b := new(big.Int).Sub(new(big.Int).SetBytes(v), big.NewInt(ether155V)) + c := new(big.Int).Add(a, big.NewInt(etherPre155V)) + txV = new(big.Int).Add(b, c) + } + + txR := big.NewInt(0).SetBytes(r) + txS := big.NewInt(0).SetBytes(s) + + return &types.LegacyTx{ + Nonce: nonce, + GasPrice: gasPrice, + Gas: gas, + To: to, + Value: value, + Data: data, + V: txV, + R: txR, + S: txS, + }, nil +} diff --git a/state/types.go b/state/types.go new file mode 100644 index 00000000..40aaa88b --- /dev/null +++ b/state/types.go @@ -0,0 +1,49 @@ +package state + +import ( + "time" +) + +// ZKCounters counters for the tx +type ZKCounters struct { + GasUsed uint64 + UsedKeccakHashes uint32 + UsedPoseidonHashes uint32 + UsedPoseidonPaddings uint32 + UsedMemAligns uint32 + UsedArithmetics uint32 + UsedBinaries uint32 + UsedSteps uint32 + UsedSha256Hashes_V2 uint32 +} + +// BatchResources is a struct that contains the ZKEVM resources used by a batch/tx +type BatchResources struct { + ZKCounters ZKCounters + Bytes uint64 +} + +// Proof struct +type Proof struct { + BatchNumber uint64 + BatchNumberFinal uint64 + Proof string + InputProver string + ProofID *string + // Prover name, unique identifier across prover reboots. + Prover *string + // ProverID prover process identifier. + ProverID *string + // GeneratingSince holds the timestamp for the moment in which the + // proof generation has started by a prover. Nil if the proof is not + // currently generating. + GeneratingSince *time.Time + CreatedAt time.Time + UpdatedAt time.Time +} + +// Sequence represents the sequence interval +type Sequence struct { + FromBatchNumber uint64 + ToBatchNumber uint64 +} diff --git a/test/Makefile b/test/Makefile index 93a62f50..f7ca2cb5 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,3 +1,13 @@ .PHONY: run-tests run-tests: ## Runs all tests within the workspace - go test -v -race ./... \ No newline at end of file + go test -v -race ./... + +.PHONY: generate-mocks +generate-mocks: generate-mocks-localbridgesync + +.PHONY: generate-mocks-localbridgesync +generate-mocks-localbridgesync: ## Generates mocks for localbridgesync , using mockery tool + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=EthClienter --dir=../localbridgesync --output=../localbridgesync --outpkg=localbridgesync --inpackage --structname=L2Mock --filename=mock_l2_test.go + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=downloaderFull --dir=../localbridgesync --output=../localbridgesync --outpkg=localbridgesync --inpackage --structname=DownloaderMock --filename=mock_downloader_test.go + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=processorInterface --dir=../localbridgesync --output=../localbridgesync --outpkg=localbridgesync --inpackage --structname=ProcessorMock --filename=mock_processor_test.go + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=ReorgDetector --dir=../localbridgesync --output=../localbridgesync --outpkg=localbridgesync --inpackage --structname=ReorgDetectorMock --filename=mock_reorgdetector_test.go \ No newline at end of file diff --git a/test/aggregator.keystore b/test/aggregator.keystore new file mode 100644 index 00000000..36adf8bc --- /dev/null +++ b/test/aggregator.keystore @@ -0,0 +1 @@ +{"version":3,"id":"71b028b6-9b1d-4f4c-9e66-31c94a6eb679","address":"70997970c51812dc3a010c7d01b50e0d17dc79c8","crypto":{"ciphertext":"985d5dc5f7750fc4ad0ad0d370486870016bb97e00ef1f7b146d6ad95d456861","cipherparams":{"iv":"f51b18b9f45872f71c3578513fca6cb0"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"6253e2d8a71e4808dd11143329cfea467cabb37ac1e1e55dbc0dd90ff22524a7","n":8192,"r":8,"p":1},"mac":"922f741e84201fc7c17bbf9fae5dba6c04a2a99a7268998b5a0268aa690004be"}} \ No newline at end of file diff --git a/test/config/test.config.toml b/test/config/test.config.toml new file mode 100644 index 00000000..f4b55677 --- /dev/null +++ b/test/config/test.config.toml @@ -0,0 +1,110 @@ +[SequenceSender] +IsValidiumMode = false +WaitPeriodSendSequence = "15s" +LastBatchVirtualizationTimeMaxWaitPeriod = "10s" +L1BlockTimestampMargin = "30s" +MaxTxSizeForL1 = 131072 +L2Coinbase = "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266" +PrivateKey = {Path = "./test/sequencer.keystore", Password = "testonly"} +SequencesTxFileName = "sequencesender.json" +GasOffset = 80000 +WaitPeriodPurgeTxFile = "60m" +MaxPendingTx = 1 + [SequenceSender.StreamClient] + Server = "127.0.0.1:6900" + [SequenceSender.EthTxManager] + FrequencyToMonitorTxs = "1s" + WaitTxToBeMined = "2m" + GetReceiptMaxTime = "250ms" + GetReceiptWaitInterval = "1s" + PrivateKeys = [ + {Path = "./test/sequencer.keystore", Password = "testonly"}, + ] + ForcedGas = 0 + GasPriceMarginFactor = 1 + MaxGasPriceLimit = 0 + PersistenceFilename = "ethtxmanager.json" + ReadPendingL1Txs = false + SafeStatusL1NumberOfBlocks = 5 + FinalizedStatusL1NumberOfBlocks = 10 + [SequenceSender.EthTxManager.Etherman] + URL = "http://127.0.0.1:8545" + MultiGasProvider = false + L1ChainID = 1337 + HTTPHeaders = [] + +[Aggregator] +Host = "0.0.0.0" +Port = 50081 +RetryTime = "5s" +VerifyProofInterval = "10s" +TxProfitabilityCheckerType = "acceptall" +TxProfitabilityMinReward = "1.1" +ProofStatePollingInterval = "5s" +SenderAddress = "0x3f2963d678442c4af27a797453b64ef6ce9443e9" +CleanupLockedProofsInterval = "2m" +GeneratingProofCleanupThreshold = "10m" +ForkId = 9 +GasOffset = 0 +WitnessURL = "http://zkevm-erigon-seq:8123" +UseL1BatchData = true +SettlementBackend = "l1" +AggLayerTxTimeout = "5m" +AggLayerURL = "" +SequencerPrivateKey = {} +UseFullWitness = false + [Aggregator.DB] + Name = "aggregator_db" + User = "aggregator_user" + Password = "aggregator_password" + Host = "cdk-aggregator-db" + Port = "5432" + EnableLog = false + MaxConns = 200 + [Aggregator.Log] + Environment = "development" # "production" or "development" + Level = "info" + Outputs = ["stderr"] + [Aggregator.StreamClient] + Server = "zkevm-erigon-seq:6900" + [Aggregator.EthTxManager] + FrequencyToMonitorTxs = "1s" + WaitTxToBeMined = "2m" + GetReceiptMaxTime = "250ms" + GetReceiptWaitInterval = "1s" + PrivateKeys = [ + {Path = "/pk/aggregator.keystore", Password = "testonly"}, + ] + ForcedGas = 0 + GasPriceMarginFactor = 1 + MaxGasPriceLimit = 0 + PersistenceFilename = "" + ReadPendingL1Txs = false + SafeStatusL1NumberOfBlocks = 0 + FinalizedStatusL1NumberOfBlocks = 0 + [Aggregator.EthTxManager.Etherman] + URL = "" + L1ChainID = 11155111 + HTTPHeaders = [] + [Aggregator.Synchronizer] + [Aggregator.Synchronizer.DB] + Name = "sync_db" + User = "sync_user" + Password = "sync_password" + Host = "cdk-l1-sync-db" + Port = "5432" + EnableLog = false + MaxConns = 10 + [Aggregator.Synchronizer.Synchronizer] + SyncInterval = "10s" + SyncChunkSize = 1000 + GenesisBlockNumber = 5511080 + SyncUpToBlock = "finalized" + BlockFinality = "finalized" + [Aggregator.Synchronizer.Etherman] + [Aggregator.Synchronizer.Etherman.Validium] + Enabled = false + TrustedSequencerURL = "" + DataSourcePriority = ["trusted", "external"] + [Aggregator.Synchronizer.Etherman.Validium.Translator] + FullMatchRules = [] diff --git a/test/config/test.genesis.json b/test/config/test.genesis.json new file mode 100644 index 00000000..9744f7b9 --- /dev/null +++ b/test/config/test.genesis.json @@ -0,0 +1,100 @@ +{ + "l1Config": { + "chainId": 1337, + "polygonZkEVMAddress": "0x8dAF17A20c9DBA35f005b6324F493785D239719d", + "polygonRollupManagerAddress": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", + "polTokenAddress": "0x5FbDB2315678afecb367f032d93F642f64180aa3", + "polygonZkEVMGlobalExitRootAddress": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" + }, + "genesisBlockNumber": 67, + "root": "0xcc9ec17819f4ac7f282949ca8c379c4d3ee1b8b7908c51b9b405b6319af67b32", + "genesis": [ + { + "contractName": "PolygonZkEVMDeployer", + "balance": "0", + "nonce": "4", + "address": "0x51dbd54FCCb6b3A07738fd3E156D588e71f79973", + "bytecode": "0x6080604052600436106100705760003560e01c8063715018a61161004e578063715018a6146100e65780638da5cb5b146100fb578063e11ae6cb14610126578063f2fde38b1461013957600080fd5b80632b79805a146100755780634a94d4871461008a5780636d07dbf81461009d575b600080fd5b610088610083366004610927565b610159565b005b6100886100983660046109c7565b6101cb565b3480156100a957600080fd5b506100bd6100b8366004610a1e565b61020d565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100f257600080fd5b50610088610220565b34801561010757600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166100bd565b610088610134366004610a40565b610234565b34801561014557600080fd5b50610088610154366004610a90565b61029b565b610161610357565b600061016e8585856103d8565b905061017a8183610537565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527fba82f25fed02cd2a23d9f5d11c2ef588d22af5437cbf23bfe61d87257c480e4c9060200160405180910390a15050505050565b6101d3610357565b6101de83838361057b565b506040517f25adb19089b6a549831a273acdf7908cff8b7ee5f551f8d1d37996cf01c5df5b90600090a1505050565b600061021983836105a9565b9392505050565b610228610357565b61023260006105b6565b565b61023c610357565b60006102498484846103d8565b60405173ffffffffffffffffffffffffffffffffffffffff821681529091507fba82f25fed02cd2a23d9f5d11c2ef588d22af5437cbf23bfe61d87257c480e4c9060200160405180910390a150505050565b6102a3610357565b73ffffffffffffffffffffffffffffffffffffffff811661034b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610354816105b6565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314610232576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610342565b600083471015610444576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e63650000006044820152606401610342565b81516000036104af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f6044820152606401610342565b8282516020840186f5905073ffffffffffffffffffffffffffffffffffffffff8116610219576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f437265617465323a204661696c6564206f6e206465706c6f79000000000000006044820152606401610342565b6060610219838360006040518060400160405280601e81526020017f416464726573733a206c6f772d6c6576656c2063616c6c206661696c6564000081525061062b565b60606105a1848484604051806060016040528060298152602001610b3d6029913961062b565b949350505050565b6000610219838330610744565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6060824710156106bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610342565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516106e69190610acf565b60006040518083038185875af1925050503d8060008114610723576040519150601f19603f3d011682016040523d82523d6000602084013e610728565b606091505b50915091506107398783838761076e565b979650505050505050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b606083156108045782516000036107fd5773ffffffffffffffffffffffffffffffffffffffff85163b6107fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610342565b50816105a1565b6105a183838151156108195781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103429190610aeb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261088d57600080fd5b813567ffffffffffffffff808211156108a8576108a861084d565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156108ee576108ee61084d565b8160405283815286602085880101111561090757600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806000806080858703121561093d57600080fd5b8435935060208501359250604085013567ffffffffffffffff8082111561096357600080fd5b61096f8883890161087c565b9350606087013591508082111561098557600080fd5b506109928782880161087c565b91505092959194509250565b803573ffffffffffffffffffffffffffffffffffffffff811681146109c257600080fd5b919050565b6000806000606084860312156109dc57600080fd5b6109e58461099e565b9250602084013567ffffffffffffffff811115610a0157600080fd5b610a0d8682870161087c565b925050604084013590509250925092565b60008060408385031215610a3157600080fd5b50508035926020909101359150565b600080600060608486031215610a5557600080fd5b8335925060208401359150604084013567ffffffffffffffff811115610a7a57600080fd5b610a868682870161087c565b9150509250925092565b600060208284031215610aa257600080fd5b6102198261099e565b60005b83811015610ac6578181015183820152602001610aae565b50506000910152565b60008251610ae1818460208701610aab565b9190910192915050565b6020815260008251806020840152610b0a816040850160208701610aab565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a2646970667358221220964619cee0e0baf94c6f8763f013be157da5d54c89e5cff4a8caf4266e13f13a64736f6c63430008140033", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" + } + }, + { + "contractName": "ProxyAdmin", + "balance": "0", + "nonce": "1", + "address": "0xe34Fe58DDa5b8c6D547E4857E987633aa86a5e90", + "bytecode": "0x60806040526004361061007b5760003560e01c80639623609d1161004e5780639623609d1461012b57806399a88ec41461013e578063f2fde38b1461015e578063f3b7dead1461017e57600080fd5b8063204e1c7a14610080578063715018a6146100c95780637eff275e146100e05780638da5cb5b14610100575b600080fd5b34801561008c57600080fd5b506100a061009b366004610608565b61019e565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100d557600080fd5b506100de610255565b005b3480156100ec57600080fd5b506100de6100fb36600461062c565b610269565b34801561010c57600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166100a0565b6100de610139366004610694565b6102f7565b34801561014a57600080fd5b506100de61015936600461062c565b61038c565b34801561016a57600080fd5b506100de610179366004610608565b6103e8565b34801561018a57600080fd5b506100a0610199366004610608565b6104a4565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516101ea907f5c60da1b00000000000000000000000000000000000000000000000000000000815260040190565b600060405180830381855afa9150503d8060008114610225576040519150601f19603f3d011682016040523d82523d6000602084013e61022a565b606091505b50915091508161023957600080fd5b8080602001905181019061024d9190610788565b949350505050565b61025d6104f0565b6102676000610571565b565b6102716104f0565b6040517f8f28397000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690638f283970906024015b600060405180830381600087803b1580156102db57600080fd5b505af11580156102ef573d6000803e3d6000fd5b505050505050565b6102ff6104f0565b6040517f4f1ef28600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841690634f1ef28690349061035590869086906004016107a5565b6000604051808303818588803b15801561036e57600080fd5b505af1158015610382573d6000803e3d6000fd5b5050505050505050565b6103946104f0565b6040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690633659cfe6906024016102c1565b6103f06104f0565b73ffffffffffffffffffffffffffffffffffffffff8116610498576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6104a181610571565b50565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516101ea907ff851a44000000000000000000000000000000000000000000000000000000000815260040190565b60005473ffffffffffffffffffffffffffffffffffffffff163314610267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161048f565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b73ffffffffffffffffffffffffffffffffffffffff811681146104a157600080fd5b60006020828403121561061a57600080fd5b8135610625816105e6565b9392505050565b6000806040838503121561063f57600080fd5b823561064a816105e6565b9150602083013561065a816105e6565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000606084860312156106a957600080fd5b83356106b4816105e6565b925060208401356106c4816105e6565b9150604084013567ffffffffffffffff808211156106e157600080fd5b818601915086601f8301126106f557600080fd5b81358181111561070757610707610665565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561074d5761074d610665565b8160405282815289602084870101111561076657600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561079a57600080fd5b8151610625816105e6565b73ffffffffffffffffffffffffffffffffffffffff8316815260006020604081840152835180604085015260005b818110156107ef578581018301518582016060015282016107d3565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010192505050939250505056fea2646970667358221220c9867ffac53151bdb1305d8f5e3e883cd83e5270c7ec09cdc24e837b2e65239064736f6c63430008140033", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000165878a594ca255338adfa4d48449f69242eb8f" + } + }, + { + "contractName": "PolygonZkEVMBridge implementation", + "balance": "0", + "nonce": "1", + "address": "0x493732fB136a380920C390a85fc27d79C7b70756", + "bytecode": "0x6080604052600436106101a35760003560e01c806383f24403116100e2578063ccaa2d1111610085578063ccaa2d1114610511578063cd58657914610531578063d02103ca14610544578063dbc169761461056b578063ee25560b14610580578063f5efcd79146105ad578063f811bff7146105cd578063fb570834146105ed57600080fd5b806383f244031461040b5780638ed7e3f21461042b578063aaa13cc21461044b578063b8b284d01461046b578063bab161bf1461048b578063be5831c7146104ad578063c00f14ab146104d1578063cc461632146104f157600080fd5b80633cbc795b1161014a5780633cbc795b146102fd5780633e197043146103365780634b2f336d146103565780635ca1e165146103765780637843298b1461038b57806379e2cf97146103ab57806381b1c174146103c057806383c43a55146103f657600080fd5b806315064c96146101a85780632072f6c5146101d757806322e95f2c146101ee578063240ff3781461021b57806327aef4e81461022e5780632dfdf0b514610250578063318aee3d146102745780633c351e10146102dd575b600080fd5b3480156101b457600080fd5b506068546101c29060ff1681565b60405190151581526020015b60405180910390f35b3480156101e357600080fd5b506101ec61060d565b005b3480156101fa57600080fd5b5061020e610209366004612b65565b610642565b6040516101ce9190612b9c565b6101ec610229366004612c06565b610693565b34801561023a57600080fd5b50610243610703565b6040516101ce9190612ccf565b34801561025c57600080fd5b5061026660535481565b6040519081526020016101ce565b34801561028057600080fd5b506102b961028f366004612ce9565b606b6020526000908152604090205463ffffffff811690600160201b90046001600160a01b031682565b6040805163ffffffff90931683526001600160a01b039091166020830152016101ce565b3480156102e957600080fd5b50606d5461020e906001600160a01b031681565b34801561030957600080fd5b50606d5461032190600160a01b900463ffffffff1681565b60405163ffffffff90911681526020016101ce565b34801561034257600080fd5b50610266610351366004612d15565b610791565b34801561036257600080fd5b50606f5461020e906001600160a01b031681565b34801561038257600080fd5b5061026661081e565b34801561039757600080fd5b5061020e6103a6366004612d94565b6108fb565b3480156103b757600080fd5b506101ec610925565b3480156103cc57600080fd5b5061020e6103db366004612ddd565b606a602052600090815260409020546001600160a01b031681565b34801561040257600080fd5b50610243610946565b34801561041757600080fd5b50610266610426366004612e08565b610965565b34801561043757600080fd5b50606c5461020e906001600160a01b031681565b34801561045757600080fd5b5061020e610466366004612f12565b610a3b565b34801561047757600080fd5b506101ec610486366004612fad565b610b3d565b34801561049757600080fd5b5060685461032190610100900463ffffffff1681565b3480156104b957600080fd5b5060685461032190600160c81b900463ffffffff1681565b3480156104dd57600080fd5b506102436104ec366004612ce9565b610c04565b3480156104fd57600080fd5b506101c261050c36600461302f565b610c49565b34801561051d57600080fd5b506101ec61052c366004613062565b610cd2565b6101ec61053f36600461314d565b6111c7565b34801561055057600080fd5b5060685461020e90600160281b90046001600160a01b031681565b34801561057757600080fd5b506101ec611621565b34801561058c57600080fd5b5061026661059b366004612ddd565b60696020526000908152604090205481565b3480156105b957600080fd5b506101ec6105c8366004613062565b611654565b3480156105d957600080fd5b506101ec6105e83660046131e2565b6118ef565b3480156105f957600080fd5b506101c261060836600461328a565b611b62565b606c546001600160a01b0316331461063857604051631736745960e31b815260040160405180910390fd5b610640611b7a565b565b6000606a6000848460405160200161065b9291906132d2565b60408051601f19818403018152918152815160209283012083529082019290925201600020546001600160a01b031690505b92915050565b60685460ff16156106b757604051630bc011ff60e21b815260040160405180910390fd5b34158015906106d05750606f546001600160a01b031615155b156106ee576040516301bd897160e61b815260040160405180910390fd5b6106fc858534868686611bd6565b5050505050565b606e8054610710906132fc565b80601f016020809104026020016040519081016040528092919081815260200182805461073c906132fc565b80156107895780601f1061075e57610100808354040283529160200191610789565b820191906000526020600020905b81548152906001019060200180831161076c57829003601f168201915b505050505081565b6040516001600160f81b031960f889901b1660208201526001600160e01b031960e088811b821660218401526001600160601b0319606089811b821660258601529188901b909216603984015285901b16603d8201526051810183905260718101829052600090609101604051602081830303815290604052805190602001209050979650505050505050565b605354600090819081805b60208110156108f2578083901c600116600103610886576033816020811061085357610853613336565b015460408051602081019290925281018590526060016040516020818303038152906040528051906020012093506108b3565b60408051602081018690529081018390526060016040516020818303038152906040528051906020012093505b604080516020810184905290810183905260600160405160208183030381529060405280519060200120915080806108ea90613362565b915050610829565b50919392505050565b600061091d848461090b85611ca0565b61091486611d5f565b61046687611e17565b949350505050565b605354606854600160c81b900463ffffffff16101561064057610640611ecf565b60405180611ba00160405280611b668152602001613a7a611b66913981565b600083815b6020811015610a3257600163ffffffff8516821c811690036109d55784816020811061099857610998613336565b6020020135826040516020016109b8929190918252602082015260400190565b604051602081830303815290604052805190602001209150610a20565b818582602081106109e8576109e8613336565b6020020135604051602001610a07929190918252602082015260400190565b6040516020818303038152906040528051906020012091505b80610a2a81613362565b91505061096a565b50949350505050565b6000808686604051602001610a519291906132d2565b604051602081830303815290604052805190602001209050600060ff60f81b308360405180611ba00160405280611b668152602001613a7a611b669139898989604051602001610aa39392919061337b565b60408051601f1981840301815290829052610ac192916020016133b4565b60405160208183030381529060405280519060200120604051602001610b1994939291906001600160f81b031994909416845260609290921b6001600160601b03191660018401526015830152603582015260550190565b60408051808303601f19018152919052805160209091012098975050505050505050565b60685460ff1615610b6157604051630bc011ff60e21b815260040160405180910390fd5b606f546001600160a01b0316610b8a5760405163dde3cda760e01b815260040160405180910390fd5b606f54604051632770a7eb60e21b81526001600160a01b0390911690639dc29fac90610bbc90339088906004016133e3565b600060405180830381600087803b158015610bd657600080fd5b505af1158015610bea573d6000803e3d6000fd5b50505050610bfc868686868686611bd6565b505050505050565b6060610c0f82611ca0565b610c1883611d5f565b610c2184611e17565b604051602001610c339392919061337b565b6040516020818303038152906040529050919050565b6068546000908190610100900463ffffffff16158015610c6f575063ffffffff83166001145b15610c81575063ffffffff8316610ca8565b610c95600160201b63ffffffff85166133fc565b610ca59063ffffffff8616613413565b90505b600881901c600090815260696020526040902054600160ff9092169190911b908116149392505050565b60685460ff1615610cf657604051630bc011ff60e21b815260040160405180910390fd5b60685463ffffffff8681166101009092041614610d26576040516302caf51760e11b815260040160405180910390fd5b610d5a8c8c8c8c8c610d5560008e8e8e8e8e8e8e604051610d48929190613426565b6040518091039020610791565b611f68565b6001600160a01b038616610e9257606f546001600160a01b0316610e295760006001600160a01b03851684825b6040519080825280601f01601f191660200182016040528015610db1576020820181803683370190505b50604051610dbf9190613436565b60006040518083038185875af1925050503d8060008114610dfc576040519150601f19603f3d011682016040523d82523d6000602084013e610e01565b606091505b5050905080610e2357604051630ce8f45160e31b815260040160405180910390fd5b5061117a565b606f546040516340c10f1960e01b81526001600160a01b03909116906340c10f1990610e5b90879087906004016133e3565b600060405180830381600087803b158015610e7557600080fd5b505af1158015610e89573d6000803e3d6000fd5b5050505061117a565b606d546001600160a01b038781169116148015610ec05750606d5463ffffffff888116600160a01b90920416145b15610ed85760006001600160a01b0385168482610d87565b60685463ffffffff610100909104811690881603610f0957610f046001600160a01b03871685856120c7565b61117a565b60008787604051602001610f1e9291906132d2565b60408051601f1981840301815291815281516020928301206000818152606a9093529120549091506001600160a01b031680611116576000610f968386868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061212292505050565b6040516340c10f1960e01b81529091506001600160a01b038216906340c10f1990610fc7908a908a906004016133e3565b600060405180830381600087803b158015610fe157600080fd5b505af1158015610ff5573d6000803e3d6000fd5b5050505080606a600085815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060405180604001604052808b63ffffffff1681526020018a6001600160a01b0316815250606b6000836001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a8154816001600160a01b0302191690836001600160a01b031602179055509050507f490e59a1701b938786ac72570a1efeac994a3dbe96e2e883e19e902ace6e6a398a8a83888860405161110895949392919061347b565b60405180910390a150611177565b6040516340c10f1960e01b81526001600160a01b038216906340c10f199061114490899089906004016133e3565b600060405180830381600087803b15801561115e57600080fd5b505af1158015611172573d6000803e3d6000fd5b505050505b50505b7f1df3f2a973a00d6635911755c260704e95e8a5876997546798770f76396fda4d8a888887876040516111b19594939291906134b4565b60405180910390a1505050505050505050505050565b60685460ff16156111eb57604051630bc011ff60e21b815260040160405180910390fd5b6111f361219e565b60685463ffffffff610100909104811690881603611224576040516302caf51760e11b815260040160405180910390fd5b6000806060876001600160a01b03881661130a578834146112585760405163b89240f560e01b815260040160405180910390fd5b606d54606e80546001600160a01b0383169650600160a01b90920463ffffffff16945090611285906132fc565b80601f01602080910402602001604051908101604052809291908181526020018280546112b1906132fc565b80156112fe5780601f106112d3576101008083540402835291602001916112fe565b820191906000526020600020905b8154815290600101906020018083116112e157829003601f168201915b50505050509150611596565b34156113295760405163798ee6f160e01b815260040160405180910390fd5b606f546001600160a01b03908116908916036113a457604051632770a7eb60e21b81526001600160a01b03891690639dc29fac9061136d9033908d906004016133e3565b600060405180830381600087803b15801561138757600080fd5b505af115801561139b573d6000803e3d6000fd5b50505050611596565b6001600160a01b038089166000908152606b602090815260409182902082518084019093525463ffffffff81168352600160201b9004909216918101829052901561145c57604051632770a7eb60e21b81526001600160a01b038a1690639dc29fac906114179033908e906004016133e3565b600060405180830381600087803b15801561143157600080fd5b505af1158015611445573d6000803e3d6000fd5b505050508060200151945080600001519350611589565b851561146e5761146e898b89896121f7565b6040516370a0823160e01b81526000906001600160a01b038b16906370a082319061149d903090600401612b9c565b602060405180830381865afa1580156114ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114de91906134e6565b90506114f56001600160a01b038b1633308e61253d565b6040516370a0823160e01b81526000906001600160a01b038c16906370a0823190611524903090600401612b9c565b602060405180830381865afa158015611541573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061156591906134e6565b905061157182826134ff565b6068548c9850610100900463ffffffff169650935050505b61159289610c04565b9250505b7f501781209a1f8899323b96b4ef08b168df93e0a90c673d1e4cce39366cb62f9b600084868e8e86886053546040516115d6989796959493929190613512565b60405180910390a16115fd6115f8600085878f8f878980519060200120610791565b612575565b861561160b5761160b611ecf565b5050505061161860018055565b50505050505050565b606c546001600160a01b0316331461164c57604051631736745960e31b815260040160405180910390fd5b610640612660565b60685460ff161561167857604051630bc011ff60e21b815260040160405180910390fd5b60685463ffffffff86811661010090920416146116a8576040516302caf51760e11b815260040160405180910390fd5b6116ca8c8c8c8c8c610d5560018e8e8e8e8e8e8e604051610d48929190613426565b606f546000906001600160a01b031661178157846001600160a01b031684888a86866040516024016116ff949392919061357d565b60408051601f198184030181529181526020820180516001600160e01b0316630c035af960e11b179052516117349190613436565b60006040518083038185875af1925050503d8060008114611771576040519150601f19603f3d011682016040523d82523d6000602084013e611776565b606091505b505080915050611883565b606f546040516340c10f1960e01b81526001600160a01b03909116906340c10f19906117b390889088906004016133e3565b600060405180830381600087803b1580156117cd57600080fd5b505af11580156117e1573d6000803e3d6000fd5b50505050846001600160a01b031687898585604051602401611806949392919061357d565b60408051601f198184030181529181526020820180516001600160e01b0316630c035af960e11b1790525161183b9190613436565b6000604051808303816000865af19150503d8060008114611878576040519150601f19603f3d011682016040523d82523d6000602084013e61187d565b606091505b50909150505b806118a1576040516337e391c360e01b815260040160405180910390fd5b7f1df3f2a973a00d6635911755c260704e95e8a5876997546798770f76396fda4d8b898988886040516118d89594939291906134b4565b60405180910390a150505050505050505050505050565b600054610100900460ff161580801561190f5750600054600160ff909116105b806119295750303b158015611929575060005460ff166001145b6119915760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff1916600117905580156119b4576000805461ff0019166101001790555b60688054610100600160c81b03191661010063ffffffff8a160265010000000000600160c81b03191617600160281b6001600160a01b038781169190910291909117909155606c80546001600160a01b0319168583161790558616611a3d5763ffffffff851615611a3857604051630d43a60960e11b815260040160405180910390fd5b611b0c565b606d805463ffffffff8716600160a01b026001600160c01b03199091166001600160a01b03891617179055606e611a7483826135fe565b50611aeb6000801b6012604051602001611ad791906060808252600d908201526c2bb930b83832b21022ba3432b960991b608082015260a060208201819052600490820152630ae8aa8960e31b60c082015260ff91909116604082015260e00190565b604051602081830303815290604052612122565b606f80546001600160a01b0319166001600160a01b03929092169190911790555b611b146126b8565b8015611618576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150505050505050565b600081611b70868686610965565b1495945050505050565b60685460ff1615611b9e57604051630bc011ff60e21b815260040160405180910390fd5b6068805460ff191660011790556040517f2261efe5aef6fedc1fd1550b25facc9181745623049c7901287030b9ad1a549790600090a1565b60685463ffffffff610100909104811690871603611c07576040516302caf51760e11b815260040160405180910390fd5b7f501781209a1f8899323b96b4ef08b168df93e0a90c673d1e4cce39366cb62f9b6001606860019054906101000a900463ffffffff16338989898888605354604051611c5b999897969594939291906136bd565b60405180910390a1611c926115f86001606860019054906101000a900463ffffffff16338a8a8a8989604051610d48929190613426565b8215610bfc57610bfc611ecf565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b179052905160609160009182916001600160a01b03861691611ce79190613436565b600060405180830381855afa9150503d8060008114611d22576040519150601f19603f3d011682016040523d82523d6000602084013e611d27565b606091505b509150915081611d5657604051806040016040528060078152602001664e4f5f4e414d4560c81b81525061091d565b61091d816126e7565b60408051600481526024810182526020810180516001600160e01b03166395d89b4160e01b179052905160609160009182916001600160a01b03861691611da69190613436565b600060405180830381855afa9150503d8060008114611de1576040519150601f19603f3d011682016040523d82523d6000602084013e611de6565b606091505b509150915081611d5657604051806040016040528060098152602001681393d7d4d6535093d360ba1b81525061091d565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b1790529051600091829182916001600160a01b03861691611e5d9190613436565b600060405180830381855afa9150503d8060008114611e98576040519150601f19603f3d011682016040523d82523d6000602084013e611e9d565b606091505b5091509150818015611eb0575080516020145b611ebb57601261091d565b8080602001905181019061091d919061372a565b6053546068805463ffffffff909216600160c81b0263ffffffff60c81b1990921691909117908190556001600160a01b03600160281b909104166333d6247d611f1661081e565b6040518263ffffffff1660e01b8152600401611f3491815260200190565b600060405180830381600087803b158015611f4e57600080fd5b505af1158015611f62573d6000803e3d6000fd5b50505050565b606854604080516020808201879052818301869052825180830384018152606083019384905280519101206312bd9b1960e11b9092526064810191909152600091600160281b90046001600160a01b03169063257b3632906084016020604051808303816000875af1158015611fe2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061200691906134e6565b90508060000361202857604051622f6fad60e01b815260040160405180910390fd5b600080600160401b87161561206857869150612046848a8489611b62565b612063576040516338105f3b60e21b815260040160405180910390fd5b6120b2565b602087901c612078816001613747565b915087925061209361208b868c86610965565b8a8389611b62565b6120b0576040516338105f3b60e21b815260040160405180910390fd5b505b6120bc8282612875565b505050505050505050565b61211d8363a9059cbb60e01b84846040516024016120e69291906133e3565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261291d565b505050565b60008060405180611ba00160405280611b668152602001613a7a611b669139836040516020016121539291906133b4565b6040516020818303038152906040529050838151602083016000f591506001600160a01b038216612197576040516305f7d84960e51b815260040160405180910390fd5b5092915050565b6002600154036121f05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401611988565b6002600155565b60006122066004828486613764565b61220f9161378e565b9050632afa533160e01b6001600160e01b03198216016123a357600080808080808061223e896004818d613764565b81019061224b91906137be565b9650965096509650965096509650336001600160a01b0316876001600160a01b03161461228b5760405163912ecce760e01b815260040160405180910390fd5b6001600160a01b03861630146122b45760405163750643af60e01b815260040160405180910390fd5b8a85146122d4576040516303fffc4b60e01b815260040160405180910390fd5b604080516001600160a01b0389811660248301528881166044830152606482018890526084820187905260ff861660a483015260c4820185905260e48083018590528351808403909101815261010490920183526020820180516001600160e01b031663d505accf60e01b1790529151918e16916123529190613436565b6000604051808303816000865af19150503d806000811461238f576040519150601f19603f3d011682016040523d82523d6000602084013e612394565b606091505b505050505050505050506106fc565b6001600160e01b031981166323f2ebc360e21b146123d457604051637141605d60e11b815260040160405180910390fd5b6000808080808080806123ea8a6004818e613764565b8101906123f79190613812565b97509750975097509750975097509750336001600160a01b0316886001600160a01b0316146124395760405163912ecce760e01b815260040160405180910390fd5b6001600160a01b03871630146124625760405163750643af60e01b815260040160405180910390fd5b604080516001600160a01b038a811660248301528981166044830152606482018990526084820188905286151560a483015260ff861660c483015260e482018590526101048083018590528351808403909101815261012490920183526020820180516001600160e01b03166323f2ebc360e21b1790529151918f16916124e99190613436565b6000604051808303816000865af19150503d8060008114612526576040519150601f19603f3d011682016040523d82523d6000602084013e61252b565b606091505b50505050505050505050505050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052611f629085906323b872dd60e01b906084016120e6565b80600161258460206002613979565b61258e91906134ff565b605354106125af576040516377ae67b360e11b815260040160405180910390fd5b60006053600081546125c090613362565b9182905550905060005b6020811015612651578082901c6001166001036125fd5782603382602081106125f5576125f5613336565b015550505050565b6033816020811061261057612610613336565b01546040805160208101929092528101849052606001604051602081830303815290604052805190602001209250808061264990613362565b9150506125ca565b5061211d613985565b60018055565b60685460ff1661268357604051635386698160e01b815260040160405180910390fd5b6068805460ff191690556040517f1e5e34eea33501aecf2ebec9fe0e884a40804275ea7fe10b2ba084c8374308b390600090a1565b600054610100900460ff166126df5760405162461bcd60e51b81526004016119889061399b565b6106406129ef565b60606040825110612706578180602001905181019061068d91906139e6565b81516020036128425760005b602081108015612741575082818151811061272f5761272f613336565b01602001516001600160f81b03191615155b15612758578061275081613362565b915050612712565b806000036127905750506040805180820190915260128152714e4f545f56414c49445f454e434f44494e4760701b6020820152919050565b6000816001600160401b038111156127aa576127aa612e47565b6040519080825280601f01601f1916602001820160405280156127d4576020820181803683370190505b50905060005b8281101561283a578481815181106127f4576127f4613336565b602001015160f81c60f81b82828151811061281157612811613336565b60200101906001600160f81b031916908160001a9053508061283281613362565b9150506127da565b509392505050565b50506040805180820190915260128152714e4f545f56414c49445f454e434f44494e4760701b602082015290565b919050565b606854600090610100900463ffffffff16158015612899575063ffffffff82166001145b156128ab575063ffffffff82166128d2565b6128bf600160201b63ffffffff84166133fc565b6128cf9063ffffffff8516613413565b90505b600881901c60008181526069602052604081208054600160ff861690811b9182189283905592909190818316900361161857604051630c8d9eab60e31b815260040160405180910390fd5b6000612972826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612a169092919063ffffffff16565b80519091501561211d57808060200190518101906129909190613a5c565b61211d5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401611988565b600054610100900460ff1661265a5760405162461bcd60e51b81526004016119889061399b565b606061091d848460008585600080866001600160a01b03168587604051612a3d9190613436565b60006040518083038185875af1925050503d8060008114612a7a576040519150601f19603f3d011682016040523d82523d6000602084013e612a7f565b606091505b5091509150612a9087838387612a9b565b979650505050505050565b60608315612b0a578251600003612b03576001600160a01b0385163b612b035760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611988565b508161091d565b61091d8383815115612b1f5781518083602001fd5b8060405162461bcd60e51b81526004016119889190612ccf565b803563ffffffff8116811461287057600080fd5b6001600160a01b0381168114612b6257600080fd5b50565b60008060408385031215612b7857600080fd5b612b8183612b39565b91506020830135612b9181612b4d565b809150509250929050565b6001600160a01b0391909116815260200190565b8015158114612b6257600080fd5b60008083601f840112612bd057600080fd5b5081356001600160401b03811115612be757600080fd5b602083019150836020828501011115612bff57600080fd5b9250929050565b600080600080600060808688031215612c1e57600080fd5b612c2786612b39565b94506020860135612c3781612b4d565b93506040860135612c4781612bb0565b925060608601356001600160401b03811115612c6257600080fd5b612c6e88828901612bbe565b969995985093965092949392505050565b60005b83811015612c9a578181015183820152602001612c82565b50506000910152565b60008151808452612cbb816020860160208601612c7f565b601f01601f19169290920160200192915050565b602081526000612ce26020830184612ca3565b9392505050565b600060208284031215612cfb57600080fd5b8135612ce281612b4d565b60ff81168114612b6257600080fd5b600080600080600080600060e0888a031215612d3057600080fd5b8735612d3b81612d06565b9650612d4960208901612b39565b95506040880135612d5981612b4d565b9450612d6760608901612b39565b93506080880135612d7781612b4d565b9699959850939692959460a0840135945060c09093013592915050565b600080600060608486031215612da957600080fd5b612db284612b39565b92506020840135612dc281612b4d565b91506040840135612dd281612b4d565b809150509250925092565b600060208284031215612def57600080fd5b5035919050565b80610400810183101561068d57600080fd5b60008060006104408486031215612e1e57600080fd5b83359250612e2f8560208601612df6565b9150612e3e6104208501612b39565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715612e8557612e85612e47565b604052919050565b60006001600160401b03821115612ea657612ea6612e47565b50601f01601f191660200190565b6000612ec7612ec284612e8d565b612e5d565b9050828152838383011115612edb57600080fd5b828260208301376000602084830101529392505050565b600082601f830112612f0357600080fd5b612ce283833560208501612eb4565b600080600080600060a08688031215612f2a57600080fd5b612f3386612b39565b94506020860135612f4381612b4d565b935060408601356001600160401b0380821115612f5f57600080fd5b612f6b89838a01612ef2565b94506060880135915080821115612f8157600080fd5b50612f8e88828901612ef2565b9250506080860135612f9f81612d06565b809150509295509295909350565b60008060008060008060a08789031215612fc657600080fd5b612fcf87612b39565b95506020870135612fdf81612b4d565b9450604087013593506060870135612ff681612bb0565b925060808701356001600160401b0381111561301157600080fd5b61301d89828a01612bbe565b979a9699509497509295939492505050565b6000806040838503121561304257600080fd5b61304b83612b39565b915061305960208401612b39565b90509250929050565b6000806000806000806000806000806000806109208d8f03121561308557600080fd5b61308f8e8e612df6565b9b5061309f8e6104008f01612df6565b9a506108008d013599506108208d013598506108408d013597506130c66108608e01612b39565b96506130d66108808e0135612b4d565b6108808d013595506130eb6108a08e01612b39565b94506130fb6108c08e0135612b4d565b6108c08d013593506108e08d013592506001600160401b036109008e0135111561312457600080fd5b6131358e6109008f01358f01612bbe565b81935080925050509295989b509295989b509295989b565b600080600080600080600060c0888a03121561316857600080fd5b61317188612b39565b9650602088013561318181612b4d565b955060408801359450606088013561319881612b4d565b935060808801356131a881612bb0565b925060a08801356001600160401b038111156131c357600080fd5b6131cf8a828b01612bbe565b989b979a50959850939692959293505050565b60008060008060008060c087890312156131fb57600080fd5b61320487612b39565b9550602087013561321481612b4d565b945061322260408801612b39565b9350606087013561323281612b4d565b9250608087013561324281612b4d565b915060a08701356001600160401b0381111561325d57600080fd5b8701601f8101891361326e57600080fd5b61327d89823560208401612eb4565b9150509295509295509295565b60008060008061046085870312156132a157600080fd5b843593506132b28660208701612df6565b92506132c16104208601612b39565b939692955092936104400135925050565b60e09290921b6001600160e01b031916825260601b6001600160601b031916600482015260180190565b600181811c9082168061331057607f821691505b60208210810361333057634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016133745761337461334c565b5060010190565b60608152600061338e6060830186612ca3565b82810360208401526133a08186612ca3565b91505060ff83166040830152949350505050565b600083516133c6818460208801612c7f565b8351908301906133da818360208801612c7f565b01949350505050565b6001600160a01b03929092168252602082015260400190565b808202811582820484141761068d5761068d61334c565b8082018082111561068d5761068d61334c565b8183823760009101908152919050565b60008251613448818460208701612c7f565b9190910192915050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b63ffffffff861681526001600160a01b03858116602083015284166040820152608060608201819052600090612a909083018486613452565b94855263ffffffff9390931660208501526001600160a01b039182166040850152166060830152608082015260a00190565b6000602082840312156134f857600080fd5b5051919050565b8181038181111561068d5761068d61334c565b60ff8916815263ffffffff88811660208301526001600160a01b03888116604084015287821660608401528616608083015260a0820185905261010060c0830181905260009161356484830187612ca3565b925080851660e085015250509998505050505050505050565b6001600160a01b038516815263ffffffff841660208201526060604082018190526000906135ae9083018486613452565b9695505050505050565b601f82111561211d57600081815260208120601f850160051c810160208610156135df5750805b601f850160051c820191505b81811015610bfc578281556001016135eb565b81516001600160401b0381111561361757613617612e47565b61362b8161362584546132fc565b846135b8565b602080601f83116001811461366057600084156136485750858301515b600019600386901b1c1916600185901b178555610bfc565b600085815260208120601f198616915b8281101561368f57888601518255948401946001909101908401613670565b50858210156136ad5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60ff8a16815263ffffffff89811660208301526001600160a01b03898116604084015288821660608401528716608083015260a0820186905261010060c083018190526000916137108483018789613452565b925080851660e085015250509a9950505050505050505050565b60006020828403121561373c57600080fd5b8151612ce281612d06565b63ffffffff8181168382160190808211156121975761219761334c565b6000808585111561377457600080fd5b8386111561378157600080fd5b5050820193919092039150565b6001600160e01b031981358181169160048510156137b65780818660040360031b1b83161692505b505092915050565b600080600080600080600060e0888a0312156137d957600080fd5b87356137e481612b4d565b965060208801356137f481612b4d565b955060408801359450606088013593506080880135612d7781612d06565b600080600080600080600080610100898b03121561382f57600080fd5b883561383a81612b4d565b9750602089013561384a81612b4d565b96506040890135955060608901359450608089013561386881612bb0565b935060a089013561387881612d06565b979a969950949793969295929450505060c08201359160e0013590565b600181815b808511156138d05781600019048211156138b6576138b661334c565b808516156138c357918102915b93841c939080029061389a565b509250929050565b6000826138e75750600161068d565b816138f45750600061068d565b816001811461390a576002811461391457613930565b600191505061068d565b60ff8411156139255761392561334c565b50506001821b61068d565b5060208310610133831016604e8410600b8410161715613953575081810a61068d565b61395d8383613895565b80600019048211156139715761397161334c565b029392505050565b6000612ce283836138d8565b634e487b7160e01b600052600160045260246000fd5b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6000602082840312156139f857600080fd5b81516001600160401b03811115613a0e57600080fd5b8201601f81018413613a1f57600080fd5b8051613a2d612ec282612e8d565b818152856020838501011115613a4257600080fd5b613a53826020830160208601612c7f565b95945050505050565b600060208284031215613a6e57600080fd5b8151612ce281612bb056fe6101006040523480156200001257600080fd5b5060405162001b6638038062001b6683398101604081905262000035916200028d565b82826003620000458382620003a1565b506004620000548282620003a1565b50503360c0525060ff811660e052466080819052620000739062000080565b60a052506200046d915050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f620000ad6200012e565b805160209182012060408051808201825260018152603160f81b90840152805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc66060820152608081018390523060a082015260c001604051602081830303815290604052805190602001209050919050565b6060600380546200013f9062000312565b80601f01602080910402602001604051908101604052809291908181526020018280546200016d9062000312565b8015620001be5780601f106200019257610100808354040283529160200191620001be565b820191906000526020600020905b815481529060010190602001808311620001a057829003601f168201915b5050505050905090565b634e487b7160e01b600052604160045260246000fd5b600082601f830112620001f057600080fd5b81516001600160401b03808211156200020d576200020d620001c8565b604051601f8301601f19908116603f01168101908282118183101715620002385762000238620001c8565b816040528381526020925086838588010111156200025557600080fd5b600091505b838210156200027957858201830151818301840152908201906200025a565b600093810190920192909252949350505050565b600080600060608486031215620002a357600080fd5b83516001600160401b0380821115620002bb57600080fd5b620002c987838801620001de565b94506020860151915080821115620002e057600080fd5b50620002ef86828701620001de565b925050604084015160ff811681146200030757600080fd5b809150509250925092565b600181811c908216806200032757607f821691505b6020821081036200034857634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200039c57600081815260208120601f850160051c81016020861015620003775750805b601f850160051c820191505b81811015620003985782815560010162000383565b5050505b505050565b81516001600160401b03811115620003bd57620003bd620001c8565b620003d581620003ce845462000312565b846200034e565b602080601f8311600181146200040d5760008415620003f45750858301515b600019600386901b1c1916600185901b17855562000398565b600085815260208120601f198616915b828110156200043e578886015182559484019460019091019084016200041d565b50858210156200045d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a05160c05160e0516116aa620004bc6000396000610237015260008181610307015281816105c001526106a70152600061053a015260008181610379015261050401526116aa6000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806370a08231116100d8578063a457c2d71161008c578063d505accf11610066578063d505accf1461039b578063dd62ed3e146103ae578063ffa1ad74146103f457600080fd5b8063a457c2d71461034e578063a9059cbb14610361578063cd0d00961461037457600080fd5b806395d89b41116100bd57806395d89b41146102e75780639dc29fac146102ef578063a3c573eb1461030257600080fd5b806370a08231146102915780637ecebe00146102c757600080fd5b806330adf81f1161012f5780633644e515116101145780633644e51514610261578063395093511461026957806340c10f191461027c57600080fd5b806330adf81f14610209578063313ce5671461023057600080fd5b806318160ddd1161016057806318160ddd146101bd57806320606b70146101cf57806323b872dd146101f657600080fd5b806306fdde031461017c578063095ea7b31461019a575b600080fd5b610184610430565b60405161019191906113e4565b60405180910390f35b6101ad6101a8366004611479565b6104c2565b6040519015158152602001610191565b6002545b604051908152602001610191565b6101c17f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81565b6101ad6102043660046114a3565b6104dc565b6101c17f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b60405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152602001610191565b6101c1610500565b6101ad610277366004611479565b61055c565b61028f61028a366004611479565b6105a8565b005b6101c161029f3660046114df565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b6101c16102d53660046114df565b60056020526000908152604090205481565b610184610680565b61028f6102fd366004611479565b61068f565b6103297f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610191565b6101ad61035c366004611479565b61075e565b6101ad61036f366004611479565b61082f565b6101c17f000000000000000000000000000000000000000000000000000000000000000081565b61028f6103a9366004611501565b61083d565b6101c16103bc366004611574565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b6101846040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525081565b60606003805461043f906115a7565b80601f016020809104026020016040519081016040528092919081815260200182805461046b906115a7565b80156104b85780601f1061048d576101008083540402835291602001916104b8565b820191906000526020600020905b81548152906001019060200180831161049b57829003601f168201915b5050505050905090565b6000336104d0818585610b73565b60019150505b92915050565b6000336104ea858285610d27565b6104f5858585610dfe565b506001949350505050565b60007f00000000000000000000000000000000000000000000000000000000000000004614610537576105324661106d565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906104d090829086906105a3908790611629565b610b73565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610672576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f546f6b656e577261707065643a3a6f6e6c794272696467653a204e6f7420506f60448201527f6c79676f6e5a6b45564d4272696467650000000000000000000000000000000060648201526084015b60405180910390fd5b61067c8282611135565b5050565b60606004805461043f906115a7565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610754576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f546f6b656e577261707065643a3a6f6e6c794272696467653a204e6f7420506f60448201527f6c79676f6e5a6b45564d427269646765000000000000000000000000000000006064820152608401610669565b61067c8282611228565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015610822576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401610669565b6104f58286868403610b73565b6000336104d0818585610dfe565b834211156108cc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f546f6b656e577261707065643a3a7065726d69743a204578706972656420706560448201527f726d6974000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260056020526040812080547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918a918a918a9190866109268361163c565b9091555060408051602081019690965273ffffffffffffffffffffffffffffffffffffffff94851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506000610991610500565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff89169284019290925260608301879052608083018690529092509060019060a0016020604051602081039080840390855afa158015610a55573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590610ad057508973ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b610b5c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f546f6b656e577261707065643a3a7065726d69743a20496e76616c696420736960448201527f676e6174757265000000000000000000000000000000000000000000000000006064820152608401610669565b610b678a8a8a610b73565b50505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8316610c15576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff8216610cb8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610df85781811015610deb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610669565b610df88484848403610b73565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8316610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff8216610f44576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015610ffa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610df8565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f611098610430565b8051602091820120604080518082018252600181527f310000000000000000000000000000000000000000000000000000000000000090840152805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc66060820152608081018390523060a082015260c001604051602081830303815290604052805190602001209050919050565b73ffffffffffffffffffffffffffffffffffffffff82166111b2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610669565b80600260008282546111c49190611629565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff82166112cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090205481811015611381576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff83166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101610d1a565b600060208083528351808285015260005b81811015611411578581018301518582016040015282016113f5565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461147457600080fd5b919050565b6000806040838503121561148c57600080fd5b61149583611450565b946020939093013593505050565b6000806000606084860312156114b857600080fd5b6114c184611450565b92506114cf60208501611450565b9150604084013590509250925092565b6000602082840312156114f157600080fd5b6114fa82611450565b9392505050565b600080600080600080600060e0888a03121561151c57600080fd5b61152588611450565b965061153360208901611450565b95506040880135945060608801359350608088013560ff8116811461155757600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561158757600080fd5b61159083611450565b915061159e60208401611450565b90509250929050565b600181811c908216806115bb57607f821691505b6020821081036115f4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156104d6576104d66115fa565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361166d5761166d6115fa565b506001019056fea26469706673582212208d88fee561cff7120d381c345cfc534cef8229a272dc5809d4bbb685ad67141164736f6c63430008110033a2646970667358221220914f18d5b241f0d10b2ebc814aadeee338ad60bad704683e414dad415cb2e14d64736f6c63430008140033" + }, + { + "contractName": "PolygonZkEVMBridge proxy", + "balance": "340282366920938463463374607431768211455", + "nonce": "1", + "address": "0xB7098a13a48EcE087d3DA15b2D28eCE0f89819B8", + "bytecode": "0x60806040526004361061005e5760003560e01c80635c60da1b116100435780635c60da1b146100a85780638f283970146100e6578063f851a440146101065761006d565b80633659cfe6146100755780634f1ef286146100955761006d565b3661006d5761006b61011b565b005b61006b61011b565b34801561008157600080fd5b5061006b61009036600461086f565b610135565b61006b6100a336600461088a565b61017f565b3480156100b457600080fd5b506100bd6101f3565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100f257600080fd5b5061006b61010136600461086f565b610231565b34801561011257600080fd5b506100bd61025e565b61012361028c565b61013361012e610363565b61036d565b565b61013d610391565b73ffffffffffffffffffffffffffffffffffffffff16330361017757610174816040518060200160405280600081525060006103d1565b50565b61017461011b565b610187610391565b73ffffffffffffffffffffffffffffffffffffffff1633036101eb576101e68383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103d1915050565b505050565b6101e661011b565b60006101fd610391565b73ffffffffffffffffffffffffffffffffffffffff16330361022657610221610363565b905090565b61022e61011b565b90565b610239610391565b73ffffffffffffffffffffffffffffffffffffffff16330361017757610174816103fc565b6000610268610391565b73ffffffffffffffffffffffffffffffffffffffff16330361022657610221610391565b610294610391565b73ffffffffffffffffffffffffffffffffffffffff163303610133576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f7879207461726760648201527f6574000000000000000000000000000000000000000000000000000000000000608482015260a4015b60405180910390fd5b600061022161045d565b3660008037600080366000845af43d6000803e80801561038c573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b5473ffffffffffffffffffffffffffffffffffffffff16919050565b6103da83610485565b6000825111806103e75750805b156101e6576103f683836104d2565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610425610391565b6040805173ffffffffffffffffffffffffffffffffffffffff928316815291841660208301520160405180910390a1610174816104fe565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6103b5565b61048e8161060a565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606104f7838360405180606001604052806027815260200161099f602791396106d5565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff81166105a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161035a565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9290921691909117905550565b73ffffffffffffffffffffffffffffffffffffffff81163b6106ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e747261637400000000000000000000000000000000000000606482015260840161035a565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6105c4565b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516106ff9190610931565b600060405180830381855af49150503d806000811461073a576040519150601f19603f3d011682016040523d82523d6000602084013e61073f565b606091505b50915091506107508683838761075a565b9695505050505050565b606083156107f05782516000036107e95773ffffffffffffffffffffffffffffffffffffffff85163b6107e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161035a565b50816107fa565b6107fa8383610802565b949350505050565b8151156108125781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161035a919061094d565b803573ffffffffffffffffffffffffffffffffffffffff8116811461086a57600080fd5b919050565b60006020828403121561088157600080fd5b6104f782610846565b60008060006040848603121561089f57600080fd5b6108a884610846565b9250602084013567ffffffffffffffff808211156108c557600080fd5b818601915086601f8301126108d957600080fd5b8135818111156108e857600080fd5b8760208285010111156108fa57600080fd5b6020830194508093505050509250925092565b60005b83811015610928578181015183820152602001610910565b50506000910152565b6000825161094381846020870161090d565b9190910192915050565b602081526000825180602084015261096c81604085016020870161090d565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220701a0c26bdd76686e63fc3c65e4f28a20ba3ecc8a60246733c0627e679c9804e64736f6c63430008140033", + "storage": { + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000e34fe58dda5b8c6d547e4857e987633aa86a5e90", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000493732fb136a380920c390a85fc27d79c7b70756" + } + }, + { + "contractName": "PolygonZkEVMGlobalExitRootL2 implementation", + "balance": "0", + "nonce": "1", + "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9", + "bytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c806301fd904414610051578063257b36321461006d57806333d6247d1461008d578063a3c573eb146100a2575b600080fd5b61005a60015481565b6040519081526020015b60405180910390f35b61005a61007b366004610162565b60006020819052908152604090205481565b6100a061009b366004610162565b6100ee565b005b6100c97f000000000000000000000000b7098a13a48ece087d3da15b2d28ece0f89819b881565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610064565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b7098a13a48ece087d3da15b2d28ece0f89819b8161461015d576040517fb49365dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600155565b60006020828403121561017457600080fd5b503591905056fea2646970667358221220ea2171e2c85c8bff947affc409ef6fc6a8fe82fb8c174ddeda988651e595d66564736f6c63430008140033" + }, + { + "contractName": "PolygonZkEVMGlobalExitRootL2 proxy", + "balance": "0", + "nonce": "1", + "address": "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa", + "bytecode": "0x60806040523661001357610011610017565b005b6100115b61001f6101b7565b6001600160a01b0316336001600160a01b0316141561016f5760606001600160e01b031960003516631b2ce7f360e11b8114156100655761005e6101ea565b9150610167565b6001600160e01b0319811663278f794360e11b14156100865761005e610241565b6001600160e01b031981166308f2839760e41b14156100a75761005e610287565b6001600160e01b031981166303e1469160e61b14156100c85761005e6102b8565b6001600160e01b03198116635c60da1b60e01b14156100e95761005e6102f8565b60405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b815160208301f35b61017761030c565b565b606061019e83836040518060600160405280602781526020016108576027913961031c565b9392505050565b90565b6001600160a01b03163b151590565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b60606101f4610394565b600061020336600481846106a2565b81019061021091906106e8565b905061022d8160405180602001604052806000815250600061039f565b505060408051602081019091526000815290565b606060008061025336600481846106a2565b8101906102609190610719565b915091506102708282600161039f565b604051806020016040528060008152509250505090565b6060610291610394565b60006102a036600481846106a2565b8101906102ad91906106e8565b905061022d816103cb565b60606102c2610394565b60006102cc6101b7565b604080516001600160a01b03831660208201529192500160405160208183030381529060405291505090565b6060610302610394565b60006102cc610422565b610177610317610422565b610431565b6060600080856001600160a01b0316856040516103399190610807565b600060405180830381855af49150503d8060008114610374576040519150601f19603f3d011682016040523d82523d6000602084013e610379565b606091505b509150915061038a86838387610455565b9695505050505050565b341561017757600080fd5b6103a8836104d3565b6000825111806103b55750805b156103c6576103c48383610179565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103f46101b7565b604080516001600160a01b03928316815291841660208301520160405180910390a161041f81610513565b50565b600061042c6105bc565b905090565b3660008037600080366000845af43d6000803e808015610450573d6000f35b3d6000fd5b606083156104c15782516104ba576001600160a01b0385163b6104ba5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161015e565b50816104cb565b6104cb83836105e4565b949350505050565b6104dc8161060e565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b0381166105785760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840161015e565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6101db565b8151156105f45781518083602001fd5b8060405162461bcd60e51b815260040161015e9190610823565b6001600160a01b0381163b61067b5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b606482015260840161015e565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61059b565b600080858511156106b257600080fd5b838611156106bf57600080fd5b5050820193919092039150565b80356001600160a01b03811681146106e357600080fd5b919050565b6000602082840312156106fa57600080fd5b61019e826106cc565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561072c57600080fd5b610735836106cc565b9150602083013567ffffffffffffffff8082111561075257600080fd5b818501915085601f83011261076657600080fd5b81358181111561077857610778610703565b604051601f8201601f19908116603f011681019083821181831017156107a0576107a0610703565b816040528281528860208487010111156107b957600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b838110156107f65781810151838201526020016107de565b838111156103c45750506000910152565b600082516108198184602087016107db565b9190910192915050565b60208152600082518060208401526108428160408501602087016107db565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122012bb4f564f73959a03513dc74fc3c6e40e8386e6f02c16b78d6db00ce0aa16af64736f6c63430008090033", + "storage": { + "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000e34fe58dda5b8c6d547e4857e987633aa86a5e90", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000dc64a140aa3e981100a9beca4e685f962f0cf6c9" + } + }, + { + "contractName": "PolygonZkEVMTimelock", + "balance": "0", + "nonce": "1", + "address": "0x0165878A594ca255338adfa4d48449f69242Eb8F", + "bytecode": "0x6080604052600436106101c65760003560e01c806364d62353116100f7578063b1c5f42711610095578063d547741f11610064578063d547741f14610661578063e38335e514610681578063f23a6e6114610694578063f27a0c92146106d957600080fd5b8063b1c5f427146105af578063bc197c81146105cf578063c4d252f514610614578063d45c44351461063457600080fd5b80638f61f4f5116100d15780638f61f4f5146104e157806391d1485414610515578063a217fddf14610566578063b08e51c01461057b57600080fd5b806364d62353146104815780638065657f146104a15780638f2a0bb0146104c157600080fd5b8063248a9ca31161016457806331d507501161013e57806331d50750146103c857806336568abe146103e85780633a6aae7214610408578063584b153e1461046157600080fd5b8063248a9ca3146103475780632ab0f529146103775780632f2ff15d146103a857600080fd5b80630d3cf6fc116101a05780630d3cf6fc1461026b578063134008d31461029f57806313bc9f20146102b2578063150b7a02146102d257600080fd5b806301d5062a146101d257806301ffc9a7146101f457806307bd02651461022957600080fd5b366101cd57005b600080fd5b3480156101de57600080fd5b506101f26101ed366004611c52565b6106ee565b005b34801561020057600080fd5b5061021461020f366004611cc7565b610783565b60405190151581526020015b60405180910390f35b34801561023557600080fd5b5061025d7fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e6381565b604051908152602001610220565b34801561027757600080fd5b5061025d7f5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca581565b6101f26102ad366004611d09565b6107df565b3480156102be57600080fd5b506102146102cd366004611d75565b6108d7565b3480156102de57600080fd5b506103166102ed366004611e9a565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610220565b34801561035357600080fd5b5061025d610362366004611d75565b60009081526020819052604090206001015490565b34801561038357600080fd5b50610214610392366004611d75565b6000908152600160208190526040909120541490565b3480156103b457600080fd5b506101f26103c3366004611f02565b6108fd565b3480156103d457600080fd5b506102146103e3366004611d75565b610927565b3480156103f457600080fd5b506101f2610403366004611f02565b610940565b34801561041457600080fd5b5061043c7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610220565b34801561046d57600080fd5b5061021461047c366004611d75565b6109f8565b34801561048d57600080fd5b506101f261049c366004611d75565b610a0e565b3480156104ad57600080fd5b5061025d6104bc366004611d09565b610ade565b3480156104cd57600080fd5b506101f26104dc366004611f73565b610b1d565b3480156104ed57600080fd5b5061025d7fb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc181565b34801561052157600080fd5b50610214610530366004611f02565b60009182526020828152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b34801561057257600080fd5b5061025d600081565b34801561058757600080fd5b5061025d7ffd643c72710c63c0180259aba6b2d05451e3591a24e58b62239378085726f78381565b3480156105bb57600080fd5b5061025d6105ca366004612025565b610d4f565b3480156105db57600080fd5b506103166105ea36600461214e565b7fbc197c810000000000000000000000000000000000000000000000000000000095945050505050565b34801561062057600080fd5b506101f261062f366004611d75565b610d94565b34801561064057600080fd5b5061025d61064f366004611d75565b60009081526001602052604090205490565b34801561066d57600080fd5b506101f261067c366004611f02565b610e8f565b6101f261068f366004612025565b610eb4565b3480156106a057600080fd5b506103166106af3660046121f8565b7ff23a6e610000000000000000000000000000000000000000000000000000000095945050505050565b3480156106e557600080fd5b5061025d611161565b7fb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc161071881611244565b6000610728898989898989610ade565b90506107348184611251565b6000817f4cf4410cc57040e44862ef0f45f3dd5a5e02db8eb8add648d4b0e236f1d07dca8b8b8b8b8b8a604051610770969594939291906122a6565b60405180910390a3505050505050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e00000000000000000000000000000000000000000000000000000000014806107d957506107d98261139e565b92915050565b600080527fdae2aa361dfd1ca020a396615627d436107c35eff9fe7738a3512819782d70696020527f5ba6852781629bcdcd4bdaa6de76d786f1c64b16acdac474e55bebc0ea157951547fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e639060ff1661085c5761085c8133611435565b600061086c888888888888610ade565b905061087881856114ed565b6108848888888861162a565b6000817fc2617efa69bab66782fa219543714338489c4e9e178271560a91b82c3f612b588a8a8a8a6040516108bc94939291906122f1565b60405180910390a36108cd8161172e565b5050505050505050565b6000818152600160205260408120546001811180156108f65750428111155b9392505050565b60008281526020819052604090206001015461091881611244565b61092283836117d7565b505050565b60008181526001602052604081205481905b1192915050565b73ffffffffffffffffffffffffffffffffffffffff811633146109ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b6109f482826118c7565b5050565b6000818152600160208190526040822054610939565b333014610a9d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f54696d656c6f636b436f6e74726f6c6c65723a2063616c6c6572206d7573742060448201527f62652074696d656c6f636b00000000000000000000000000000000000000000060648201526084016109e1565b60025460408051918252602082018390527f11c24f4ead16507c69ac467fbd5e4eed5fb5c699626d2cc6d66421df253886d5910160405180910390a1600255565b6000868686868686604051602001610afb969594939291906122a6565b6040516020818303038152906040528051906020012090509695505050505050565b7fb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1610b4781611244565b888714610bd6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b888514610c65576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b6000610c778b8b8b8b8b8b8b8b610d4f565b9050610c838184611251565b60005b8a811015610d415780827f4cf4410cc57040e44862ef0f45f3dd5a5e02db8eb8add648d4b0e236f1d07dca8e8e85818110610cc357610cc3612331565b9050602002016020810190610cd89190612360565b8d8d86818110610cea57610cea612331565b905060200201358c8c87818110610d0357610d03612331565b9050602002810190610d15919061237b565b8c8b604051610d29969594939291906122a6565b60405180910390a3610d3a8161240f565b9050610c86565b505050505050505050505050565b60008888888888888888604051602001610d709897969594939291906124f7565b60405160208183030381529060405280519060200120905098975050505050505050565b7ffd643c72710c63c0180259aba6b2d05451e3591a24e58b62239378085726f783610dbe81611244565b610dc7826109f8565b610e53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603160248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20636160448201527f6e6e6f742062652063616e63656c6c656400000000000000000000000000000060648201526084016109e1565b6000828152600160205260408082208290555183917fbaa1eb22f2a492ba1a5fea61b8df4d27c6c8b5f3971e63bb58fa14ff72eedb7091a25050565b600082815260208190526040902060010154610eaa81611244565b61092283836118c7565b600080527fdae2aa361dfd1ca020a396615627d436107c35eff9fe7738a3512819782d70696020527f5ba6852781629bcdcd4bdaa6de76d786f1c64b16acdac474e55bebc0ea157951547fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e639060ff16610f3157610f318133611435565b878614610fc0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b87841461104f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b60006110618a8a8a8a8a8a8a8a610d4f565b905061106d81856114ed565b60005b8981101561114b5760008b8b8381811061108c5761108c612331565b90506020020160208101906110a19190612360565b905060008a8a848181106110b7576110b7612331565b9050602002013590503660008a8a868181106110d5576110d5612331565b90506020028101906110e7919061237b565b915091506110f78484848461162a565b84867fc2617efa69bab66782fa219543714338489c4e9e178271560a91b82c3f612b588686868660405161112e94939291906122f1565b60405180910390a350505050806111449061240f565b9050611070565b506111558161172e565b50505050505050505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff161580159061123257507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166315064c966040518163ffffffff1660e01b8152600401602060405180830381865afa15801561120e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061123291906125be565b1561123d5750600090565b5060025490565b61124e8133611435565b50565b61125a82610927565b156112e7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20616c60448201527f7265616479207363686564756c6564000000000000000000000000000000000060648201526084016109e1565b6112ef611161565b81101561137e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f54696d656c6f636b436f6e74726f6c6c65723a20696e73756666696369656e7460448201527f2064656c6179000000000000000000000000000000000000000000000000000060648201526084016109e1565b61138881426125e0565b6000928352600160205260409092209190915550565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806107d957507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146107d9565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff166109f4576114738161197e565b61147e83602061199d565b60405160200161148f929190612617565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f08c379a00000000000000000000000000000000000000000000000000000000082526109e191600401612698565b6114f6826108d7565b611582576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20697360448201527f206e6f742072656164790000000000000000000000000000000000000000000060648201526084016109e1565b80158061159e5750600081815260016020819052604090912054145b6109f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f54696d656c6f636b436f6e74726f6c6c65723a206d697373696e67206465706560448201527f6e64656e6379000000000000000000000000000000000000000000000000000060648201526084016109e1565b60008473ffffffffffffffffffffffffffffffffffffffff168484846040516116549291906126e9565b60006040518083038185875af1925050503d8060008114611691576040519150601f19603f3d011682016040523d82523d6000602084013e611696565b606091505b5050905080611727576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603360248201527f54696d656c6f636b436f6e74726f6c6c65723a20756e6465726c79696e67207460448201527f72616e73616374696f6e2072657665727465640000000000000000000000000060648201526084016109e1565b5050505050565b611737816108d7565b6117c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20697360448201527f206e6f742072656164790000000000000000000000000000000000000000000060648201526084016109e1565b600090815260016020819052604090912055565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff166109f45760008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff85168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556118693390565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16156109f45760008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60606107d973ffffffffffffffffffffffffffffffffffffffff831660145b606060006119ac8360026126f9565b6119b79060026125e0565b67ffffffffffffffff8111156119cf576119cf611d8e565b6040519080825280601f01601f1916602001820160405280156119f9576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611a3057611a30612331565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611a9357611a93612331565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000611acf8460026126f9565b611ada9060016125e0565b90505b6001811115611b77577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110611b1b57611b1b612331565b1a60f81b828281518110611b3157611b31612331565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93611b7081612710565b9050611add565b5083156108f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016109e1565b803573ffffffffffffffffffffffffffffffffffffffff81168114611c0457600080fd5b919050565b60008083601f840112611c1b57600080fd5b50813567ffffffffffffffff811115611c3357600080fd5b602083019150836020828501011115611c4b57600080fd5b9250929050565b600080600080600080600060c0888a031215611c6d57600080fd5b611c7688611be0565b965060208801359550604088013567ffffffffffffffff811115611c9957600080fd5b611ca58a828b01611c09565b989b979a50986060810135976080820135975060a09091013595509350505050565b600060208284031215611cd957600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146108f657600080fd5b60008060008060008060a08789031215611d2257600080fd5b611d2b87611be0565b955060208701359450604087013567ffffffffffffffff811115611d4e57600080fd5b611d5a89828a01611c09565b979a9699509760608101359660809091013595509350505050565b600060208284031215611d8757600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611e0457611e04611d8e565b604052919050565b600082601f830112611e1d57600080fd5b813567ffffffffffffffff811115611e3757611e37611d8e565b611e6860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611dbd565b818152846020838601011115611e7d57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060808587031215611eb057600080fd5b611eb985611be0565b9350611ec760208601611be0565b925060408501359150606085013567ffffffffffffffff811115611eea57600080fd5b611ef687828801611e0c565b91505092959194509250565b60008060408385031215611f1557600080fd5b82359150611f2560208401611be0565b90509250929050565b60008083601f840112611f4057600080fd5b50813567ffffffffffffffff811115611f5857600080fd5b6020830191508360208260051b8501011115611c4b57600080fd5b600080600080600080600080600060c08a8c031215611f9157600080fd5b893567ffffffffffffffff80821115611fa957600080fd5b611fb58d838e01611f2e565b909b50995060208c0135915080821115611fce57600080fd5b611fda8d838e01611f2e565b909950975060408c0135915080821115611ff357600080fd5b506120008c828d01611f2e565b9a9d999c50979a969997986060880135976080810135975060a0013595509350505050565b60008060008060008060008060a0898b03121561204157600080fd5b883567ffffffffffffffff8082111561205957600080fd5b6120658c838d01611f2e565b909a50985060208b013591508082111561207e57600080fd5b61208a8c838d01611f2e565b909850965060408b01359150808211156120a357600080fd5b506120b08b828c01611f2e565b999c989b509699959896976060870135966080013595509350505050565b600082601f8301126120df57600080fd5b8135602067ffffffffffffffff8211156120fb576120fb611d8e565b8160051b61210a828201611dbd565b928352848101820192828101908785111561212457600080fd5b83870192505b848310156121435782358252918301919083019061212a565b979650505050505050565b600080600080600060a0868803121561216657600080fd5b61216f86611be0565b945061217d60208701611be0565b9350604086013567ffffffffffffffff8082111561219a57600080fd5b6121a689838a016120ce565b945060608801359150808211156121bc57600080fd5b6121c889838a016120ce565b935060808801359150808211156121de57600080fd5b506121eb88828901611e0c565b9150509295509295909350565b600080600080600060a0868803121561221057600080fd5b61221986611be0565b945061222760208701611be0565b93506040860135925060608601359150608086013567ffffffffffffffff81111561225157600080fd5b6121eb88828901611e0c565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015260a0604082015260006122dc60a08301868861225d565b60608301949094525060800152949350505050565b73ffffffffffffffffffffffffffffffffffffffff8516815283602082015260606040820152600061232760608301848661225d565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561237257600080fd5b6108f682611be0565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126123b057600080fd5b83018035915067ffffffffffffffff8211156123cb57600080fd5b602001915036819003821315611c4b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612440576124406123e0565b5060010190565b81835260006020808501808196508560051b810191508460005b878110156124ea57828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18836030181126124a057600080fd5b8701858101903567ffffffffffffffff8111156124bc57600080fd5b8036038213156124cb57600080fd5b6124d686828461225d565b9a87019a9550505090840190600101612461565b5091979650505050505050565b60a0808252810188905260008960c08301825b8b8110156125455773ffffffffffffffffffffffffffffffffffffffff61253084611be0565b1682526020928301929091019060010161250a565b5083810360208501528881527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff89111561257e57600080fd5b8860051b9150818a602083013701828103602090810160408501526125a69082018789612447565b60608401959095525050608001529695505050505050565b6000602082840312156125d057600080fd5b815180151581146108f657600080fd5b808201808211156107d9576107d96123e0565b60005b8381101561260e5781810151838201526020016125f6565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161264f8160178501602088016125f3565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000601791840191820152835161268c8160288401602088016125f3565b01602801949350505050565b60208152600082518060208401526126b78160408501602087016125f3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b8183823760009101908152919050565b80820281158282048414176107d9576107d96123e0565b60008161271f5761271f6123e0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019056fea2646970667358221220c474c39da3523b28ebfa5fd66c05b42d6ddcc4a57055483bdda32888b366016164736f6c63430008140033", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000e10", + "0xaedcc9e7897c0d335bdc5d92fe3a8b4f23727fe558cd1c19f332b28716a30559": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xf5e61edb9c9cc6bfbae4463e9a2b1dd6ac3b44ddef38f18016e56ba0363910d9": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x64494413541ff93b31aa309254e3fed72a7456e9845988b915b4c7a7ceba8814": "0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5", + "0x60b9d94c75b7b3f721925089391e4644cd890cb5e6466f9596dfbd2c54e0b280": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x3412d5605ac6cd444957cedb533e5dacad6378b4bc819ebe3652188a665066d6": "0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5", + "0x4b63b79f1e338a49559dcd3193ac9eecc50d0f275d24e97cc8c319e5a31a8bd0": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xdae2aa361dfd1ca020a396615627d436107c35eff9fe7738a3512819782d706a": "0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5", + "0x800d5dfe4bba53eedee06cd4546a27da8de00f12db83f56062976d4493fda899": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xc3ad33e20b0c56a223ad5104fff154aa010f8715b9c981fd38fdc60a4d1a52fc": "0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5" + } + }, + { + "accountName": "keyless Deployer", + "balance": "0", + "nonce": "1", + "address": "0x28BB4e66addE1f042B77E04cf7D3784C1dcDBbA3" + }, + { + "accountName": "deployer", + "balance": "100000000000000000000000", + "nonce": "8", + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + } + ] + } \ No newline at end of file diff --git a/test/config/test.prover.config.json b/test/config/test.prover.config.json new file mode 100644 index 00000000..810ddce6 --- /dev/null +++ b/test/config/test.prover.config.json @@ -0,0 +1,93 @@ +{ + "runExecutorServer": false, + "runExecutorClient": false, + "runExecutorClientMultithread": false, + + "runHashDBServer": false, + "runHashDBTest": false, + + "runAggregatorServer": false, + "runAggregatorClient": true, + "runAggregatorClientMock": false, + "aggregatorClientMockTimeout": 1, + "proverName": "stateless-prover-test", + + "runFileGenBatchProof": false, + "runFileGenAggregatedProof": false, + "runFileGenFinalProof": false, + "runFileProcessBatch": false, + "runFileProcessBatchMultithread": false, + + "runKeccakScriptGenerator": false, + "runKeccakTest": false, + "runStorageSMTest": false, + "runBinarySMTest": false, + "runMemAlignSMTest": false, + "runSHA256Test": false, + "runBlakeTest": false, + + "executeInParallel": true, + "useMainExecGenerated": true, + "saveRequestToFile": false, + "saveInputToFile": false, + "saveDbReadsToFile": false, + "saveDbReadsToFileOnChange": false, + "saveOutputToFile": true, + "saveProofToFile": true, + "saveResponseToFile": false, + "loadDBToMemCache": true, + "opcodeTracer": false, + "logRemoteDbReads": false, + "logExecutorServerResponses": false, + + "proverServerPort": 50051, + "proverServerMockPort": 50052, + "proverServerMockTimeout": 10000000, + "proverClientPort": 50051, + "proverClientHost": "127.0.0.1", + + "executorServerPort": 50071, + "executorROMLineTraces": false, + "executorClientPort": 50071, + "executorClientHost": "127.0.0.1", + + "hashDBServerPort": 50061, + "hashDBURL": "local", + + "aggregatorServerPort": 50081, + "aggregatorClientPort": 50081, + "aggregatorClientHost": "zkevm-aggregator", + "aggregatorClientWatchdogTimeout": 120000000, + + "mapConstPolsFile": false, + "mapConstantsTreeFile": false, + + "inputFile": "input_executor_0.json", + "inputFile2": "input_executor_1.json", + + "keccakScriptFile": "config/scripts/keccak_script.json", + "storageRomFile": "config/scripts/storage_sm_rom.json", + + "outputPath": "output", + "configPath": "config", + + "databaseURL": "local", + "dbNodesTableName": "state.nodes", + "dbProgramTableName": "state.program", + "dbMultiWrite": true, + "dbFlushInParallel": false, + "dbMTCacheSize": 1024, + "dbProgramCacheSize": 512, + "dbNumberOfPoolConnections": 30, + "dbGetTree": true, + "cleanerPollingPeriod": 600, + "requestsPersistence": 3600, + "maxExecutorThreads": 256, + "maxProverThreads": 8, + "maxHashDBThreads": 256, + "ECRecoverPrecalc": false, + "ECRecoverPrecalcNThreads": 32, + "stateManager": true, + "useAssociativeCache" : false, + "jsonLogs": false +} \ No newline at end of file diff --git a/test/docker-compose.yml b/test/docker-compose.yml new file mode 100644 index 00000000..a280d675 --- /dev/null +++ b/test/docker-compose.yml @@ -0,0 +1,91 @@ +networks: + default: + name: cdk + +services: + cdk-sequence-sender: + container_name: cdk-sequence-sender + restart: no + image: cdk + build: . + volumes: + - ./config/test.config.toml:/app/config.toml + - ./config/test.genesis.json:/app/genesis.json + - ./sequencer.keystore:/app/keystore/sequencer.keystore + command: + - "/bin/sh" + - "-c" + - "/app/cdk run --cfg /app/config.toml --network custom --custom-network-file /app/genesis.json --components sequence-sender" + + zkevm-prover: + container_name: zkevm-prover + restart: unless-stopped + image: hermeznetwork/zkevm-prover:v6.0.3-RC16 + volumes: + - ./config/test.prover.config.json:/usr/src/app/config.json + - ~/stateless-aggregator/prover/config:/app/config + command: > + zkProver -c /usr/src/app/config.json + + cdk-aggregator: + container_name: cdk-aggregator + image: cdk + ports: + - 50081:50081 + - 9093:9091 # needed if metrics enabled + environment: + - CDK_AGGREGATOR_DB_HOST=cdk-aggregator-db + - CDK_AGGREGATOR_SENDER_ADDRESS=0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 + volumes: + - ./config/test.config.toml:/app/config.toml + - ./config/test.genesis.json:/app/genesis.json + - ./aggregator.keystore:/pk/aggregator.keystore + command: + - "/bin/sh" + - "-c" + - "/app/cdk run --cfg /app/config.toml --network custom --custom-network-file /app/genesis.json --components aggregator" + depends_on: + cdk-aggregator-db: + condition: service_started + cdk-l1-sync-db: + condition: service_started + + cdk-aggregator-db: + container_name: cdk-aggregator-db + image: postgres:15 + deploy: + resources: + limits: + memory: 2G + reservations: + memory: 1G + ports: + - 5434:5432 + environment: + - POSTGRES_USER=aggregator_user + - POSTGRES_PASSWORD=aggregator_password + - POSTGRES_DB=aggregator_db + command: + - "postgres" + - "-N" + - "500" + + cdk-l1-sync-db: + container_name: cdk-l1-sync-db + image: postgres:15 + deploy: + resources: + limits: + memory: 2G + reservations: + memory: 1G + ports: + - 5436:5432 + environment: + - POSTGRES_USER=test_user + - POSTGRES_PASSWORD=test_password + - POSTGRES_DB=sync + command: + - "postgres" + - "-N" + - "500" diff --git a/test/sequencer.keystore b/test/sequencer.keystore new file mode 100644 index 00000000..96b662b7 --- /dev/null +++ b/test/sequencer.keystore @@ -0,0 +1 @@ +{"address":"f39fd6e51aad88f6f4ce6ab8827279cfffb92266","crypto":{"cipher":"aes-128-ctr","ciphertext":"d005030a7684f3adad2447cbb27f63039eec2224c451eaa445de0d90502b9f3d","cipherparams":{"iv":"dc07a54bc7e388efa89c34d42f2ebdb4"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"cf2ec55ecae11171de575112cfb16963570533a9c46fb774473ceb11519eb24a"},"mac":"3eb180d405a5da6e462b2adc00091c14856c91d574bf27348714506357d6e177"},"id":"035454db-6b6d-477f-8a79-ce24c10b185f","version":3} \ No newline at end of file diff --git a/version.go b/version.go new file mode 100644 index 00000000..2dbd8a0c --- /dev/null +++ b/version.go @@ -0,0 +1,25 @@ +package zkevm + +import ( + "fmt" + "io" + "runtime" +) + +// Populated during build, don't touch! +var ( + Version = "v0.1.0" + GitRev = "undefined" + GitBranch = "undefined" + BuildDate = "Fri, 17 Jun 1988 01:58:00 +0200" +) + +// PrintVersion prints version info into the provided io.Writer. +func PrintVersion(w io.Writer) { + fmt.Fprintf(w, "Version: %s\n", Version) + fmt.Fprintf(w, "Git revision: %s\n", GitRev) + fmt.Fprintf(w, "Git branch: %s\n", GitBranch) + fmt.Fprintf(w, "Go version: %s\n", runtime.Version()) + fmt.Fprintf(w, "Built: %s\n", BuildDate) + fmt.Fprintf(w, "OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH) +} diff --git a/version.mk b/version.mk new file mode 100644 index 00000000..73db3b01 --- /dev/null +++ b/version.mk @@ -0,0 +1,4 @@ +VERSION := $(shell git describe --tags --always) +GITREV := $(shell git rev-parse --short HEAD) +GITBRANCH := $(shell git rev-parse --abbrev-ref HEAD) +DATE := $(shell LANG=US date +"%a, %d %b %Y %X %z")